Files in this shell archive add JPEG 2000 support ("*.jp[2c]" files) to XV 3.10a using a CODEC in the open-source JasPer Library Version 1.701, available from: http://www.ece.uvic.ca/~mdadams/jasper This library is also distributed in Ghostscript Version 8.50. The IJG JPEG Library continues to be used by XV file "xvjpeg.c" for classical JPEG images, but this patch does not further require it for JPEG 2000 images. Beside installing 3 new files and applying a patch, update your "make(1)" file: 1. Add dependencies for new files, "xvjp2k.[co]" and "bits/br_jp[2c]". 2. Add compiler option "-DDOJP2K". 3. Add linker option "-ljasper". File "jp2k.patch" amends the existing files, "xv.[ch]", "xvdir.c", "xvevent.c", and "xvmisc.c" It was generated against the original XV 3.10a versions of "xv.[ch]" and "xvdir.c", so it appears to add several later published patches that I applied for PNG and G3 FAX support, plus bug fixes. If you, too, have already applied those published patches, this one might trigger some complaints about lines changed identically by different patches, so manually edit this one as necessary. These changes include a minor alteration of subroutine "ReadFileType()" in file "xv.c": XV previously rejected image files smaller than 30 bytes, but it *is* possible to generate legal P[BGN]M files smaller than that, and the Jasper Library includes some in its test suite, so I reduced XV's minimum file size to 1 byte(!). -- Scott Marovich Hewlett-Packard Laboratories M/S 1198 E-mail: marovich@hpl.hp.com P.O. Box 10490 (1501 Page Mill Road) Tel. (650)857-2998 / FAX (650)857-7724 Palo Alto, California 94303-0971 U.S.A. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # Wrapped by Scott Marovich on Fri Jan 21 11:03:35 2005 # # This archive contains: # jp2k.patch xvjp2k.c bits/br_jp2 bits/br_jpc echo x - jp2k.patch cat >jp2k.patch <<'@EOF' *** xv.h.orig Mon Jan 23 12:22:23 1995 --- xv.h Mon Jan 23 12:22:23 1995 *************** *** 311,328 **** #ifdef DOJPEG #define HAVE_JPEG #endif #ifdef DOTIFF #define HAVE_TIFF #endif #ifdef DOPDS #define HAVE_PDS #endif - #define PROGNAME "Xv" /* used in resource database */ #define MAXNAMES 4096 /* max # of files in ctrlW list */ --- 311,338 ---- #ifdef DOJPEG #define HAVE_JPEG #endif + #ifdef DOJP2K + #define HAVE_JP2K + #endif + #ifdef DOTIFF #define HAVE_TIFF #endif + #ifdef DOPNG + #define HAVE_PNG + #endif + #ifdef DOPDS #define HAVE_PDS #endif + #ifdef DOG3 + #define HAVE_G3 + #endif #define PROGNAME "Xv" /* used in resource database */ #define MAXNAMES 4096 /* max # of files in ctrlW list */ *************** *** 440,471 **** #else #define F_JPGINC 0 #endif #ifdef HAVE_TIFF #define F_TIFINC 1 #else #define F_TIFINC 0 #endif #define F_GIF 0 ! #define F_JPEG ( 0 + F_JPGINC) ! #define F_TIFF ( 0 + F_JPGINC + F_TIFINC) ! #define F_PS ( 1 + F_JPGINC + F_TIFINC) ! #define F_PBMRAW ( 2 + F_JPGINC + F_TIFINC) ! #define F_PBMASCII ( 3 + F_JPGINC + F_TIFINC) ! #define F_XBM ( 4 + F_JPGINC + F_TIFINC) ! #define F_XPM ( 5 + F_JPGINC + F_TIFINC) ! #define F_BMP ( 6 + F_JPGINC + F_TIFINC) ! #define F_SUNRAS ( 7 + F_JPGINC + F_TIFINC) ! #define F_IRIS ( 8 + F_JPGINC + F_TIFINC) ! #define F_TARGA ( 9 + F_JPGINC + F_TIFINC) ! #define F_FITS (10 + F_JPGINC + F_TIFINC) ! #define F_PM (11 + F_JPGINC + F_TIFINC) ! #define F_DELIM1 (12 + F_JPGINC + F_TIFINC) /* ----- */ ! #define F_FILELIST (13 + F_JPGINC + F_TIFINC) ! #define F_MAXFMTS (14 + F_JPGINC + F_TIFINC) /* 15, normally */ /* return values from ReadFileType() --- 450,496 ---- #else #define F_JPGINC 0 #endif + #ifdef HAVE_JP2K + #define F_JP2INC 2 + #else + #define F_JP2INC 0 + #endif + #ifdef HAVE_TIFF #define F_TIFINC 1 #else #define F_TIFINC 0 #endif + #ifdef HAVE_PNG + #define F_PNGINC 1 + #else + #define F_PNGINC 0 + #endif + #define F_GIF 0 ! #define F_JPEG ( 0+F_JPGINC) ! #define F_JPC ( 1+F_JPGINC) ! #define F_JP2 ( 0+F_JPGINC+F_JP2INC) ! #define F_TIFF ( 0+F_JPGINC+F_JP2INC+F_TIFINC) ! #define F_PNG ( 0+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_PS ( 1+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_PBMRAW ( 2+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_PBMASCII ( 3+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_XBM ( 4+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_XPM ( 5+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_BMP ( 6+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_SUNRAS ( 7+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_IRIS ( 8+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_TARGA ( 9+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_FITS (10+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_PM (11+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_DELIM1 (12+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) /* ----- */ ! #define F_FILELIST (13+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) ! #define F_MAXFMTS (14+F_JPGINC+F_JP2INC+F_TIFINC+F_PNGINC) /* 19, normally */ /* return values from ReadFileType() *************** *** 484,500 **** #define RFT_UTAHRLE 7 #define RFT_IRIS 8 #define RFT_PCX 9 #define RFT_JFIF 10 ! #define RFT_TIFF 11 ! #define RFT_PDSVICAR 12 ! #define RFT_COMPRESS 13 ! #define RFT_PS 14 ! #define RFT_IFF 15 ! #define RFT_TARGA 16 ! #define RFT_XPM 17 ! #define RFT_XWD 18 ! #define RFT_FITS 19 /* definitions for page up/down, arrow up/down list control */ #define LS_PAGEUP 0 #define LS_PAGEDOWN 1 --- 509,529 ---- #define RFT_UTAHRLE 7 #define RFT_IRIS 8 #define RFT_PCX 9 #define RFT_JFIF 10 ! #define RFT_JPC 11 ! #define RFT_JP2 12 ! #define RFT_TIFF 13 ! #define RFT_PDSVICAR 14 ! #define RFT_COMPRESS 15 ! #define RFT_PS 16 ! #define RFT_IFF 17 ! #define RFT_TARGA 18 ! #define RFT_XPM 19 ! #define RFT_XWD 20 ! #define RFT_FITS 21 ! #define RFT_PNG 22 ! #define RFT_G3 23 /* definitions for page up/down, arrow up/down list control */ #define LS_PAGEUP 0 #define LS_PAGEDOWN 1 *************** *** 755,765 **** typedef struct { Window win; /* window ID */ int x,y,w,h; /* window coords in parent */ int active; /* true if can do anything*/ ! int min,max; /* min/max values 'pos' can take */ ! int val; /* 'value' of dial */ ! int page; /* amt val change on pageup/pagedown */ char *title; /* title for this guage */ char *units; /* string appended to value */ u_long fg,bg,hi,lo; /* colors */ int rad, cx, cy; /* internals */ --- 784,795 ---- typedef struct { Window win; /* window ID */ int x,y,w,h; /* window coords in parent */ int active; /* true if can do anything*/ ! double min,max; /* min/max values 'pos' can take */ ! double val; /* 'value' of dial */ ! double inc; /* amt val change on up/down */ ! double page; /* amt val change on pageup/pagedown */ char *title; /* title for this guage */ char *units; /* string appended to value */ u_long fg,bg,hi,lo; /* colors */ int rad, cx, cy; /* internals */ *************** *** 1137,1151 **** --- 1167,1195 ---- WHERE int jpegUp; /* is jpegW mapped, or what? */ #endif + #ifdef HAVE_JP2K + /* stuff used for 'jp2k' box */ + WHERE Window jp2kW; + WHERE int jp2kUp; /* is jp2kW mapped, or what? */ + #endif + + #ifdef HAVE_TIFF /* stuff used for 'tiff' box */ WHERE Window tiffW; WHERE int tiffUp; /* is tiffW mapped, or what? */ #endif + #ifdef HAVE_PNG + /* stuff used for 'png' box */ + WHERE Window pngW; + WHERE int pngUp; /* is pngW mapped, or what? */ + #endif + + #undef WHERE *************** *** 1456,1469 **** void SCTrack PARM((SCRL *, int, int)); /*************************** XVDIAL.C ***************************/ ! void DCreate PARM((DIAL *, Window, int, int, int, int, int, ! int, int, int, u_long, u_long, u_long, ! u_long, char *, char *)); ! void DSetRange PARM((DIAL *, int, int, int, int)); ! void DSetVal PARM((DIAL *, int)); void DSetActive PARM((DIAL *, int)); void DRedraw PARM((DIAL *)); int DTrack PARM((DIAL *, int, int)); --- 1500,1513 ---- void SCTrack PARM((SCRL *, int, int)); /*************************** XVDIAL.C ***************************/ ! void DCreate PARM((DIAL *, Window, int, int, int, int, double, ! double, double, double, double, u_long, ! u_long, u_long, u_long, char *, char *)); ! void DSetRange PARM((DIAL *, double,double,double,double,double)); ! void DSetVal PARM((DIAL *, double)); void DSetActive PARM((DIAL *, int)); void DRedraw PARM((DIAL *)); int DTrack PARM((DIAL *, int, int)); *************** *** 1603,1610 **** --- 1647,1661 ---- void CreateTIFFW PARM((void)); void TIFFDialog PARM((int)); int TIFFCheckEvent PARM((XEvent *)); void TIFFSaveParams PARM((char *, int)); + + /**************************** XVPNG.C ***************************/ + int LoadPNG PARM((char *, PICINFO *)); + void CreatePNGW PARM((void)); + void PNGDialog PARM((int)); + int PNGCheckEvent PARM((XEvent *)); + void PNGSaveParams PARM((char *, int)); /**************************** XVPDS.C ***************************/ int LoadPDS PARM((char *, PICINFO *)); *** xv.c.orig Thu Jan 19 10:08:43 1995 --- xv.c Thu Jan 19 10:08:43 1995 *************** *** 53,60 **** --- 53,64 ---- static int clearonload; /* clear window/root (on colormap visuals) */ static int randomShow = 0; /* do a 'random' slideshow */ static int startIconic = 0; /* '-iconic' option */ static int defaultVis = 0; /* true if using DefaultVisual */ + #ifdef HAVE_G3 + static int fax = 0; /* temporary(?) kludge */ + int highresfax = 0; + #endif /* HAVE_G3 */ static double hexpand = 1.0; /* '-expand' argument */ static double vexpand = 1.0; /* '-expand' argument */ static char *maingeom = NULL; static char *icongeom = NULL; *************** *** 280,287 **** --- 284,295 ---- #ifdef HAVE_JPEG jpegW = (Window) NULL; jpegUp = 0; #endif + #ifdef HAVE_JP2K + jp2kW = (Window) NULL; jp2kUp = 0; + #endif + #ifdef HAVE_TIFF tiffW = (Window) NULL; tiffUp = 0; #endif *************** *** 806,813 **** --- 814,826 ---- CreateJPEGW(); XSetTransientForHint(theDisp, jpegW, dirW); #endif + #ifdef HAVE_JP2K + CreateJP2KW(); + XSetTransientForHint(theDisp, jp2kW, dirW); + #endif + #ifdef HAVE_TIFF CreateTIFFW(); XSetTransientForHint(theDisp, tiffW, dirW); #endif *************** *** 1480,1488 **** else if (!argcmp(argv[i],"-white",3,0,&pm)) /* white color */ { if (++iwindow == jpegW) JPEGDialog(0); #endif + #ifdef HAVE_JP2K + else if (client_event->window == jp2kW) JP2KDialog(0); + #endif + #ifdef HAVE_TIFF else if (client_event->window == tiffW) TIFFDialog(0); #endif + #ifdef HAVE_PNG + else if (client_event->window == pngW) PNGDialog(0); + #endif + else if (client_event->window == mainW) Quit(0); } } break; *************** *** 534,544 **** --- 550,568 ---- #ifdef HAVE_JPEG if (wasJpegUp) { JPEGDialog(wasJpegUp); wasJpegUp=0; } #endif + #ifdef HAVE_JP2K + if (wasJp2kUp) { JP2KDialog(wasJpegUp); wasJp2kUp=0; } + #endif + #ifdef HAVE_TIFF if (wasTiffUp) { TIFFDialog(wasTiffUp); wasTiffUp=0; } #endif + + #ifdef HAVE_PNG + if (wasPngUp) { PNGDialog(wasJpegUp); wasPngUp=0; } + #endif } } } break; *************** *** 572,582 **** --- 596,614 ---- #ifdef HAVE_JPEG if (jpegUp) { wasJpegUp = jpegUp; JPEGDialog(0); } #endif + #ifdef HAVE_JP2K + if (jp2kUp) { wasJp2kUp = jp2kUp; JP2KDialog(0); } + #endif + #ifdef HAVE_TIFF if (tiffUp) { wasTiffUp = tiffUp; TIFFDialog(0); } #endif + + #ifdef HAVE_PNG + if (pngUp) { wasPngUp = pngUp; PNGDialog(0); } + #endif } } } } *************** *** 1142,1153 **** --- 1174,1193 ---- #ifdef HAVE_JPEG if (JPEGCheckEvent(event)) break; #endif + #ifdef HAVE_JP2K + if (JP2KCheckEvent(event)) break; + #endif + #ifdef HAVE_TIFF if (TIFFCheckEvent(event)) break; #endif + #ifdef HAVE_PNG + if (PNGCheckEvent (event)) break; + #endif + if (GamCheckEvent (event)) break; if (BrowseCheckEvent (event, &retval, &done)) break; if (TextCheckEvent (event, &retval, &done)) break; *************** *** 1361,1372 **** --- 1401,1420 ---- #ifdef HAVE_JPEG if (JPEGCheckEvent(event)) break; #endif + #ifdef HAVE_JP2K + if (JP2KCheckEvent(event)) break; + #endif + #ifdef HAVE_TIFF if (TIFFCheckEvent(event)) break; #endif + #ifdef HAVE_PNG + if (PNGCheckEvent (event)) break; + #endif + if (GamCheckEvent (event)) break; if (BrowseCheckEvent (event, &retval, &done)) break; if (TextCheckEvent (event, &retval, &done)) break; *************** *** 2367,2376 **** --- 2415,2432 ---- #ifdef HAVE_JPEG if (jpegUp) JPEGDialog(0); /* close jpeg window */ #endif + #ifdef HAVE_JP2K + if (jp2kUp) JP2KDialog(0); /* close jpeg 2000 window */ + #endif + #ifdef HAVE_TIFF if (tiffUp) TIFFDialog(0); /* close tiff window */ + #endif + + #ifdef HAVE_PNG + if (pngUp) PNGDialog(0); /* close png window */ #endif ClosePopUp(); *** xvmisc.c.orig Thu Dec 22 14:34:47 1994 --- xvmisc.c Thu Dec 22 14:34:47 1994 *************** *** 517,524 **** --- 517,528 ---- #ifdef HAVE_JPEG if (jpegW) XDestroyWindow(theDisp, jpegW); #endif + #ifdef HAVE_JP2K + if (jp2kW) XDestroyWindow(theDisp, jp2kW); + #endif + #ifdef HAVE_TIFF if (tiffW) XDestroyWindow(theDisp, tiffW); #endif *************** *** 716,723 **** --- 720,731 ---- SetTextCursor(otherc); #ifdef HAVE_JPEG if (jpegW) XDefineCursor(theDisp, jpegW, otherc); + #endif + + #ifdef HAVE_JP2K + if (jp2kW) XDefineCursor(theDisp, jp2kW, otherc); #endif #ifdef HAVE_TIFF if (tiffW) XDefineCursor(theDisp, tiffW, otherc); @EOF echo x - xvjp2k.c cat >xvjp2k.c <<'@EOF' /* * xvjp2k.c - I/O subroutines for JPEG 2000 format pictures * * This module is a "shim" between XV and a JPEG 2000 CODEC in the open-source * JasPer Library created by Michael D. Adams; for more information, see the URL * "http://www.ece.uvic.ca/~mdadams/jasper". We don't use most of the other * facilities in this library, so it's better to link XV with a UNIX "archive" * representation of it, not a DLL. * * JPEG 2000 files can be represented in either of two general ways: The * simplest representation is a "code stream", which often has a ".jpc" file * name suffix and is organized much like a classical JPEG file, except that * unfortunately, JPEG 2000 code streams indicate the no. of colors in an image * but no longer give any clues about its color space (e.g., RGB or YCbCr). * Instead, there is now a semantically higher-level representation, which often * has a ".jp2" file name suffix and encapsulates a "code stream" with (possibly * a lot of) color-space information, optionally including things like ICC * correction tables. * * Compared to the IJG JPEG Library used in file "xvjpeg.c", one must solve * several problems for color images when marrying JasPer to XV. * * 1. JPEG 2000 files can represent a wide range of image sizes, resolutions, * and color spaces, not all of which can be easily rendered "as is" on a * normal "X Windows" display, so we must carefully check a decoded image's * parameters in order to reject anything that we can't handle gracefully. * * 2. JasPer prefers to decode/encode images using color-plane "slices", instead * of interleaved pixels needed by "X Windows", so we must (de)interleave * copies of the image buffer here. * * XXX Things to do: * * 1. In "LoadJP{2,C}()" try to handle the "quick" option, which requests * faster loading of a reduced-size image for the visual schnauzer. The * schnauzer's icon size is currently 80x60, so the old "xvjpeg.c" module tries * to produce a 2X (160x120) image. Can we do the same? * * 2. In "StoreJP2K()", JasPer Library Version 1.701 apparently has no API to * let the XV global "picComments" string be inserted in a JPEG 2000 comment * record. If the library ever gets fixed, enhance our code accordingly. * * --Scott Marovich , Hewlett-Packard Laboratories, * January 2005. */ #include #include #include "copyright.h" #include "xv.h" #ifdef HAVE_JP2K static char *fbasename, /* File's base name, for error/warning message(s) */ bad_samp[] ={"%s: can't read %d-plane %s file!"}, fmode[] ={"rb"}, full_msg[] ={"%s %s. (%ld bytes)"}, jp2_kind[] ={"JP2"}, jpc_kind[] ={"JPEG 2000"}, load_msg[] ={"Loading %dx%d %s %s (%ld bytes)..."}, no_mem[] ={"%s: can't read %s file - out of memory"}, pixel_size[]={"%s: can't display %d-bit pixels!"}, shrt_msg[] ={"%dx%d %s %s. "}, truncated[] ={"%s: Unexpected end of %s file"}, read_err[] ={"%s: I/O error reading %s file"}; /* We only want to override the JasPer Library's "jas_eprintf()" subroutine in order to make it a "wrapper" around XV's own error-reporting subroutine, but because of the way the former is currently packaged in JasPer Library Version 1.701, we must override everything else packaged in the "jas_debug.o" module with it, otherwise we get "duplicate definition" messages from the linker. */ int jas_getdbglevel(void){return 0;} int jas_setdbglevel(int n){} int jas_memdump(FILE *fp,void *data,size_t len){} int jas_eprintf(const char *fmt,...) /* Handle JasPer Library message */ { static char error[] ={'e','r','r','o','r',':',' '}, warning[]={'w','a','r','n','i','n','g',':',' '}; int kind = ISTR_WARNING; char buffer[512]; register char *p = buffer; /* Unlike the IJG JPEG Library, the JasPer Library current has no graceful way for an application (= us!) to intercept its diagnostic messages and output them using our own subroutines, so this ugly replacement for its output subroutine will have to suffice. At Version 1.701, lthough the library's own "jas_eprintf()" is a varargs subroutine, all calls currently pass just 1 string with a Line Feed at the end and no "printf(3C)" arguments. Most strings begin with "error: " or "warning: ", although a few have neither. We try to translate these into the format preferred by XV. Start by copying the message into a temporary buffer in order to trim its trailing Line Feed character (ugh!). */ while (*p++ = *fmt++); if (*(p -= 2) == '\n') *p = '\000'; p = buffer; if (strncmp(p,error,sizeof error) == 0) /* "error: ... " */ { kind = ISTR_WARNING; p += sizeof error; } else /* "warning: ... " */ if (strncmp(p,warning,sizeof warning) == 0) { kind = ISTR_INFO; p += sizeof warning; }; SetISTR(kind,"s: %s",fbasename,p); } static char *SetBuf(FILE *f) { char *buf; register char *p; /* JPEG 2000 image files are apt to be large, but the buffer size allocated by most implementations of the "C" Standard I/O Library is still ridiculously small, typically 1 KB. We want to allocate a much larger buffer for higher I/O efficiency, but the details are unfortunately a bit platform-specific. Under UNIX systems with virtual memory, we want to encourage its internal "physio()" subroutine by making the buffer an integral number of pages, aligned on a page-multiple memory address boundary. Under HP-UX 11.1+ and perhaps other operating-systems, a Standard I/O buffer is preceded by a header whose size must also be taken into account. */ # ifndef IOBUFSIZ # define IOBUFSIZ 65536 # endif /* IOBUFSIZ */ # ifdef __hpux # define OVERHEAD sizeof(mbstate_t) # endif /* __hpux */ # ifndef OVERHEAD # define OVERHEAD 0 # endif /* OVERHEAD */ # ifdef NBPG if (!(buf = p = malloc(NBPG+OVERHEAD+IOBUFSIZ))) return 0; p = (char *)((unsigned long)p+NBPG-1 & ~(NBPG-1)); p -= OVERHEAD; # else /* not NBPG */ if (!(buf = p = malloc(OVERHEAD+IOBUFSIZ))) return 0; p += OVERHEAD; # endif /* NBPG */ setvbuf(f,p,_IOFBF,OVERHEAD+IOBUFSIZ); return buf; # undef OVERHEAD # undef IOBUFSIZ } int LoadJPC(char *fname,register PICINFO *pinfo,int quick) { jas_image_t *img; jas_stream_t *str; FILE *fp; byte *pic; char *fbasename, *iobuf, *s; unsigned long filesize, imagesize; int ok = 0, vstride; register int i; /* Load a JPEG 2000 "code stream" image file into a pixel buffer for XV. Unlike classical JPEG files, they have no clue about the image's color space, so we check for 8-bit data samples but make no effort to check or convert color spaces, and "what you see is what you get". For now, ignore the "quick" option to return a reduced-resolution or -size image. Return 1 on success, or 0 on failure. */ if (!(fp = xv_fopen(fname,fmode))) return 0; fbasename = BaseName(fname); /* Input file's base name, for message(s) */ if (!(iobuf = SetBuf(fp))) { (void)fclose(fp); SetISTR(ISTR_WARNING,no_mem,fbasename,jpc_kind); goto L3; }; /* Before telling the JasPer Library about this file, get its size for display purposes. Non-UNIX systems don't necessarily simulate "stat(2)", so do it crudely but portably by seeking to the end, then back to the beginning. */ fseek(fp,0L,2); filesize = ftell(fp); fseek(fp,0L,0); /* "jas_stream_close()" will eventually close the input file, so only do it explicitly if no stream can be created: */ if (!(str = jas_stream_freopen(fname,fmode,fp))) { (void)fclose(fp); goto L3; }; /* It's not clear to me whether the following represents a JasPer Library "bug" but it sure looks goofy: Unless a stream buffer is marked "read only", which only happens when the stream's "fillbuf" method is called, even though our buffers are always "read only", the library will try to flush out buffer contents when the stream is destroyed, which makes it die a horrible death. So, mark the stream buffer proactively: */ str->bufmode_ |= JAS_STREAM_RDBUF; /* We will only read the stream buffer */ if (!(img = jpc_decode(str,0))) goto L2; if (imagesize = vstride = jas_image_numcmpts(img)) /* no. of color planes */ { /* After the image-component streams created, they are left in a "write" state with the streams' cursors positioned at their ends, so "seek" in order to "read" each stream from its beginning. */ i = vstride; while (--i >= 0) if (jas_stream_seek(img->cmpts_[i]->stream_,0L,0)) { SetISTR(ISTR_WARNING,read_err,fbasename,jpc_kind); goto L1; } }; imagesize *= pinfo->normw = pinfo->w = jas_image_width(img); imagesize *= pinfo->normh = pinfo->h = jas_image_height(img); /* Sanity-check the image's color space and no. of colors. For now, accept only "generic" color spaces, not files needing image-specific color correction, but fix that someday... */ switch (vstride) { default: SetISTR(ISTR_WARNING,bad_samp,fbasename,vstride,jpc_kind); goto L1; case 1: if ((i = jas_image_cmptprec(img,0)) != 8) /* not 8-bit pixels */ { SetISTR(ISTR_WARNING,pixel_size,fbasename,i); goto L1; }; s = "Greyscale"; pinfo->type = PIC8; pinfo->colType = F_GREYSCALE; i = 256; /* Return fake indexed-color "map" */ while (--i >= 0) pinfo->r[i] = pinfo->g[i] = pinfo->b[i] = i; break; case 3: /* BEWARE OF KLUDGE: If the image's color space is RGB, assume that the data-sample precision for all color planes is the same. If the color space is YCbCr, assume the luminance (Y = 0th) component has the greatest precision, although the chrominance (Cr = 1st, Cb = 2nd) components are usually sub-sampled. */ if ((i = jas_image_cmptprec(img,0)) != 8) /* not 24-bit pixels */ { SetISTR(ISTR_WARNING,pixel_size,fbasename,i*3); goto L1; }; s = "Color"; pinfo->type = PIC24; pinfo->colType = F_FULLCOLOR; /* XXX Unlike the IJG JPEG Library, the JasPer Library is apparently unable to quantize colors or tell us whether the image's colors were quantized by its creator, so it seems that we can't return a color map for XV to potentially use 8-bit indexed color. If there *is* an easy way to do it that escapes me, put the code here someday. */ }; if (!(pinfo->pic = (byte *)malloc(imagesize))) /* image buffer for XV */ { SetISTR(ISTR_WARNING,no_mem,fbasename,jpc_kind); goto L1; }; pinfo->frmType = F_JPC; sprintf(pinfo->fullInfo,full_msg,s,jpc_kind,filesize); sprintf(pinfo->shrtInfo,shrt_msg,pinfo->w,pinfo->h,s,jpc_kind); SetISTR(ISTR_INFO,load_msg,pinfo->normw,pinfo->normh,s,jpc_kind,filesize); if (vstride == 1) /* gray-scale image */ { register jas_stream_t *c = img->cmpts_[0]->stream_; register byte *p = pinfo->pic; /* Since this is a 1-plane image, avoid a lot of errant nonsense in the JasPer Library by sequentially reading all of the data into our buffer directly. */ do if ((i = (*c->ops_->read_)(c->obj_,(char *)p,imagesize)) <= 0) { SetISTR(ISTR_WARNING,i < 0 ? read_err : truncated,fbasename, jpc_kind); goto L1; } while ((p += i),(imagesize -= i) > 0); } else /* RGB color image */ { /* Reading color images is inefficient because JPEG 2000 wants to partition file data into separate image planes (colors), while XV wants data samples from each plane to be interleaved as 3-byte pixels. Apparently the fastest method consists of 3 passes through the XV image buffer, into which we insert the bytes of each component. */ i = 0; do /* each color component */ { long npix = imagesize/3; register jas_stream_t *c = img->cmpts_[i]->stream_; register byte *p = pinfo->pic + i; do /* each pixel */ { register int b; if ((b = jas_stream_getc(c)) < 0) { SetISTR(ISTR_WARNING, (c->flags_ & JAS_STREAM_EOF) ? truncated : read_err, fbasename,jpc_kind); goto L1; }; *p = b; } while ((p += 3),--npix > 0); } while (++i <= 2); }; ok = 1; /* Success! */ L1: jas_image_destroy(img); L2: (void)jas_stream_close(str); free(iobuf); L3: return ok; } int LoadJP2(char *fname,register PICINFO *pinfo,int quick) { jas_image_t *img; jas_stream_t *str; FILE *fp; byte *pic; char *fbasename, *iobuf, *s; unsigned long filesize, imagesize; int ok = 0, vstride; register int i; /* Load a JPEG 2000 JP2 image file into a pixel buffer for XV, doing any necessary color-space conversion to end up with 8-bit gray scale or 24-bit RGB. For now, ignore the "quick" option to return a reduced-resolution or -size image. Return 1 on success, or 0 on failure. */ if (!(fp = xv_fopen(fname,fmode))) return 0; fbasename = BaseName(fname); /* Input file's base name, for message(s) */ if (!(iobuf = SetBuf(fp))) { (void)fclose(fp); SetISTR(ISTR_WARNING,no_mem,fbasename,jpc_kind); goto L3; }; /* Before telling the JasPer Library about this file, get its size for display purposes. Non-UNIX systems don't necessarily simulate "stat(2)", so do it crudely but portably by seeking to the end, then back to the beginning. */ fseek(fp,0L,2); filesize = ftell(fp); fseek(fp,0L,0); /* "jas_stream_close()" will eventually close the input file, so only do it explicitly if no stream can be created: */ if (!(str = jas_stream_freopen(fname,fmode,fp))) { (void)fclose(fp); goto L3; }; /* It's not clear to me whether the following represents a JasPer Library "bug" but it sure looks goofy: Unless a stream buffer is marked "read only", which only happens when the stream's "fillbuf" method is called, even though our buffers are always "read only", the library will try to flush out buffer contents when the stream is destroyed, which makes it die a horrible death. So, mark the stream buffer proactively: */ str->bufmode_ |= JAS_STREAM_RDBUF; /* We will only read the stream buffer */ if (!(img = jp2_decode(str,0))) goto L2; if (imagesize = vstride = jas_image_numcmpts(img)) /* no. of color planes */ { /* After the image-component streams created, they are left in a "write" state with the streams' cursors positioned at their ends, so "seek" in order to "read" each stream from its beginning. */ i = vstride; while (--i >= 0) if (jas_stream_seek(img->cmpts_[i]->stream_,0L,0)) { SetISTR(ISTR_WARNING,read_err,fbasename,jp2_kind); goto L1; } }; imagesize *= pinfo->normw = pinfo->w = jas_image_width(img); imagesize *= pinfo->normh = pinfo->h = jas_image_height(img); /* Sanity-check the image's color space and no. of colors. For now, accept only "generic" color spaces, not files needing image-specific color correction, but fix that someday... */ switch (vstride) { static char color_space[]={"%s: invalid color space!"}; default: SetISTR(ISTR_WARNING,bad_samp,fbasename,vstride,jp2_kind); goto L1; case 1: if ( !jas_clrspc_isunknown(i = jas_image_clrspc(img)) && jas_clrspc_fam(i) != JAS_CLRSPC_FAM_GRAY ) { SetISTR(ISTR_WARNING,color_space,fbasename); goto L1; }; if ((i = jas_image_cmptprec(img,0)) != 8) /* not 8-bit pixels */ { SetISTR(ISTR_WARNING,pixel_size,fbasename,i); goto L1; }; s = "Greyscale"; pinfo->type = PIC8; pinfo->colType = F_GREYSCALE; i = 256; /* Return fake indexed-color "map" */ while (--i >= 0) pinfo->r[i] = pinfo->g[i] = pinfo->b[i] = i; break; case 3: if (jas_clrspc_isunknown(i = jas_image_clrspc(img))) { SetISTR(ISTR_WARNING,color_space,fbasename); goto L1; }; if (jas_clrspc_fam(i) != JAS_CLRSPC_FAM_RGB) { jas_image_t *oimg; jas_cmprof_t *profile; /* Here's where the JasPer Library really shines. The only color space that XV handles is RGB, so if that's not what our image uses, then to quote Capt. Kirk: "Make it so!" */ if (!(profile = jas_cmprof_createfromclrspc(JAS_CLRSPC_SRGB))) { SetISTR(ISTR_WARNING,"%s: can't create RGB profile", fbasename); goto L1; }; img = jas_image_chclrspc( oimg = img , profile , JAS_CMXFORM_INTENT_PER ); jas_cmprof_destroy(profile); if (!img) /* Oops! We failed, so restore original image */ { img = oimg; SetISTR(ISTR_WARNING,"%s: can't convert to RGB",fbasename); goto L1; }; jas_image_destroy(oimg); }; /* BEWARE OF KLUDGE: If the image's color space is RGB, assume that the data-sample precision for all color planes is the same. If the color space is YCbCr, assume the luminance (Y = 0th) component has the greatest precision, although the chrominance (Cr = 1st, Cb = 2nd) components are usually sub-sampled. */ if ((i = jas_image_cmptprec(img,0)) != 8) /* not 24-bit pixels */ { SetISTR(ISTR_WARNING,pixel_size,fbasename,i*3); goto L1; }; s = "Color"; pinfo->type = PIC24; pinfo->colType = F_FULLCOLOR; /* XXX Unlike the IJG JPEG Library, the JasPer Library is apparently unable to quantize colors or tell us whether the image's colors were quantized by its creator, so it seems that we can't return a color map for XV to potentially use 8-bit indexed color. If there *is* an easy way to do it that escapes me, put the code here someday. */ }; if (!(pinfo->pic = (byte *)malloc(imagesize))) /* image buffer for XV */ { SetISTR(ISTR_WARNING,no_mem,fbasename,jp2_kind); goto L1; }; pinfo->frmType = F_JP2; sprintf(pinfo->fullInfo,full_msg,s,jp2_kind,filesize); sprintf(pinfo->shrtInfo,shrt_msg,pinfo->w,pinfo->h,s,jp2_kind); SetISTR(ISTR_INFO,load_msg,pinfo->normw,pinfo->normh,s,jp2_kind,filesize); if (vstride == 1) /* gray-scale image */ { register jas_stream_t *c = img->cmpts_[0]->stream_; register byte *p = pinfo->pic; /* Since this is a 1-plane image, avoid a lot of errant nonsense in the JasPer Library by sequentially reading all of the data into our buffer directly. */ do if ((i = (*c->ops_->read_)(c->obj_,(char *)p,imagesize)) <= 0) { SetISTR(ISTR_WARNING,i < 0 ? read_err : truncated,fbasename, jp2_kind); goto L1; } while ((p += i),(imagesize -= i) > 0); } else /* RGB color image */ { /* Reading color images is inefficient because JPEG 2000 wants to partition file data into separate image planes (colors), while XV wants data samples from each plane to be interleaved as 3-byte pixels. Apparently the fastest method consists of 3 passes through the XV image buffer, into which we insert the bytes of each component. */ i = 0; do /* each color component */ { long npix = imagesize/3; register jas_stream_t *c = img->cmpts_[i]->stream_; register byte *p = pinfo->pic + i; do /* each pixel */ { register int b; if ((b = jas_stream_getc(c)) < 0) { SetISTR(ISTR_WARNING, (c->flags_ & JAS_STREAM_EOF) ? truncated : read_err, fbasename,jp2_kind); goto L1; }; *p = b; } while ((p += 3),--npix > 0); } while (++i <= 2); }; ok = 1; /* Success! */ L1: jas_image_destroy(img); L2: (void)jas_stream_close(str); free(iobuf); L3: return ok; } /* The following variables and subroutines are used when writing a JPEG 2000 file, which is done mainly using call-backs from "X Windows" widgets. The most complicated part of this interface is: managing interactions with a window to request the boat-loads of options that the JasPer Library supports. Start by defining subwindow sizes, plus indices into several arrays of corresponding widget-state variables. IMPLEMENTATION NOTES: The following dimensions create a tall, thin window which appears to have considerable empty space at the bottom. Before you complain, click the Precinct Height menu button in order to the tall pop-up subwindow that it generates. If the parent window is made shorter, then this pop-up will be clipped, which is an ugly nuisance. I don't know how to make the pop-up visible outside its parent's borders; do you? If there's some way to make "X Windows" do this, then we might consider making the parent shorter. Note that there is currently no mechanism to program the no. of intermediate layers used by the encoder, or their rates. This is potentially a large and complicated data-entry problem, and perhaps someday we can invent a clever solution using the rest of the parent window's space. */ # define JP2KW 275 /* Window width, in pixels */ # define JP2KH 400 /* Window height, in pixels */ # define BUTTW 51 /* Button width, in pixels (odd for half-toning) */ # define BUTTH 20 /* Button height, in pixels */ # define MENUW 75 /* Menu-button width, in pixels (odd for half-toning) */ # define MENUH 24 /* Menu-button height, in pixels */ # define RBUTH 20 /* Radio button height, in pixels */ # define RBUTW 51 /* Radio button width, in pixels (odd for half-toning) */ # define TEXTH (LINEHIGH+5) /* Text subwindow height, in pixels */ # define TEXTW 75 /* Text subwindow width, in pixels */ # define J_BOK 0 /* Boolean "Ok" button */ # define J_BCANC 1 /* Boolean "Cancel" button */ # define J_NBUTT 2 /* No. of regular button widgets */ # define J_CSOP 0 /* Boolean encoding-style option buttons */ # define J_CEPH 1 # define J_CLAZY 2 # define J_CTERM 3 # define J_CSEGS 4 # define J_CVCAU 5 # define J_CPTRM 6 # define J_CRSTP 7 # define J_NCHKB 8 /* No. of check-box button widgets */ # define J_MCBXW 0 /* 1-of-N menu-selection buttons */ # define J_MCBXH 1 # define J_MPREW 2 # define J_MPREH 3 # define J_MPROG 4 # define J_NMENU 5 /* No. of menu-button widgets */ # define J_TGBIT 0 /* (Unsigned numeric) string subwindows */ # define J_TRES 1 # define J_TRATE 2 # define J_NTEXT 3 /* No. of text subwindows */ static BUTT button[J_NBUTT]; static CBUTT chkbut[J_NCHKB]; static MBUTT menu[J_NMENU]; static RBUTT *radio; static Window text[J_NTEXT]; static int colorType, format, textval[J_NTEXT]; static char *ProgList[]={"lrcp","rlcp","rpcl","pcrl","cprl"}; void CreateJP2KW(void) { static char EXP2_0[] ={ "1"}, /* Successive powers of 2 */ EXP2_1[] ={ "2"}, EXP2_2[] ={ "4"}, EXP2_3[] ={ "8"}, EXP2_4[] ={ "16"}, EXP2_5[] ={ "32"}, EXP2_6[] ={ "64"}, EXP2_7[] ={ "128"}, EXP2_8[] ={ "256"}, EXP2_9[] ={ "512"}, EXP2_10[]={ "1024"}, EXP2_11[]={ "2048"}, EXP2_12[]={ "4096"}, EXP2_13[]={ "8192"}, EXP2_14[]={"16384"}, EXP2_15[]={"32768"}; static char *CBoxList[]= { EXP2_1 ,EXP2_2 ,EXP2_3 ,EXP2_4 ,EXP2_5,EXP2_6 ,EXP2_7 ,EXP2_8 ,EXP2_9, EXP2_10,EXP2_11 }, *PrecList[]= { EXP2_0,EXP2_1,EXP2_2 ,EXP2_3 ,EXP2_4 ,EXP2_5 ,EXP2_6 ,EXP2_7 , EXP2_8,EXP2_9,EXP2_10,EXP2_11,EXP2_12,EXP2_13,EXP2_14,EXP2_15 }, hstr[]={"Height"}, wstr[]={"Width"}; if (!(jp2kW = CreateWindow( "xvjp2k" , "XVjp2k" , 0 , JP2KW , JP2KH , infofg , infobg , 0 ) ) ) FatalError("can't create JPEG 2000 window!"); XSelectInput(theDisp,jp2kW,ExposureMask|ButtonPressMask|KeyPressMask); /* Create a row of 2 boolean-valued, regular buttons ("Ok" and "Cancel") in the window's bottom right corner. */ BTCreate(&button[J_BOK ],jp2kW, JP2KW-2*BUTTW-20,JP2KH-10-BUTTH-1,BUTTW,BUTTH, "Ok" ,infofg,infobg,hicol,locol); BTCreate(&button[J_BCANC],jp2kW, JP2KW- BUTTW-10,JP2KH-10-BUTTH-1,BUTTW,BUTTH, "Cancel",infofg,infobg,hicol,locol); /* Create a vertical column of 8 boolean-valued, check-box buttons (for encoding-style options) down the window's left side. */ CBCreate(&chkbut[J_CSOP] ,jp2kW, 10,10+ASCENT+SPACING+2*LINEHIGH+0*BUTTH, "sop" ,infofg,infobg,hicol,locol); CBCreate(&chkbut[J_CEPH] ,jp2kW, 10,10+ASCENT+SPACING+2*LINEHIGH+1*BUTTH, "eph" ,infofg,infobg,hicol,locol); CBCreate(&chkbut[J_CLAZY],jp2kW, 10,10+ASCENT+SPACING+2*LINEHIGH+2*BUTTH, "lazy" ,infofg,infobg,hicol,locol); CBCreate(&chkbut[J_CTERM],jp2kW, 10,10+ASCENT+SPACING+2*LINEHIGH+3*BUTTH, "termall" ,infofg,infobg,hicol,locol); CBCreate(&chkbut[J_CSEGS],jp2kW, 10,10+ASCENT+SPACING+2*LINEHIGH+4*BUTTH, "segsym" ,infofg,infobg,hicol,locol); CBCreate(&chkbut[J_CVCAU],jp2kW, 10,10+ASCENT+SPACING+2*LINEHIGH+5*BUTTH, "vcausal" ,infofg,infobg,hicol,locol); CBCreate(&chkbut[J_CPTRM],jp2kW, 10,10+ASCENT+SPACING+2*LINEHIGH+6*BUTTH, "pterm" ,infofg,infobg,hicol,locol); CBCreate(&chkbut[J_CRSTP],jp2kW, 10,10+ASCENT+SPACING+2*LINEHIGH+7*BUTTH, "resetprob",infofg,infobg,hicol,locol); /* Create text subwindows for unsigned decimal integer values. */ text[J_TGBIT] = XCreateSimpleWindow(theDisp,jp2kW, JP2KW-TEXTW-10,10+ASCENT+SPACING+2*LINEHIGH+3*MENUH+0*TEXTH,TEXTW,TEXTH, 1,infofg,infobg); XSelectInput(theDisp,text[J_TGBIT],ExposureMask|KeyPressMask); text[J_TRES ] = XCreateSimpleWindow(theDisp,jp2kW, JP2KW-TEXTW-10,10+ASCENT+SPACING+2*LINEHIGH+3*MENUH+1*TEXTH,TEXTW,TEXTH, 1,infofg,infobg); XSelectInput(theDisp,text[J_TRES ],ExposureMask|KeyPressMask); text[J_TRATE] = XCreateSimpleWindow(theDisp,jp2kW, JP2KW-TEXTW-10,10+ASCENT+SPACING+2*LINEHIGH+3*MENUH+2*TEXTH,TEXTW,TEXTH, 1,infofg,infobg); XSelectInput(theDisp,text[J_TRATE],ExposureMask|KeyPressMask); /* Create a row of 2 boolean-valued radio buttons (for the "Rate" subwindow value's unit of measure). The 1st button is "selected" by default. */ radio = RBCreate(0,jp2kW, JP2KW-19*RBUTW/8-10,10+ASCENT+SPACING+2*LINEHIGH+3*MENUH+3*TEXTH+4, "Percent",infofg,infobg,hicol,locol); (void)RBCreate(radio,jp2kW, JP2KW-1*RBUTW-10 ,10+ASCENT+SPACING+2*LINEHIGH+3*MENUH+3*TEXTH+4, "Bytes",infofg,infobg,hicol,locol); /* Create pop-up menu-selection buttons after mapping the above subwindows, since we don't want the pop-up menus mapped unless the corresponding button is selected. */ XMapSubwindows(theDisp,jp2kW); MBCreate(&menu[J_MCBXW],jp2kW, JP2KW-2*MENUW-10,10+ASCENT+SPACING+2*LINEHIGH+0*MENUH,MENUW,MENUH, wstr ,CBoxList,sizeof CBoxList/sizeof *CBoxList,infofg,infobg, hicol,locol); MBCreate(&menu[J_MCBXH],jp2kW, JP2KW-2*MENUW-10,10+ASCENT+SPACING+2*LINEHIGH+1*MENUH,MENUW,MENUH, hstr ,CBoxList,sizeof CBoxList/sizeof *CBoxList,infofg,infobg, hicol,locol); MBCreate(&menu[J_MPREW],jp2kW, JP2KW-1*MENUW-10,10+ASCENT+SPACING+2*LINEHIGH+0*MENUH,MENUW,MENUH, wstr ,PrecList,sizeof PrecList/sizeof *PrecList,infofg,infobg, hicol,locol); MBCreate(&menu[J_MPREH],jp2kW, JP2KW-1*MENUW-10,10+ASCENT+SPACING+2*LINEHIGH+1*MENUH,MENUW,MENUH, hstr ,PrecList,sizeof PrecList/sizeof *PrecList,infofg,infobg, hicol,locol); MBCreate(&menu[J_MPROG],jp2kW, JP2KW-1*MENUW-10,10+ASCENT+SPACING+2*LINEHIGH+2*MENUH,MENUW,MENUH, "Order",ProgList,sizeof ProgList/sizeof *ProgList,infofg,infobg, hicol,locol); /* Initialize values represented by widgets, which should correspond to default compiled into the JasPer Library. Unfortunately, as of Version 1.701 there is no easy way for an application to extract these from the library, so the following code might get out of sync over time: */ menu[J_MCBXW].hascheck = 1; menu[J_MCBXW].flags[ 5] = 1; menu[J_MCBXH].hascheck = 1; menu[J_MCBXH].flags[ 5] = 1; menu[J_MPREW].hascheck = 1; menu[J_MPREW].flags[15] = 1; menu[J_MPREH].hascheck = 1; menu[J_MPREH].flags[15] = 1; menu[J_MPROG].hascheck = 1; menu[J_MPROG].flags[ 0] = 1; textval[J_TGBIT] = 2; /* No. of guard bits */ textval[J_TRES ] = 6; /* Max. no. of resolution levels */ textval[J_TRATE] = 100; /* Rate = 100% */ } void JP2KSaveParams(int fmt,char *fname,int col) /* Save output-file parms */ { format = fmt; /* Desired file format: F_JPC|F_JP2 */ fbasename = fname; /* ->Output file path */ colorType = col; /* Desired color space: F_GREYSCALE|... */ } static void StoreJP2K(char *options) { static jas_image_cmptparm_t parm[3]= /* Image parameters */ {{0,0,1,1,0,0,8,0},{0,0,1,1,0,0,8,0},{0,0,1,1,0,0,8,0}}; static char nomem[]={"StoreJP2K: out of memory\n"}, write[]={"w"}; jas_image_t *img; jas_stream_t *str; FILE *fp, *fp2; byte *pic, *r, *g, *b; char *filename, *iobuf = 0; unsigned long imagesize; jas_clrspc_t color_space; int nc, w, h, pfree, ptype, error = 1; register int i; /* This is a generic subroutine for writing JPEG 2000 image files using the JasPer Library. Our argument is an ASCII string, containing a Space (" ")- separted sequence of encoder options that currently aren't well documented. Most of the work is identical for both ".jpc" and ".jp2" files. Start by verifying that the output file can be opened, then get an image buffer from XV and begin crunching it into a suitable form for the JasPer Library. */ if (!(fp = OpenOutFile(filename = fbasename))) return; /* Oops! */ setbuf(fp,0); /* We don't really use this file pointer for I/O; see below */ fbasename = BaseName(filename); WaitCursor(); pic = GenSavePic(&ptype,&w,&h,&pfree,&nc,&r,&g,&b); imagesize = w*h; if (ptype == PIC24) imagesize *= 3; /* As an optimization to save file space, even if our user didn't ask to store a gray-scale image, check whether we could and, if so, do it anyway. */ if (colorType != F_GREYSCALE) /* can we force a gray-scale image? */ { if (ptype == PIC8) { i = nc; while (--i >= 0 && r[i] == g[i] && r[i] == b[i]); } else /* PIC24 */ { register byte *p = pic + imagesize; while ((p -= 3) >= pic && p[0] == p[1] && p[0] == p[2]); i = p-pic; }; if (i < 0) colorType = F_GREYSCALE; /* We made it all the way through */ }; /* If XV is currently color-mapping the image, make a color-mapped copy so that the map needn't be transmitted in the output file. */ if ((i = (colorType != F_GREYSCALE) << 1 | (ptype != PIC8)) != 3) { byte *tmp = pic, *last = pic + imagesize; register byte *from = tmp, *to = pic; if (!(pic = (byte *)malloc(imagesize))) FatalError(nomem); switch (i) { /* Color-map 8->8 bit image. */ case 0: do { i = *from; *to++ = MONO(r[i],g[i],b[i]); } while (++from < last); break; /* Color-map 24->8 bit image. */ case 1: do *to++ = MONO(from[0],from[1],from[2]); while ((from += 3) < last); break; /* Color-map 8->24 bit image. */ case 2: do { i = *from; *to++ = r[i]; *to++ = g[i]; *to++ = b[i]; } while (++from < last); break; }; if (pfree) free(tmp); /* Release the original image buffer if we can */ pfree = 1; /* Let the modified buffer be released later */ }; /* Initialize various image-file parameter variables. */ parm[0].width = w; parm[0].height = h; if (colorType == F_GREYSCALE) /* gray-scale image */ { ptype = 1; /* No. of color planes */ color_space = JAS_CLRSPC_SGRAY; } else /* RGB color image */ { ptype = 3; /* No. of color planes */ color_space = JAS_CLRSPC_SRGB; parm[2].width = parm[1].width = parm[0].width; parm[2].height = parm[1].height = parm[0].height; }; /* Now comes a egregious hack: The JasPer Library will eventually want to close the output file that it writes, but since XV opened the file, XV also thinks it has the right to close the file! In order to pacify them both, we duplicate the file pointer and let the JasPer Library have it, while we retain the original for XV. XXX This code is very UNIX-specific; what's an equivalent hack for Windows? */ if (!(fp2 = (i = dup(fileno(fp))) >= 0 ? fdopen(i,write) : 0)) FatalError("StoreJP2K: can't duplicate output file pointer\n"); if (!(iobuf = SetBuf(fp2))) { (void)fclose(fp2); FatalError(nomem); }; /* Hand our output file to the JasPer Library and create an image object. "jas_stream_close()" will eventually close our output file, so only do it explicitly if no stream can be created. If everything looks copacetic, then write our buffer contents to the image components' streams. */ if (!(str = jas_stream_freopen(filename,write,fp2))) { (void)fclose(fp2); FatalError("StoreJP2K: can't open output stream\n"); }; if (!(img = jas_image_create(ptype,parm,color_space))) goto L2; if (ptype == 1) { register jas_stream_t *c = img->cmpts_[0]->stream_; register byte *p = pic; /* Since this is a 1-plane image, avoid a lot of errant nonsense in the JasPer Library by sequentially writing all of the data directly from our buffer. */ jas_image_setcmpttype(img,0,JAS_IMAGE_CT_GRAY_Y); img->cmpts_[0]->type_ = JAS_IMAGE_CT_GRAY_Y; do if ((i = (*c->ops_->write_)(c->obj_,(char *)p,imagesize)) <= 0) goto L1; while ((p += i),(imagesize -= i) > 0); if (jas_stream_flush(c) < 0) goto L1; } else /* RGB color image */ { /* Writing color images is inefficient because JPEG 2000 wants to partition file data into separate image planes (colors), while XV wants data samples from each plane to be interleaved as 3-byte pixels. Apparently the fastest method consists of 3 passes through the XV image buffer, from which we extract the bytes of each component. */ i = 0; do /* each color component */ { long npix = imagesize/3; register jas_stream_t *c = img->cmpts_[i]->stream_; register byte *p = pic + i; jas_image_setcmpttype(img,i,i+JAS_IMAGE_CT_RGB_R); do if (jas_stream_putc(c,*p) < 0) goto L1; while ((p += 3),--npix > 0); if (jas_stream_flush(c) < 0) goto L1; } while (++i <= 2); }; if ( (*(format == F_JPC ? jpc_encode : jp2_encode))(img,str,options) >= 0 && jas_stream_flush(str) >= 0 ) error = 0; /* Success! */ L1: jas_image_destroy(img); L2: (void)jas_stream_close(str); if (iobuf) free(iobuf); if (pfree) free(pic); if (!CloseOutFile(fp,filename,error)) DirBox(0); SetCursors(-1); } void JP2KDialog(int vis) { if (jp2kUp = vis) CenterMapWindow(jp2kW,0,0,JP2KW,JP2KH); else XUnmapWindow(theDisp,jp2kW); } static void TWRedraw(Window w,unsigned int val) { char buf[11]; register int i; /* Draw a 1-line numeric text string in the specified window, representing the argument value as a left-justified unsigned decimal integer, followed by a "cursor" icon. */ sprintf(buf,"%u",val); if (ctrlColor) XClearArea(theDisp,w,2,2,TEXTW-4,TEXTH-4,False); else XClearWindow(theDisp,w); Draw3dRect(w,0,0,TEXTW-1,TEXTH-1,R3D_IN,2,hicol,locol,infobg); XSetForeground(theDisp,theGC,infofg); XDrawString(theDisp,w,theGC,3,ASCENT+3,buf,i = strlen(buf)); /* Draw a "cursor" icon after the numeric string. */ i = XTextWidth(mfinfo,buf,i); XDrawLine(theDisp,w,theGC,i+3,2 ,i+3,2+CHIGH+1); XDrawLine(theDisp,w,theGC,i+3,2+CHIGH+1,i+5,2+CHIGH+3); XDrawLine(theDisp,w,theGC,i+3,2+CHIGH+1,i+1,2+CHIGH+3); } int JP2KCheckEvent(register XEvent *xev) { int ok = 0; /* Check whether the argument "X Windows" event is for one of our subwindows. If it is, handle the event and return 1; otherwise, return 0. */ if (!jp2kUp) return 0; switch (xev->type) { KeySym ks; int len; char buf[128]; register int i; /* Throw away excess "expose" events for "dumb" windows. */ case Expose : # define E ((XExposeEvent *)xev) if (E->window == jp2kW) { XRectangle rect; rect.x = E->x ; rect.y = E->y ; rect.width = E->width; rect.height = E->height; XSetClipRectangles(theDisp,theGC,0,0,&rect,1,Unsorted); XSetForeground(theDisp,theGC,infofg); XSetBackground(theDisp,theGC,infobg); i = sizeof button/sizeof *button; while (--i >= 0) BTRedraw(&button[i]); i = sizeof chkbut/sizeof *chkbut; while (--i >= 0) CBRedraw(&chkbut[i]); i = sizeof menu/sizeof *menu; while (--i >= 0) MBRedraw(&menu[i]); RBRedraw(radio,-1); DrawString(jp2kW,10,10+ASCENT,"Save JPEG 2000 File..."); DrawString(jp2kW,10,10+ASCENT+2*LINEHIGH,"Style options:"); DrawString(jp2kW,JP2KW-2*MENUW-10,10+ASCENT+2*LINEHIGH, "Coding Blk"); DrawString(jp2kW,JP2KW-1*MENUW-10,10+ASCENT+2*LINEHIGH, " Precinct"); DrawString(jp2kW, JP2KW-2*MENUW-10,10+ASCENT+SPACING+3*LINEHIGH+2*MENUH, "Progression:"); DrawString(jp2kW, JP2KW-2*TEXTW-10,10+ASCENT+SPACING+3*LINEHIGH+3*MENUH+0*TEXTH, "Guard bits:"); DrawString(jp2kW, JP2KW-2*TEXTW-10,10+ASCENT+SPACING+3*LINEHIGH+3*MENUH+1*TEXTH, "Res levels:"); DrawString(jp2kW, JP2KW-2*TEXTW-10,10+ASCENT+SPACING+3*LINEHIGH+3*MENUH+2*TEXTH, "Rate:"); XSetClipMask(theDisp,theGC,None); ok = 1; break; }; i = sizeof text/sizeof *text; while (--i >= 0 && E->window != text[i]); if (i >= 0) { TWRedraw(E->window,textval[i]); ok = 1; }; break; # undef E /* Check whether the user pressed one of our buttons. */ case ButtonPress: # define E ((XButtonEvent *)xev) if (E->button = Button1 && E->window == jp2kW) { register int j; ok = 1; /* Check whether a regular button was pressed */ i = sizeof button/sizeof *button; while ( --i >= 0 && !PTINRECT(E->x,E->y,button[i].x,button[i].y,button[i].w,button[i].h) ); if (i >= 0) /* our button was pressed */ { char options[1024]; register char *p, *q; if (!BTTrack(&button[i])) break; /* Ignore it */ if (i != J_BOK) /* must be "Cancel" button */ { JP2KDialog(0); break; }; /* Our user hit the "Ok" button. At this point, we have an ugly job to do: JasPer Library encoder options must be specified as an ASCII string of Space (" ")-separated [=] entries, so we must collect the values of all of our widgets and subwindows, determine whether our user has requested any non-default values, then build the string only for the JasPer Library to immediately tear it apart again. Yechh! */ if ((unsigned)textval[J_TGBIT]-1 > 7) { OpenAlert("No. of guard bits must be 1-8"); sleep(3); CloseAlert(); break; }; if ((unsigned)textval[J_TRES ] <= 0) { OpenAlert("Maximum resolution levels must be >= 1"); sleep(3); CloseAlert(); break; }; /* XXX Should we check and complain if the rate is zero? Jasper Library Version 1.701 apparently accepts that value, even though it seems kinda weird. */ p = options; i = 0; do if ((j = MBWhich(&menu[i])) != 5) { static char *parm[2]={"cblkwidth=%u","cblkheight=%u"}; if (p > options) *p++ = ' '; sprintf(p,parm[i-J_MCBXW],++j); while (*++p); } while (++i <= J_MCBXH); do if ((j = MBWhich(&menu[i])) < 15) { static char *parm[2]={"prcwidth=%u","prcheight=%u"}; if (p > options) *p++ = ' '; sprintf(p,parm[i-J_MPREW],j); while (*++p); } while (++i <= J_MPREH); if (j = MBWhich(&menu[i])) { if (p > options) *p++ = ' '; *p++ = 'p'; *p++ = 'r'; *p++ = 'g'; *p = '='; q = ProgList[j]; while (*++p = *q++); }; if ((i = textval[J_TRES ]) != 6) { if (p > options) *p++ = ' '; sprintf(p,"numrlvls=%u",i); while (*++p); }; i = 0; do if (chkbut[i].val) /* append this encoding option */ { if (p > options) *p++ = ' '; q = chkbut[i].str; while (*p++ = *q++); *--p = '\000'; } while (++i < sizeof chkbut/sizeof *chkbut); if ((i = textval[J_TGBIT]) != 2) { if (p > options) *p++ = ' '; sprintf(p,"numgbits=%u",i); while (*++p); }; if ((i = textval[J_TRATE]) != 100) { if (p > options) *p++ = ' '; *p++ = 'r'; *p++ = 'a'; *p++ = 't'; *p++ = 'e'; *p++ = '='; if (i) /* non-zero rate */ { if (RBWhich(radio)) sprintf(p,"%uB",i); /* # Bytes */ else /* the value is a percentage */ { if (i > 100) i = 200; /* => Raw size + 1 */ sprintf(p,"%u.%.2u",i/100,i%100); } while (*++p); } else /* rate = 0 */ { /* XXX This is apparently valid, since JasPer Library Version 1.701 doesn't complain about it. */ *p++ = '0'; *p = '\000'; } }; StoreJP2K(options); /* Finally, do the *real* work! */ JP2KDialog(0); p = GetDirFullName(); if (!ISPIPE(*p)) { XVCreatedFile(p); StickInCtrlList(0); }; break; }; /* See whether a check-box button was pressed. */ i = sizeof chkbut/sizeof *chkbut; while (--i >= 0 && !CBClick(&chkbut[i],E->x,E->y)); if (i >= 0) /* our button was pressed */ { (void)CBTrack(&chkbut[i]); break; }; /* See whether a menu button was pressed. */ i = sizeof menu/sizeof *menu; while (--i >= 0 && !MBClick(&menu[i],E->x,E->y)); if (i >= 0) /* our button was pressed */ { register int j; if ((j = MBTrack(&menu[i])) >= 0) { switch (i) { /* The Jasper Library constrains a coding box's area (width x height) to be <= 4096 pixels, so if this button-press would violate that condition, then quietly limit the box's orthogonal dimension by just enough to compensate. */ case J_MCBXH: case J_MCBXW: if (MBWhich(&menu[!i]) + j >= 10) MBSelect(&menu[!i],10 - j); }; MBSelect(&menu[i],j); MBSetActive(&menu[i],1); }; break; }; /* See whether a radio button was pressed. */ if ( (i = RBClick(radio,E->x,E->y)) >= 0 /* button pressed */ && RBTrack(radio,i) ) { RBSelect(radio,i); RBSetActive(radio,i,1); }; break; }; XBell(theDisp,50); break; # undef E /* Translate a few key-press events into simulated button events. */ case KeyPress : # define E ((XKeyEvent *)xev) buf[len = XLookupString(E,buf,sizeof buf,&ks,0)] = '\000'; RemapKeyCheck(ks,buf,&len); if (E->window == jp2kW) { ok = 1; if (len > 0) switch (buf[0]) { case '\r' : case '\n' : FakeButtonPress(&button[J_BOK ]); break; case '\033': FakeButtonPress(&button[J_BCANC]); }; break; }; i = sizeof text/sizeof *text; while (--i >= 0 && E->window != text[i]); if (i >= 0) /* a key was pressed in our text window */ { int oldval = textval[i]; /* Save this subwindow's old value */ register int c, j = 0; /* This is a *very* simple text-input editing loop that assembles an an unsigned integer from successive ASCII decimal digits, typed into one of our 1-line subwindows. For convenience, the only character recognized is Backspace. */ ok = 1; L: if (j >= len) break; if ((unsigned)(c = buf[j++] - '0') <= 9) { TWRedraw(text[i],textval[i] = textval[i]*10 + c); goto L; }; if ((c += '0') == '\b') { TWRedraw(text[i],textval[i] = textval[i]/10); goto L; }; textval[i] = oldval; }; XBell(theDisp,50); # undef E }; return ok; } #endif /* HAVE_JP2K */ @EOF mkdir -p bits echo x - bits/br_jp2 cat >bits/br_jp2 <<'@EOF' #define br_jp2_width 48 #define br_jp2_height 48 static unsigned char br_jp2_bits[] = { 0xe0, 0xff, 0xff, 0xff, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0x09, 0x00, 0x20, 0x00, 0x00, 0x00, 0x11, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x20, 0x00, 0x00, 0x00, 0x41, 0x00, 0x20, 0x00, 0x00, 0x00, 0x81, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x01, 0x20, 0x00, 0x00, 0x00, 0xff, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0xff, 0xff, 0xff, 0x7f, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0xff, 0xff, 0xff, 0x7f, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0xfb, 0xf1, 0x01, 0x02, 0x20, 0x00, 0xfb, 0xfb, 0x03, 0x02, 0x20, 0x00, 0x1b, 0x1b, 0x03, 0x02, 0x20, 0x00, 0x1b, 0x1b, 0x00, 0x02, 0x20, 0x00, 0x1b, 0x1b, 0x00, 0x02, 0x20, 0x00, 0x9b, 0x1b, 0x00, 0x02, 0x20, 0x00, 0xfb, 0x19, 0x00, 0x02, 0x20, 0x00, 0xfb, 0x18, 0x00, 0x02, 0x20, 0x18, 0x1b, 0x18, 0x00, 0x02, 0x20, 0x18, 0x1b, 0x18, 0x00, 0x02, 0x20, 0xb8, 0x1b, 0x18, 0x03, 0x02, 0x20, 0xf0, 0x19, 0xf8, 0x03, 0x02, 0x20, 0xe0, 0x18, 0xf0, 0x01, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0xff, 0xff, 0xff, 0x7f, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0xff, 0xff, 0xff, 0x7f, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x03}; @EOF echo x - bits/br_jpc cat >bits/br_jpc <<'@EOF' #define br_jpc_width 48 #define br_jpc_height 48 static unsigned char br_jpc_bits[] = { 0xe0, 0xff, 0xff, 0xff, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0x09, 0x00, 0x20, 0x00, 0x00, 0x00, 0x11, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x20, 0x00, 0x00, 0x00, 0x41, 0x00, 0x20, 0x00, 0x00, 0x00, 0x81, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x01, 0x20, 0x00, 0x00, 0x00, 0xff, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0xff, 0xff, 0xff, 0x7f, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0xff, 0xff, 0xff, 0x7f, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0xfb, 0xf1, 0x01, 0x02, 0x20, 0x00, 0xfb, 0xfb, 0x03, 0x02, 0x20, 0x00, 0x1b, 0x1b, 0x03, 0x02, 0x20, 0x00, 0x1b, 0x03, 0x03, 0x02, 0x20, 0x00, 0x1b, 0x03, 0x03, 0x02, 0x20, 0x00, 0x9b, 0x83, 0x01, 0x02, 0x20, 0x00, 0xfb, 0xc1, 0x01, 0x02, 0x20, 0x00, 0xfb, 0xe0, 0x00, 0x02, 0x20, 0x18, 0x1b, 0x70, 0x00, 0x02, 0x20, 0x18, 0x1b, 0x38, 0x00, 0x02, 0x20, 0xb8, 0x1b, 0x18, 0x00, 0x02, 0x20, 0xf0, 0x19, 0xf8, 0x03, 0x02, 0x20, 0xe0, 0x18, 0xf8, 0x03, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0xff, 0xff, 0xff, 0x7f, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0xff, 0xff, 0xff, 0x7f, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x03}; @EOF