#include "i.h" // big tables in separate files #include "rgb.h" #include "ycbcr.h" enum { CLAMPBOFF = 300, NCLAMPB = CLAMPBOFF+256+CLAMPBOFF, CLAMPNOFF = 64, NCLAMPN = CLAMPNOFF+256+CLAMPNOFF }; int dbgimg = 0; static int progressive = 0; static uchar clampb[NCLAMPB]; static int clampn_b[NCLAMPN]; static int clampn_g[NCLAMPN]; static int clampn_r[NCLAMPN]; static int cmap_r[256]; static int cmap_g[256]; static int cmap_b[256]; static void imgerror(ImageSource* is, int err); static int getc(ImageSource* is); static void ungetc(ImageSource* is); static void ungetc2(ImageSource* is); static int getlew(ImageSource* is); static int getbew(ImageSource* is); static void readis(ImageSource* is, uchar* buf, int n); static int getn(ImageSource* is, int n); static Image* newimage(ImageSource* is, int w, int h); static MaskedImage* newmi(Image* im); static void setdims(ImageSource* is); static void tracearray(int* a, int n, char* name); static void getxbitmaphdr(ImageSource* is); static MaskedImage* getxbitmapmim(ImageSource* is); static int getxbitmapdefine(ImageSource* is, int* pval); static void get_to_char(ImageSource* is, int cterm); static int get_hexbyte(ImageSource* is); static int hexdig(int c); static void getgifhdr(ImageSource* is); static int gifgettoimage(ImageSource* is); static MaskedImage* getgifmim(ImageSource* is); static uchar* gifreadcmap(ImageSource* is, int bpe, int* pmaplen); static void getjpeghdr(ImageSource* is); static int jpegmarker(ImageSource* is); static void jpegtabmisc(ImageSource* is, int* pm, int* pn); static void jpeghuffmantables(ImageSource* is, int n); static int jpeghuffmantable(ImageSource* is); static void jpegquanttables(ImageSource* is, int n); static int jpegquanttable(ImageSource* is); static MaskedImage* getjpegmim(ImageSource* is); static uchar** jpegbaselinescan(ImageSource* is, int* pnchans); static void jrestart(ImageSource* is, int mcu); static void jpegprogressivescan(ImageSource* is); static uchar** jprogressiveIDCT(ImageSource* is, int* pnchans); static void jprogressiveinit(ImageSource* is, Jpegstate* h); static void jprogressivedc(ImageSource* is, int comp); static void jprogressiveac(ImageSource* is, int comp); static void jprogressiveacinc(ImageSource* is, int comp); static void jincrement(ImageSource* is, int* acc, int k, int Pt); static void colormap1(Jpegstate* h, uchar* pic, int* data, int mcu, int nacross); static void colormapall1(Jpegstate* h, uchar** chans, int* data0, int* data1, int* data2, int mcu, int nacross); static void colormap(Jpegstate* h, uchar** chans, int** data0, int** data1, int** data2, int mcu, int nacross, int Hmax, int Vmax, int* H, int* V); static int jdecode(ImageSource* is, Huffman* t); static int jnextbyte(ImageSource* is); static int jnextborm(ImageSource* is); static int jreceive(ImageSource* is, int s); static int jreceiveEOB(ImageSource* is, int s); static int jreceivebit(ImageSource* is); static void nibbles(int c, int* p1, int* p2); static void idct(int* b); static void init_tabs(void); static void remap1(uchar* pic, int dx, int dy, uchar* cmap, int cmaplen); static void remapgrey(uchar* pic, int dx, int dy); static void remaprgb(uchar** chans, int dx, int dy); static uchar* resample(uchar* src, int sw, int sh, int dw, int dh); void imginit(void) { init_tabs(); } // Return true if mtype is an image type we can handle int supported(int mtype) { switch(mtype) { case ImageJpeg: case ImageGif: case ImageXXBitmap: return 1; } return 0; } // w,h passed in are specified width and height. // Result will be resampled if they don't match the dimensions // in the decoded picture (if only one of w,h is specified, the other // dimension is scaled by the same factor). ImageSource* newimagesource(ByteSource* bs, int w, int h) { int mtype; ImageSource* is; dbgimg = config.dbg['i']; mtype = UnknownType; if(bs->hdr != nil) mtype = bs->hdr->mtype; is = (ImageSource*)emallocz(sizeof(ImageSource)); is->width = w; is->height = h; is->mtype = mtype; is->bs = bs; return is; } int getmim(ImageSource* is, MaskedImage** pans) { MaskedImage* ans; int ret; int v; int ed; if(dbgimg) trace("img: getmim %U %dx%d\n", is->bs->req->url, is->width, is->height); if(dbgev) logtime("IMAGE_GETMIM", is->width*is->height); ans = nil; *pans = nil; ret = Mimnone; ed = is->bs->edata; // To avoid the need to check for error returns on a lot of low level functions, // we setjmp here, and call imgerror to return control to this place if // there is an error in processing the image. if((v = setjmp(is->getmimsave))) { is->err = v; ret = Mimerror; if(dbgimg) trace("getmim got err: %S\n", errphrase(is->err)); } else { if(is->bs->hdr == nil) return Mimnone; else if(is->mtype == UnknownType) is->mtype = is->bs->hdr->mtype; // temporary hack: wait until whole file is here first if(is->bs->edata == is->bs->dalloclen) { switch(is->mtype) { case ImageJpeg: ans = getjpegmim(is); break; case ImageGif: ans = getgifmim(is); break; case ImageXXBitmap: ans = getxbitmapmim(is); break; default: is->err = ERRunsupimg; ret = Mimerror; ans = nil; break; } if(ans != nil) ret = Mimdone; } } if(dbgimg) trace("img: getmim returns (%d,%p)\n", ret, (void*)ans); if(dbgev) logtime("IMAGE_GETMIM_END", 0); is->bs->lim = ed; if(is->i > ed) is->bs->lim = is->i; *pans = ans; return ret; } // transfer control back to getmim, with given error code static void imgerror(ImageSource* is, int err) { if(dbgimg) trace("Image error: %S\n", errphrase(err)); longjmp(is->getmimsave, err); } // Get next char or call imgerror if cannot static int getc(ImageSource* is) { if(is->i >= is->bs->dalloclen) { imgerror(is, ERReof); } return is->bs->data[is->i++]; } // Unget the last character. // When called before any other getting routines, we // know the buffer still has that character in it. static void ungetc(ImageSource* is) { assert(is->i > 0); is->i--; } // Like ungetc, but ungets two bytes static void ungetc2(ImageSource* is) { assert(is->i > 1); is->i -= 2; } // Get 2 bytes and return the 16-bit value, little-endian order. static int getlew(ImageSource* is) { int c0; int c1; c0 = getc(is); c1 = getc(is); return c0 + (c1 << 8); } // Get 2 bytes and return the 16-bit value, big-endian order. static int getbew(ImageSource* is) { int c0; int c1; c0 = getc(is); c1 = getc(is); return (c0 << 8) + c1; } // Copy next n bytes of input into buf // or call imgerror if cannot. static void readis(ImageSource* is, uchar* buf, int n) { if(is->i + n < is->bs->dalloclen) { memmove(buf, is->bs->data+is->i, n); is->i += n; } else imgerror(is, ERReof); } // Caller needs n bytes. // Return index into is->bs->data where at least // the next n bytes can be found. // There might be a "premature eof" exception. static int getn(ImageSource* is, int n) { int i; i = is->i; if(i + n <= is->bs->dalloclen) is->i += n; else imgerror(is, ERReof); return i; } // draw's allocimage but with some defaults. // calls imgerror if out of memory. static Image* newimage(ImageSource* is, int w, int h) { Image* im; if(!imcacheneed(w*h)) imgerror(is, ERRnomem); im = allocimage(display, Rect(0, 0, w, h), CMAP8, 1, DWhite); if(im == nil) imgerror(is, ERRnomem); return im; } static MaskedImage* newmi(Image* im) { MaskedImage* mi; mi = (MaskedImage*)emallocz(sizeof(MaskedImage)); mi->im = im; return mi; } // Call this after origw and origh are set to set the width and height // to our desired (rescaled) answer dimensions. // If only one of the dimensions is specified, the other is scaled by // the same factor. static void setdims(ImageSource* is) { int sw; int sh; int dw; int dh; sw = is->origw; sh = is->origh; dw = is->width; dh = is->height; if(dw == 0 && dh == 0) { dw = sw; dh = sh; } else if(dw == 0 || dh == 0) { if(dw == 0) { dw = (int)((((double)sw)*((double)dh/(double)sh))); if(dw == 0) dw = 1; } else { dh = (int)((((double)sh)*((double)dw/(double)sw))); if(dh == 0) dh = 1; } } is->width = dw; is->height = dh; } // for debugging static void tracearray(int* a, int n, char* name) { int i; trace("%s:", name); for(i = 0; i < n; i++) { if((i%10) == 0) trace("\n%5d: ", i); trace("%6d", a[i]); } trace("\n"); } // XBitmaps static void getxbitmaphdr(ImageSource* is) { int fnd; fnd = getxbitmapdefine(is, &is->origw); if(fnd) fnd = getxbitmapdefine(is, &is->origh); if(!fnd) imgerror(is, ERRimgbad); fnd = getxbitmapdefine(is, nil); if(fnd) getxbitmapdefine(is, nil); get_to_char(is, '{'); } static MaskedImage* getxbitmapmim(ImageSource* is) { int bytesperline; uchar* pixels; int npixels; int pixi; int i; Image* im; int v; int kend; int k; int j; getxbitmaphdr(is); setdims(is); bytesperline = (is->origw + 7)/8; npixels = is->origw*is->origh; pixels = (uchar*)emalloc(npixels); pixi = 0; for(i = 0; i < is->origh; i++) { for(j = 0; j < bytesperline; j++) { v = get_hexbyte(is); kend = 7; if(j == bytesperline - 1) kend = (is->origw - 1)%8; for(k = 0; k <= kend; k++) { if(v&(1 << k)) pixels[pixi] = Black; else pixels[pixi] = White; pixi++; } } } if(is->width != is->origw || is->height != is->origh) pixels = resample(pixels, is->origw, is->origh, is->width, is->height); im = newimage(is, is->width, is->height); loadimage(im, im->r, pixels, npixels); return newmi(im); } // get a line, which should be of form // '//define fieldname val' // and return 1 if found. // If pval != nil, put val in *pval. static int getxbitmapdefine(ImageSource* is, int* pval) { int fnd; int n; int c; fnd = 0; n = 0; c = getc(is); if(c == '#') { get_to_char(is, ' '); get_to_char(is, ' '); c = getc(is); while(c >= '0' && c <= '9') { fnd = 1; n = n*10 + c - '0'; c = getc(is); } } else ungetc(is); get_to_char(is, '\n'); if(pval != nil) *pval = n; return fnd; } // read fd until get char cterm // (getc will call imgerror if eof hit first) static void get_to_char(ImageSource* is, int cterm) { while(1) { if(getc(is) == cterm) return; } } // read fd until get xDD, were DD are hex digits. // (getc will call imgerror if eof hit first) static int get_hexbyte(ImageSource* is) { int n1; int n2; get_to_char(is, 'x'); n1 = hexdig(getc(is)); n2 = hexdig(getc(is)); if(n1 < 0 || n2 < 0) imgerror(is, ERRimgbad); return (n1 << 4) + n2; } static int hexdig(int c) { if('0' <= c && c <= '9') c -= 48; else if('a' <= c && c <= 'f') c += 10 - 'a'; else if('A' <= c && c <= 'F') c += 10 - 'A'; else c = -1; return c; } // GIF // GIF flags enum { TRANSP = 1, INPUT = 2, DISPMASK = (7<<2), HASCMAP = 0x80, INTERLACED = 0x40 }; static void getgifhdr(ImageSource* is) { Gifstate* h; int i; char* vers; if(dbgimg) trace("img: getgifhdr\n"); h = (Gifstate*)emallocz(sizeof(Gifstate)); i = getn(is, 6); vers = (char*)(is->bs->data+i); if(strncmp(vers, "GIF87a", 6) && strncmp(vers, "GIF89a", 6)) imgerror(is, ERRimgbad); is->origw = getlew(is); is->origh = getlew(is); h->fields = getc(is); h->bgrnd = getc(is); h->aspect = getc(is); setdims(is); if(dbgimg) trace("img: getgifhdr has origw=%d, origh=%d, w=%d, h=%d, fields=16r%x, bgrnd=%d, aspect=%d\n", is->origw, is->origh, is->width, is->height, h->fields, h->bgrnd, h->aspect); h->tbl = (GifEntry*)emalloc(4096 * sizeof(GifEntry)); for(i = 0; i < 258; i++) { h->tbl[i].prefix = -1; h->tbl[i].exten = i; } if(h->fields&HASCMAP) h->globalcmap = gifreadcmap(is, (h->fields&7) + 1, &h->globalcmaplen); is->gstate = h; if(warn && h->aspect != 0) trace("warning: non-standard aspect ratio in GIF image ignored\n"); if(!gifgettoimage(is)) imgerror(is, ERRimgbad); } static int gifgettoimage(ImageSource* is) { Gifstate* h; int nbytes; int i; int hsize; int hasdata; int c; if(dbgimg) trace("img: gifgettoimage\n"); h = is->gstate; while(1) { // some GIFs omit Trailer if(is->i >= is->bs->dalloclen) break; switch(c = getc(is)) { case 0x2C: // Image Descriptor return 1; break; case 0x21: // Extension hsize = 0; hasdata = 0; switch(getc(is)) { case 0x01: // Plain Text Extension hsize = 14; hasdata = 1; if(dbgimg) trace("gifgettoimage: text extension\n"); break; case 0xF9: // Graphic Control Extension getc(is); // blocksize (should be 4) h->flags = getc(is); h->delay = getlew(is); h->trindex = getc(is); getc(is); // block terminator (should be 0) if(dbgimg) trace("gifgettoimage: graphic control flags=16r%x, delay=%d, trindex=%d\n", h->flags, h->delay, h->trindex); break; case 0xFE: // Comment Extension if(dbgimg) trace("gifgettoimage: comment extension\n"); hasdata = 1; break; case 0xFF: // Application Extension if(dbgimg) trace("gifgettoimage: application extension\n"); hsize = getc(is); // standard says this must be 11, but Adobe likes to put out 10-byte ones, // so we pay attention to the field. hasdata = 1; break; default: imgerror(is, ERRimgbad); break; } if(hsize > 0) getn(is, hsize); if(hasdata) { while(1) { if((nbytes = getc(is)) == 0) break; i = getn(is, nbytes); if(dbgimg) trace("extension data: '%S'\n", toStr(is->bs->data+i, nbytes, UTF_8)); } } break; case 0x3B: // Trailer goto loop_done; break; default: if(c == 0) continue; // Fix for some buggy gifs imgerror(is, ERRimgbad); break; } } loop_done: return 0; } static MaskedImage* getgifmim(ImageSource* is) { Gifstate* h; int left; int top; int width; int height; int totpix; int c; int incode; int codesize; int CTM; int EOD; uchar* pic; int pici; uchar* data; int datai; int dataend; int nbits; int sreg; uchar* stack; int stacki; int fc; GifEntry* tbl; Image* mask; int bgcolor; int i; Image* im; MaskedImage* mi; int dispmeth; int nbytes; int code; int nb; int early; int csize; int csmask; int nentry; int maxentry; int first; int ocode; int start; int by; uchar* ipic; int ipici; int ipiclim; int w2; int w4; int w8; int k; double wscale; double hscale; int owidth; int oheight; uchar v; int x; int bytesperrow; uchar trpix; uchar* mpic; int mpici; int y; Image* newmask; Rectangle r; int col; Image* newim; int ilstart[4]; int ilby[4]; if(is->gstate == nil) getgifhdr(is); // At this point, should just have read Image Descriptor marker byte h = is->gstate; left = getlew(is); top = getlew(is); width = getlew(is); height = getlew(is); h->fields = getc(is); totpix = width*height; h->cmap = h->globalcmap; h->cmaplen = h->globalcmaplen; if(dbgimg) trace("getgifmim, left=%d, top=%d, width=%d, height=%d, fields=16r%x\n", left, top, width, height, h->fields); if(dbgev) logtime("IMAGE_GETGIFMIM", 0); if(h->fields&HASCMAP) h->cmap = gifreadcmap(is, (h->fields&7) + 1, &h->cmaplen); if(h->cmap == nil) imgerror(is, ERRimgbad); // now decode the image codesize = getc(is); if(codesize > 8) imgerror(is, ERRimgbad); if(h->cmaplen != 3*(1 << codesize) && (codesize != 2 || h->cmaplen != 3*2)) imgerror(is, ERRimgbad); CTM = 1 << codesize; EOD = CTM + 1; pic = (uchar*)emalloc(totpix); pici = 0; data = is->bs->data; datai = 0; dataend = 0; nbits = 0; sreg = 0; stack = (uchar*)emalloc(4096); fc = 0; tbl = h->tbl; while(1) { Decode_continue: csize = codesize + 1; csmask = ((1 << csize) - 1); nentry = EOD + 1; maxentry = csmask; first = 1; for(ocode = -1; ; ocode = incode) { while(nbits < csize) { if(datai == dataend) { nbytes = getc(is); if(nbytes == 0) goto Decode_done; datai = getn(is, nbytes); dataend = datai + nbytes; } c = data[datai++]; sreg |= c << nbits; nbits += 8; } code = sreg&csmask; sreg >>= csize; nbits -= csize; if(code == EOD) { nbytes = getc(is); if(nbytes != 0 && warn) trace("warning: unexpected data past EOD\n"); goto Decode_done; } if(code == CTM) goto Decode_continue; stacki = 4096 - 1; incode = code; // special case for KwKwK if(code == nentry) { stack[stacki--] = fc; code = ocode; } if(code > nentry) imgerror(is, ERRimgbad); for(c = code; c >= 0; c = tbl[c].prefix) stack[stacki--] = tbl[c].exten; nb = 4096 - (stacki + 1); if(pici + nb > totpix) { // this common error is harmless // we have to keep reading to keep the blocks in sync ; } else { memmove(pic+pici, stack+stacki+1, nb); pici += nb; } fc = stack[stacki + 1]; if(first) { first = 0; continue; } early = 0; // peculiar tiff feature here for reference if(nentry == maxentry - early) { if(csize >= 12) continue; csize++; maxentry = (1 << csize); csmask = maxentry - 1; if(csize < 12) maxentry--; } tbl[nentry].prefix = ocode; tbl[nentry].exten = fc; nentry++; } } Decode_done: while(pici < totpix) { // shouldn't happen, but sometimes get buggy gifs pic[pici++] = 0; } if(h->fields&INTERLACED) { if(dbgimg) trace("getgifmim uninterlacing\n"); if(dbgev) logtime("IMAGE_GETGIFMIM_INTERLACE_START", 0); // (TODO: Could un-interlace in place. // Decompose permutation into cycles, // then need one double-copy of a line // per cycle). ipic = (uchar*)emalloc(totpix); pici = 0; ipiclim = totpix - width; w2 = width + width; w4 = w2 + w2; w8 = w4 + w4; ilstart[0] = 0; ilstart[1] = w4; ilstart[2] = w2; ilstart[3] = width; ilby[0] = w8; ilby[1] = w8; ilby[2] = w4; ilby[3] = w2; for(k = 0; k < 4; k++) { start = ilstart[k]; by = ilby[k]; for(ipici = start; ipici <= ipiclim; ipici += by) { memmove(ipic+ipici, pic+pici, width); pici += width; } } pic = ipic; if(dbgev) logtime("IMAGE_GETGIFMIM_INTERLACE_END", 0); } if(is->width != is->origw || is->height != is->origh) { // need to resample, using same factors as original image wscale = (double)is->width/(double)is->origw; hscale = (double)is->height/(double)is->origh; owidth = width; oheight = height; width = (int)((wscale*(double)width)+.5); if(width == 0) width = 1; height = (int)((hscale*(double)height)+.5); if(height == 0) height = 1; left = (int)((wscale*(double)left)+.5); top = (int)((hscale*(double)top)+.5); pic = resample(pic, owidth, oheight, width, height); } mask = nil; if(h->flags&TRANSP) { if(dbgimg) trace("getgifmim making mask, trindex=%d\n", h->trindex); if(dbgev) logtime("IMAGE_GETGIFMIM_MASK_START", 0); // make a 1-bit deep bitmap for mask // expect most mask bits will be 1 bytesperrow = (width + 7)/8; trpix = h->trindex; nb = bytesperrow*height; mpic = (uchar*)emalloc(nb); mpici = 0; pici = 0; for(y = 0; y < height; y++) { v = 0xFF; k = 0; for(x = 0; x < width; x++) { if(pic[pici++] == trpix) v &= ~(0x80 >> k); if(++k == 8) { k = 0; mpic[mpici++] = v; v = 0xFF; } } if(k != 0) mpic[mpici++] = v; } if(!imcacheneed(nb)) imgerror(is, ERRnomem); mask = allocimage(display, Rect(0, 0, width, height), GREY1, 0, DOpaque); if(mask == nil) imgerror(is, ERRnomem); loadimage(mask, mask->r, mpic, nb); if(dbgev) logtime("IMAGE_GETGIFMIM_MASK_END", 0); } if(dbgev) logtime("IMAGE_GETGIFMIM_REMAP_START", 0); remap1(pic, width, height, h->cmap, h->cmaplen); if(dbgev) logtime("IMAGE_GETGIFMIM_REMAP_END", 0); bgcolor = -1; i = h->bgrnd; if(i >= 0 && 3*i + 2 < h->cmaplen) { bgcolor = ((h->cmap[3*i]) << 16) | ((h->cmap[3*i + 1]) << 8) | (h->cmap[3*i + 2]); } im = newimage(is, width, height); loadimage(im, im->r, pic, totpix); if(is->curframe == 0) { // make sure first frame fills up whole rectangle if(is->width != width || is->height != height || left != 0 || top != 0) { r = Rect(left, top, left + width, top + height); col = DWhite; if(bgcolor != -1) col = (bgcolor<<8)|0xFF; newim = allocimage(display, Rect(0, 0, is->width, is->height), im->chan, 0, col); if(newim == nil) imgerror(is, ERRnomem); draw(newim, r, im, mask, ZP); im = newim; if(mask != nil) { newmask = allocimage(display, Rect(0, 0, is->width, is->height), GREY1, 0, DTransparent); if(newmask == nil) imgerror(is, ERRnomem); draw(newmask, r, mask, nil, ZP); mask = newmask; } left = 0; top = 0; } } mi = newmi(im); mi->mask = mask; mi->delay = h->delay*10; // convert centiseconds to milliseconds mi->origin = Pt(left, top); dispmeth = (h->flags >> 2)&7; if(dispmeth == 2) { // reset to background color after displaying this frame mi->bgcolor = bgcolor; } else if(dispmeth == 3) { // Supposed to "reset to previous", which appears to // mean the previous frame that didn't have a "reset to previous". // Signal this special case to layout by setting bgcolor to -2. mi->bgcolor = -2; } if(gifgettoimage(is)) { mi->more = 1; is->curframe++; // have to reinitialize table for next time for(i = 0; i < 258; i++) { h->tbl[i].prefix = -1; h->tbl[i].exten = i; } } if(dbgev) logtime("IMAGE_GETGIFMIM_END", 0); return mi; } // Read a GIF colormap, where bpe is number of bits in an entry. // Causes a 'premature eof' imgerror if can't get the whole map. static uchar* gifreadcmap(ImageSource* is, int bpe, int* pmaplen) { int size; uchar* map; size = 3*(1 << bpe); map = (uchar*)emalloc(size); if(dbgimg > 1) trace("gifreadcmap wants %d bytes\n", size); readis(is, map, size); *pmaplen = size; return map; } // JPG // Constants enum { // All of these are preceded by 0xFF byte SOF = 0xC0, // Start of Frame SOF2 = 0xC2, // Start of Frame; progressive Huffman JPG = 0xC8, // Reserved for JPEG extensions DHT = 0xC4, // Define Huffman Tables DAC = 0xCC, // Arithmetic coding conditioning RST = 0xD0, // Restart interval termination RST7 = 0xD7, // Restart interval termination (highest value) SOI = 0xD8, // Start of Image EOI = 0xD9, // End of Image SOS = 0xDA, // Start of Scan DQT = 0xDB, // Define quantization tables DNL = 0xDC, // Define number of lines DRI = 0xDD, // Define restart interval DHP = 0xDE, // Define hierarchical progression EXP = 0xDF, // Expand reference components APPn = 0xE0, // Reserved for application segments JPGn = 0xF0, // Reserved for JPEG extensions COM = 0xFE, // Comment NBUF = 16*1024, // channel descriptions CRGB = 0, // three channels, R, G, B, no map CY = 1, // one channel, luminance CRGB1 = 2, // one channel, map present CYCbCr = 3, // three channels, Y, Cb, Cr, no map jpegcolorspace = CYCbCr, // default space to convert jpeg to // Some floats converted to fixed point (scaling by 2048) jc1 = 2871, // 1.402 * 2048 jc2 = 705, // 0.34414 * 2048 jc3 = 1463, // 0.71414 * 2048 jc4 = 3629, // 1.772 * 2048 W1 = 2841, // 2048*sqrt(2)*cos(1*pi/16) W2 = 2676, // 2048*sqrt(2)*cos(2*pi/16) W3 = 2408, // 2048*sqrt(2)*cos(3*pi/16) W5 = 1609, // 2048*sqrt(2)*cos(5*pi/16) W6 = 1108, // 2048*sqrt(2)*cos(6*pi/16) W7 = 565, // 2048*sqrt(2)*cos(7*pi/16) W1pW7 = 3406, // W1+W7 W1mW7 = 2276, // W1-W7 W3pW5 = 4017, // W3+W5 W3mW5 = 799, // W3-W5 W2pW6 = 3784, // W2+W6 W2mW6 = 1567, // W2-W6 R2 = 181, // 256/sqrt(2) }; static void getjpeghdr(ImageSource* is) { Jpegstate* h; int m; int n; int i; int H; int V; if(dbgimg) trace("getjpeghdr\n"); h = (Jpegstate*)emallocz(sizeof(Jpegstate)); is->jstate = h; if(jpegmarker(is) != SOI) imgerror(is, ERRimgbad); jpegtabmisc(is, &m, &n); if(!(m == SOF || m == SOF2)) imgerror(is, ERRimgbad); getc(is); // sample precision h->Y = getbew(is); h->X = getbew(is); h->Nf = getc(is); if(dbgimg) trace("start of frame, Y=%d, X=%d, Nf=%d\n", h->Y, h->X, h->Nf); h->comp = (Framecomp*)emalloc(h->Nf * sizeof(Framecomp)); h->nblock = (int*)emalloc(h->Nf * sizeof(int)); for(i = 0; i < h->Nf; i++) { h->comp[i].C = getc(is); nibbles(getc(is), &H, &V); h->comp[i].H = H; h->comp[i].V = V; h->comp[i].Tq = getc(is); h->nblock[i] = H*V; if(dbgimg) trace("comp[%d]: C=%d, H=%d, V=%d, Tq=%d\n", i, h->comp[i].C, H, V, h->comp[i].Tq); } h->mode = m; is->origw = h->X; is->origh = h->Y; setdims(is); if(n != 6 + 3*h->Nf) imgerror(is, ERRimgbad); } static int jpegmarker(ImageSource* is) { if(getc(is) != 0xFF) imgerror(is, ERRimgbad); return getc(is); } // Consume tables and miscellaneous marker segments, // returning the marker id and length of the first non-such-segment // (after having consumed the marker) in *pm and *pn. static void jpegtabmisc(ImageSource* is, int *pm, int *pn) { Jpegstate* h; int m; int n; int vers0; int vers1; uchar* buf; int i; h = is->jstate; while(1) { h->nseg++; m = jpegmarker(is); n = 0; if(m != EOI) n = getbew(is) - 2; if(dbgimg > 1) trace("jpegtabmisc reading segment, got m=%x, n=%d\n", m, n); switch(m) { case SOF: case SOF2: case SOS: case EOI: goto Loop_done; break; case APPn+0: if(h->nseg == 1 && n >= 6) { i = getn(is, 6); buf = is->bs->data; n -= 6; if(!strncmp((char*)(buf+i), "JFIF", 4)) { vers0 = buf[i + 5]; vers1 = buf[i + 6]; if(vers0 > 1 || vers1 > 2) imgerror(is, ERRimgbad); // unimplemented version } } break; case DQT: jpegquanttables(is, n); n = 0; break; case DHT: jpeghuffmantables(is, n); n = 0; break; case DRI: h->ri = getbew(is); n -= 2; break; case COM: break; default: if(!(m >= APPn+1 && m <= APPn+15)) imgerror(is, ERRimgbad); // unexpected marker break; } if(n > 0) getn(is, n); } Loop_done: *pm = m; *pn = n; } // Consume huffman tables static void jpeghuffmantables(ImageSource* is, int n) { Jpegstate* h; int l; if(dbgimg) trace("jpeghuffmantables\n"); h = is->jstate; if(h->dcht == nil) { h->dcht = (Huffman**)emallocz(4 * sizeof(Huffman*)); h->acht = (Huffman**)emallocz(4 * sizeof(Huffman*)); } l = 0; while(l < n) l += jpeghuffmantable(is); if(l != n) imgerror(is, ERRimgbad); // huffman table bad length } static int jpeghuffmantable(ImageSource* is) { Huffman* t; Jpegstate* h; int Tc; int th; uchar* b; int bi; int nsize; int i; int k; int code; int si; int j; int* maxcode; int v; int n; int cnt; int m; int sr; int numcodes[16]; t = (Huffman*)emalloc(sizeof(Huffman)); h = is->jstate; nibbles(getc(is), &Tc, &th); if(dbgimg > 1) trace("jpeghuffmantable, Tc=%d, th=%d\n", Tc, th); if(Tc > 1) imgerror(is, ERRimgbad); // unknown Huffman table class if(th > 3 || (h->mode == (uchar)SOF && th > 1)) imgerror(is, ERRimgbad); // unknown Huffman table index if(Tc == 0) h->dcht[th] = t; else h->acht[th] = t; // flow chart C-2 bi = getn(is, 16); b = is->bs->data; nsize = 0; for(i = 0; i < 16; i++) nsize += (numcodes[i] = b[bi + i]); t->size = (int*)emalloc((nsize + 1) * sizeof(int)); k = 0; for(i = 1; i <= 16; i++) { n = numcodes[i - 1]; for(j = 0; j < n; j++) t->size[k++] = i; } t->size[k] = 0; // initialize HUFFVAL t->val = (int*)emalloc(nsize * sizeof(int)); bi = getn(is, nsize); for(i = 0; i < nsize; i++) t->val[i] = b[bi++]; // flow chart C-3 t->code = (int*)emalloc((nsize + 1) * sizeof(int)); k = 0; code = 0; si = t->size[0]; while(1) { do t->code[k++] = code++; while(t->size[k] == si); if(t->size[k] == 0) break; do { code <<= 1; si++; } while(t->size[k] != si); } // flow chart F-25 i = 0; j = 0; while(1) { while(1) { i++; if(i > 16) goto F25_done; if(numcodes[i - 1] != 0) break; t->maxcode[i] = -1; } t->valptr[i] = j; t->mincode[i] = t->code[j]; j += numcodes[i - 1] - 1; t->maxcode[i] = t->code[j]; j++; } F25_done: // stupid startup algorithm: just run machine for each byte value maxcode = t->maxcode; for(v = 0; v < 256; v++) { cnt = 7; m = 1 << 7; code = 0; sr = v; i = 1; for(; ; i++) { if(sr&m) code |= 1; if(code <= maxcode[i]) break; code <<= 1; m >>= 1; if(m == 0) { t->shift[v] = 0; t->value[v] = -1; goto Bytes_continue; } cnt--; } t->shift[v] = 8 - cnt; t->value[v] = t->val[t->valptr[i] + (code - t->mincode[i])]; Bytes_continue: ; } if(dbgimg > 2) { trace("Huffman table %d:\n", th); tracearray(t->size, nsize+1, "size"); tracearray(t->code, nsize+1, "code"); tracearray(t->val, nsize, "val"); tracearray(t->mincode, 17, "mincode"); tracearray(t->maxcode, 17, "maxcode"); tracearray(t->value, 256, "value"); tracearray(t->shift, 256, "shift"); } return nsize + 17; } static void jpegquanttables(ImageSource* is, int n) { Jpegstate* h; int l; if(dbgimg) trace("jpegquanttables\n"); h = is->jstate; if(h->qt == nil) h->qt = (int**)emalloc(4 * sizeof(int*)); l = 0; while(l < n) l += jpegquanttable(is); if(l != n) imgerror(is, ERRimgbad); } static int jpegquanttable(ImageSource* is) { int pq; int tq; int* q; int i; nibbles(getc(is), &pq, &tq); if(dbgimg) trace("jpegquanttable pq=%d tq=%d\n", pq, tq); if(pq > 1) imgerror(is, ERRimgbad); // unknown quantization table class if(tq > 3) imgerror(is, ERRimgbad); // bad quantization table index q = (int*)emalloc(64 * sizeof(int)); is->jstate->qt[tq] = q; for(i = 0; i < 64; i++) { if(pq == 0) q[i] = getc(is); else q[i] = getbew(is); } if(dbgimg > 2) tracearray(q, 64, "quant table"); return 1 + (64*(1 + pq)); } // Have just read Frame header. // Now expect: // ((tabl/misc segment(s))* (scan header) (entropy coded segment)+)+ EOI static MaskedImage* getjpegmim(ImageSource* is) { Jpegstate* h; uchar** chans; int width; int height; Image* im; int m; int n; Scancomp* scomp; int i; int k; int nchans; if(dbgimg) trace("getjpegmim\n"); if(dbgev) logtime("IMAGE_GETJPGMIM", is->width*is->height); getjpeghdr(is); h = is->jstate; chans = nil; while(1) { jpegtabmisc(is, &m, &n); if(m == EOI) break; if(m != SOS) imgerror(is, ERRimgbad); // expected start of scan h->Ns = getc(is); if(dbgimg) trace("start of scan, Ns=%d\n", h->Ns); scomp = (Scancomp*)emalloc(h->Ns * sizeof(Scancomp)); for(i = 0; i < h->Ns; i++) { scomp[i].C = getc(is); nibbles(getc(is), &scomp[i].tdc, &scomp[i].tac); } h->scomp = scomp; h->Ss = getc(is); h->Se = getc(is); nibbles(getc(is), &h->Ah, &h->Al); if(n != 4 + h->Ns*2) imgerror(is, ERRimgbad); // SOS header wrong length if(h->mode == SOF) { if(chans != nil) imgerror(is, ERRimgbad); // baseline has > 1 scan chans = jpegbaselinescan(is, &nchans); } else jpegprogressivescan(is); } if(h->mode == SOF2) chans = jprogressiveIDCT(is, &nchans); if(chans == nil) imgerror(is, ERRimgbad); // jpeg has no image width = is->width; height = is->height; if(width != h->X || height != h->Y) { for(k = 0; k < nchans; k++) chans[k] = resample(chans[k], h->X, h->Y, width, height); } if(dbgev) logtime("IMAGE_JPG_REMAP", 0); if(nchans == 1) remapgrey(chans[0], width, height); else remaprgb(chans, width, height); if(dbgev) logtime("IMAGE_JPG_REMAP_END", 0); im = newimage(is, width, height); loadimage(im, im->r, chans[0], width*height); if(dbgev) logtime("IMAGE_GETJPGMIM_END", 0); return newmi(im); } static int zig[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, // 0-7 24, 32, 25, 18, 11, 4, 5, //# 8-15 12, 19, 26, 33, 40, 48, 41, 34, // 16-23 27, 20, 13, 6, 7, 14, 21, 28, // 24-31 35, 42, 49, 56, 57, 50, 43, 36, // 32-39 29, 22, 15, 23, 30, 37, 44, 51, // 40-47 58, 59, 52, 45, 38, 31, 39, 46, // 48-55 53, 60, 61, 54, 47, 55, 62, 63 // 56-63 }; static uchar** jpegbaselinescan(ImageSource* is, int *pnchans) { Jpegstate* h; int Ns; uchar** chans; int k; int* Td; int* Ta; int*** data; int* H; int* V; int* DC; int Hmax; int Vmax; int comp; int allHV1; int ri; int nacross; int nmcu; int mcu; int nblock; int m; int z; int rs; int rrrr; int ssss; int t; int diff; int* zz; Huffman* dcht; Huffman* acht; int* qt; int block; if(dbgimg) trace("jpegbaselinescan\n"); if(dbgev) logtime("IMAGE_JPGBASELINESCAN", 0); h = is->jstate; Ns = h->Ns; if(Ns != h->Nf) imgerror(is, ERRimgbad); // baseline needs Ns==Nf if(!(Ns == 3 || Ns == 1)) imgerror(is, ERRimgbad); // baseline needs Ns==1 or 3 chans = (uchar**)emalloc(h->Nf * sizeof(uchar*)); *pnchans = h->Nf; for(k = 0; k < h->Nf; k++) chans[k] = (uchar*)emalloc(h->X*h->Y); // build per-component arrays Td = (int*)emalloc(Ns * sizeof(int)); Ta = (int*)emalloc(Ns * sizeof(int)); data = (int***)emalloc(Ns * sizeof(int**)); H = (int*)emalloc(Ns * sizeof(int)); V = (int*)emalloc(Ns * sizeof(int)); DC = (int*)emalloc(Ns * sizeof(int)); // compute maximum H and V Hmax = 0; Vmax = 0; for(comp = 0; comp < Ns; comp++) { if(h->comp[comp].H > Hmax) Hmax = h->comp[comp].H; if(h->comp[comp].V > Vmax) Vmax = h->comp[comp].V; } if(dbgimg > 1) trace("Hmax=%d, Vmax=%d\n", Hmax, Vmax); // initialize data structures allHV1 = 1; for(comp = 0; comp < Ns; comp++) { // JPEG requires scan components to be in same order as in frame, // so if both have 3 we know scan is Y Cb Cr and there's no need to // reorder Td[comp] = h->scomp[comp].tdc; Ta[comp] = h->scomp[comp].tac; H[comp] = h->comp[comp].H; V[comp] = h->comp[comp].V; nblock = H[comp]*V[comp]; if(nblock != 1) allHV1 = 0; data[comp] = (int**)emalloc(nblock * sizeof(int*)); DC[comp] = 0; for(m = 0; m < nblock; m++) data[comp][m] = (int*)emalloc(8*8 * sizeof(int)); if(dbgimg > 2) trace("scan comp %d: H=%d, V=%d, nblock=%d, Td=%d, Ta=%d\n", comp, H[comp], V[comp], nblock, Td[comp], Ta[comp]); } ri = h->ri; h->cnt = 0; h->sr = 0; nacross = ((h->X + (8*Hmax - 1))/(8*Hmax)); nmcu = ((h->Y + (8*Vmax - 1))/(8*Vmax))*nacross; if(dbgimg) trace("nacross=%d, nmcu=%d\n", nacross, nmcu); mcu = 0; while(mcu < nmcu) { if(dbgimg > 2) trace("mcu %d\n", mcu); for(comp = 0; comp < Ns; comp++) { if(dbgimg > 2) trace("comp %d\n", comp); dcht = h->dcht[Td[comp]]; acht = h->acht[Ta[comp]]; qt = h->qt[h->comp[comp].Tq]; for(block = 0; block < H[comp]*V[comp]; block++) { if(dbgimg > 2) trace("block %d\n", block); // F-22 t = jdecode(is, dcht); diff = jreceive(is, t); DC[comp] += diff; if(dbgimg > 2) trace("t=%d, diff=%d, DC=%d\n", t, diff, DC[comp]); // F=23 zz = data[comp][block]; memset(zz, 0, 64*sizeof(int)); zz[0] = qt[0]*DC[comp]; k = 1; while(1) { rs = jdecode(is, acht); nibbles(rs, &rrrr, &ssss); if(ssss == 0) { if(rrrr != 15) break; k += 16; } else { k += rrrr; z = jreceive(is, ssss); zz[zig[k]] = z*qt[k]; if(k == 63) break; k++; } } idct(zz); } } // rotate colors to RGB and assign to bytes if(Ns == 1) colormap1(h, chans[0], data[0][0], mcu, nacross); else if(allHV1) colormapall1(h, chans, data[0][0], data[1][0], data[2][0], mcu, nacross); else colormap(h, chans, data[0], data[1], data[2], mcu, nacross, Hmax, Vmax, H, V); // process restart marker, if present mcu++; if(ri > 0 && mcu < nmcu && mcu%ri == 0) { jrestart(is, mcu); for(comp = 0; comp < Ns; comp++) DC[comp] = 0; } } if(dbgev) logtime("IMAGE_JPGBASELINESCAN_END", 0); return chans; } static void jrestart(ImageSource* is, int mcu) { Jpegstate* h; int ri; int restart; int rst; int nskip; h = is->jstate; ri = h->ri; restart = mcu/ri - 1; nskip = 0; do { do { rst = jnextborm(is); nskip++; } while(rst >= 0 && rst != 0xFF); if(rst == 0xFF) { rst = jnextborm(is); nskip++; } } while(rst >= 0 && (rst&~7) != RST); if(nskip != 2 || rst < 0 || ((rst&7) != (restart&7))) imgerror(is, ERRimgbad); // restart problem h->cnt = 0; h->sr = 0; } static void jpegprogressivescan(ImageSource* is) { Jpegstate* h; int c; int comp; int i; if(dbgev) logtime("IMAGE_JPGPROGSCAN", 0); h = is->jstate; if(h->dccoeff == nil) jprogressiveinit(is, h); c = h->scomp[0].C; comp = -1; for(i = 0; i < h->Nf; i++) if(h->comp[i].C == c) comp = i; if(comp == -1) imgerror(is, ERRimgbad); // bad component index in scan header if(h->Ss == 0) jprogressivedc(is, comp); else if(h->Ah == 0) jprogressiveac(is, comp); else jprogressiveacinc(is, comp); if(dbgev) logtime("IMAGE_JPGPROGSCAN_END", 0); } static uchar** jprogressiveIDCT(ImageSource* is, int* pnchans) { Jpegstate* h; int Nf; int* H; int* V; int allHV1; int*** data; int comp; uchar** chans; int k; int* blockno; int nmcu; int mcu; int nblock; int m; int* zz; int* dccoeff; int** accoeff; int bn; int block; if(dbgev) logtime("IMAGE_JPGPROGIDCT", 0); h = is->jstate; Nf = h->Nf; H = (int*)emalloc(Nf * sizeof(int)); V = (int*)emalloc(Nf * sizeof(int)); allHV1 = 1; data = (int***)emalloc(Nf * sizeof(int**)); for(comp = 0; comp < Nf; comp++) { H[comp] = h->comp[comp].H; V[comp] = h->comp[comp].V; nblock = h->nblock[comp]; if(nblock != 1) allHV1 = 0; data[comp] = (int**)emalloc(nblock * sizeof(int*)); for(m = 0; m < nblock; m++) data[comp][m] = (int*)emalloc(8*8 * sizeof(int)); } chans = (uchar**)emalloc(h->Nf*sizeof(uchar*)); *pnchans = h->Nf; for(k = 0; k < h->Nf; k++) chans[k] = (uchar*)emalloc(h->X*h->Y); blockno = (int*)emallocz(Nf * sizeof(int)); nmcu = h->nacross*h->ndown; for(mcu = 0; mcu < nmcu; mcu++) { for(comp = 0; comp < Nf; comp++) { dccoeff = h->dccoeff[comp]; accoeff = h->accoeff[comp]; bn = blockno[comp]; for(block = 0; block < h->nblock[comp]; block++) { zz = data[comp][block]; memset(zz, 0, 8*8 * sizeof(int)); zz[0] = dccoeff[bn]; for(k = 1; k < 64; k++) zz[zig[k]] = accoeff[bn][k]; idct(zz); bn++; } blockno[comp] = bn; } // rotate colors to RGB and assign to bytes if(Nf == 1) colormap1(h, chans[0], data[0][0], mcu, h->nacross); else if(allHV1) colormapall1(h, chans, data[0][0], data[1][0], data[2][0], mcu, h->nacross); else colormap(h, chans, data[0], data[1], data[2], mcu, h->nacross, h->Hmax, h->Vmax, H, V); } return chans; } static void jprogressiveinit(ImageSource* is, Jpegstate* h) { int Ns; int Nf; int comp; int nmcu; int k; int j; Ns = h->Ns; Nf = h->Nf; if((Ns != 3 && Ns != 1) || Ns != Nf) imgerror(is, ERRimgbad); h->Hmax = 0; h->Vmax = 0; for(comp = 0; comp < Nf; comp++) { if(h->comp[comp].H > h->Hmax) h->Hmax = h->comp[comp].H; if(h->comp[comp].V > h->Vmax) h->Vmax = h->comp[comp].V; } h->nacross = ((h->X + (8*h->Hmax - 1))/(8*h->Hmax)); h->ndown = ((h->Y + (8*h->Vmax - 1))/(8*h->Vmax)); nmcu = h->nacross*h->ndown; h->dccoeff = (int**)emalloc(Nf * sizeof(int*)); h->accoeff = (int***)emalloc(Nf * sizeof(int**)); for(k = 0; k < Nf; k++) { h->dccoeff[k] = (int*)emallocz(h->nblock[k]*nmcu * sizeof(int)); h->accoeff[k] = (int**)emalloc(h->nblock[k]*nmcu * sizeof(int*)); for(j = 0; j < h->nblock[k]*nmcu; j++) h->accoeff[k][j] = (int*)emallocz(64 * sizeof(int)); } } static void jprogressivedc(ImageSource* is, int comp) { Jpegstate* h; int Ns; int Ah; int Al; int* Td; int* DC; int ri; int nmcu; int* blockno; int mcu; int t; int diff; Huffman* dcht; int qt; int* dc; int bn; int block; h = is->jstate; Ns = h->Ns; Ah = h->Ah; Al = h->Al; if(Ns != h->Nf) imgerror(is, ERRimgbad); // progressive with Nf!=Ns in DC scan // build per-component arrays Td = (int*)emalloc(Ns * sizeof(int)); DC = (int*)emalloc(Ns * sizeof(int)); h->cnt = 0; h->sr = 0; for(comp = 0; comp < Ns; comp++) { // JPEG requires scan components to be in same order as in frame, // so if both have 3 we know scan is Y Cb Cr and there's no need to // reorder Td[comp] = h->scomp[comp].tdc; DC[comp] = 0; } ri = h->ri; nmcu = h->nacross*h->ndown; blockno = (int*)emallocz(Ns * sizeof(int)); mcu = 0; while(mcu < nmcu) { for(comp = 0; comp < Ns; comp++) { dcht = h->dcht[Td[comp]]; qt = h->qt[h->comp[comp].Tq][0]; dc = h->dccoeff[comp]; bn = blockno[comp]; for(block = 0; block < h->nblock[comp]; block++) { if(Ah == 0) { t = jdecode(is, dcht); diff = jreceive(is, t); DC[comp] += diff; dc[bn] = qt*DC[comp] << Al; } else dc[bn] |= qt*jreceivebit(is) << Al; bn++; } blockno[comp] = bn; } // process restart marker, if present mcu++; if(ri > 0 && mcu < nmcu && mcu%ri == 0) { jrestart(is, mcu); for(comp = 0; comp < Ns; comp++) DC[comp] = 0; } } } static void jprogressiveac(ImageSource* is, int comp) { Jpegstate* h; int Ns; int Al; int Ss; int Se; int H; int V; int nacross; int ndown; int q; int nhor; int nver; int Ta; int ri; int eobrun; Huffman* acht; int* qt; int nmcu; int mcu; int y; int z; int rs; int rrrr; int ssss; int tmcu; int blockno; int* acc; int k; int x; h = is->jstate; Ns = h->Ns; Al = h->Al; if(Ns != 1) imgerror(is, ERRimgbad); // illegal Ns>1 in progressive AC scan Ss = h->Ss; Se = h->Se; H = h->comp[comp].H; V = h->comp[comp].V; nacross = h->nacross*H; ndown = h->ndown*V; q = 8*h->Hmax/H; nhor = (h->X + q - 1)/q; q = 8*h->Vmax/V; nver = (h->Y + q - 1)/q; // initialize data structures h->cnt = 0; h->sr = 0; Ta = h->scomp[0].tac; ri = h->ri; eobrun = 0; acht = h->acht[Ta]; qt = h->qt[h->comp[comp].Tq]; nmcu = nacross*ndown; mcu = 0; for(y = 0; y < nver; y++) { for(x = 0; x < nhor; x++) { // Figure G-3 if(eobrun > 0) { --eobrun; continue; } // arrange blockno to be in same sequence as // original scan calculation. tmcu = x/H + (nacross/H)*(y/V); blockno = tmcu*H*V + H*(y%V) + x%H; acc = h->accoeff[comp][blockno]; k = Ss; while(1) { rs = jdecode(is, acht); nibbles(rs, &rrrr, &ssss); if(ssss == 0) { if(rrrr < 15) { eobrun = 0; if(rrrr > 0) eobrun = jreceiveEOB(is, rrrr) - 1; break; } k += 16; } else { k += rrrr; z = jreceive(is, ssss); acc[k] = z*qt[k] << Al; if(k == Se) break; k++; } } } // process restart marker, if present mcu++; if(ri > 0 && mcu < nmcu && mcu%ri == 0) { jrestart(is, mcu); eobrun = 0; } } } static void jprogressiveacinc(ImageSource* is, int comp) { Jpegstate* h; int Ns; int Ss; int Se; int H; int V; int Al; int nacross; int ndown; int q; int nhor; int nver; int Ta; int ri; int eobrun; int** ac; Huffman* acht; int* qt; int nmcu; int mcu; int pending; int nzeros; int y; int k; int i; int rs; int rrrr; int ssss; int tmcu; int blockno; int* acc; int x; h = is->jstate; Ns = h->Ns; if(Ns != 1) imgerror(is, ERRimgbad); // illegal Ns>1 in progressive AC scan Ss = h->Ss; Se = h->Se; H = h->comp[comp].H; V = h->comp[comp].V; Al = h->Al; nacross = h->nacross*H; ndown = h->ndown*V; q = 8*h->Hmax/H; nhor = (h->X + q - 1)/q; q = 8*h->Vmax/V; nver = (h->Y + q - 1)/q; // initialize data structures h->cnt = 0; h->sr = 0; Ta = h->scomp[0].tac; ri = h->ri; eobrun = 0; ac = h->accoeff[comp]; acht = h->acht[Ta]; qt = h->qt[h->comp[comp].Tq]; nmcu = nacross*ndown; mcu = 0; pending = 0; nzeros = -1; for(y = 0; y < nver; y++) { for(x = 0; x < nhor; x++) { // Figure G-7 // arrange blockno to be in same sequence as // original scan calculation. tmcu = x/H + (nacross/H)*(y/V); blockno = tmcu*H*V + H*(y%V) + x%H; acc = ac[blockno]; if(eobrun > 0) { if(nzeros > 0) imgerror(is, ERRimgbad); // zeros pending at block start for(k = Ss; k <= Se; k++) jincrement(is, acc, k, qt[k] << Al); --eobrun; continue; } k = Ss; while(k <= Se) { if(nzeros >= 0) { if(acc[k] != 0) jincrement(is, acc, k, qt[k] << Al); else if(nzeros-- == 0) acc[k] = pending; k++; continue; } rs = jdecode(is, acht); nibbles(rs, &rrrr, &ssss); if(ssss == 0) { if(rrrr < 15) { eobrun = 0; if(rrrr > 0) eobrun = jreceiveEOB(is, rrrr) - 1; while(k <= Se) { jincrement(is, acc, k, qt[k] << Al); k++; } break; } for(i = 0; i < 16; k++) { jincrement(is, acc, k, qt[k] << Al); if(acc[k] == 0) i++; } continue; } else if(ssss != 1) imgerror(is, ERRimgbad); // Jpeg ssss!=1 in progressive increment nzeros = rrrr; pending = jreceivebit(is); if(pending == 0) pending = -1; pending *= qt[k] << Al; } } // process restart marker, if present mcu++; if(ri > 0 && mcu < nmcu && mcu%ri == 0) { jrestart(is, mcu); eobrun = 0; nzeros = -1; } } } static void jincrement(ImageSource* is, int* acc, int k, int Pt) { int b; if(acc[k] == 0) return ; b = jreceivebit(is); if(b != 0) if(acc[k] < 0) acc[k] -= Pt; else acc[k] += Pt; } // Fills in pixels (x,y) for x = minx=8*(mcu%nacross), minx+1, ..., minx+7 (or h.X-1, if less) // and for y = miny=8*(mcu/nacross), miny+1, ..., miny+7 (or h.Y-1, if less) static void colormap1(Jpegstate* h, uchar* pic, int* data, int mcu, int nacross) { int minx; int dx; int miny; int dy; int pici; int k; int y; int x; minx = 8*(mcu%nacross); dx = 8; if(minx + dx > h->X) dx = h->X - minx; miny = 8*(mcu/nacross); dy = 8; if(miny + dy > h->Y) dy = h->Y - miny; pici = miny*h->X + minx; k = 0; for(y = 0; y < dy; y++) { for(x = 0; x < dx; x++) pic[pici + x] = clampb[(data[k + x] + 128) + CLAMPBOFF]; pici += h->X; k += 8; } } // Fills in same pixels as colormap1 static void colormapall1(Jpegstate* h, uchar** chans, int* data0, int* data1, int* data2, int mcu, int nacross) { uchar* rpic; uchar* gpic; uchar* bpic; int minx; int dx; int miny; int dy; int pici; int k; int y; int Y; int Cb; int Cr; int r; int g; int b; int x; rpic = chans[0]; gpic = chans[1]; bpic = chans[2]; minx = 8*(mcu%nacross); dx = 8; if(minx + dx > h->X) dx = h->X - minx; miny = 8*(mcu/nacross); dy = 8; if(miny + dy > h->Y) dy = h->Y - miny; pici = miny*h->X + minx; k = 0; for(y = 0; y < dy; y++) { for(x = 0; x < dx; x++) { if(jpegcolorspace == CYCbCr) { rpic[pici + x] = clampb[data0[k + x] + 128 + CLAMPBOFF]; gpic[pici + x] = clampb[data1[k + x] + 128 + CLAMPBOFF]; bpic[pici + x] = clampb[data2[k + x] + 128 + CLAMPBOFF]; } else { Y = (data0[k + x] + 128) << 11; Cb = data1[k + x]; Cr = data2[k + x]; r = Y + jc1*Cr; g = Y - jc2*Cb - jc3*Cr; b = Y + jc4*Cb; rpic[pici + x] = clampb[(r >> 11) + CLAMPBOFF]; gpic[pici + x] = clampb[(g >> 11) + CLAMPBOFF]; bpic[pici + x] = clampb[(b >> 11) + CLAMPBOFF]; } } pici += h->X; k += 8; } } // Fills in pixels (x,y) for x = minx=8*Hmax*(mcu%nacross), minx+1, ..., minx+8*Hmax-1 (or h.X-1, if less) // and for y = miny=8*Vmax*(mcu/nacross), miny+1, ..., miny+8*Vmax-1 (or h.Y-1, if less) static void colormap(Jpegstate* h, uchar** chans, int** data0, int** data1, int** data2, int mcu, int nacross, int Hmax, int Vmax, int* H, int* V) { uchar* rpic; uchar* gpic; uchar* bpic; int minx; int dx; int miny; int dy; int pici; int H0; int H1; int H2; int y; int Y; int Cb; int Cr; int r; int g; int b; int t; int b0; int y0; int b1; int y1; int b2; int y2; int x0; int x1; int x2; int x; rpic = chans[0]; gpic = chans[1]; bpic = chans[2]; minx = 8*Hmax*(mcu%nacross); dx = 8*Hmax; if(minx + dx > h->X) dx = h->X - minx; miny = 8*Vmax*(mcu/nacross); dy = 8*Vmax; if(miny + dy > h->Y) dy = h->Y - miny; pici = miny*h->X + minx; H0 = H[0]; H1 = H[1]; H2 = H[2]; if(dbgimg > 2) trace("colormap, minx=%d, miny=%d, dx=%d, dy=%d, pici=%d, H0=%d, H1=%d, H2=%d\n", minx, miny, dx, dy, pici, H0, H1, H2); for(y = 0; y < dy; y++) { t = y*V[0]; b0 = H0*(t/(8*Vmax)); y0 = 8*((t/Vmax)&7); t = y*V[1]; b1 = H1*(t/(8*Vmax)); y1 = 8*((t/Vmax)&7); t = y*V[2]; b2 = H2*(t/(8*Vmax)); y2 = 8*((t/Vmax)&7); x0 = 0; x1 = 0; x2 = 0; for(x = 0; x < dx; x++) { if(jpegcolorspace == CYCbCr) { rpic[pici + x] = clampb[data0[b0][y0 + x0++*H0/Hmax] + 128 + CLAMPBOFF]; gpic[pici + x] = clampb[data1[b1][y1 + x1++*H1/Hmax] + 128 + CLAMPBOFF]; bpic[pici + x] = clampb[data2[b2][y2 + x2++*H2/Hmax] + 128 + CLAMPBOFF]; } else { // RGB Y = (data0[b0][y0 + x0++*H0/Hmax] + 128) << 11; Cb = data1[b1][y1 + x1++*H1/Hmax]; Cr = data2[b2][y2 + x2++*H2/Hmax]; r = Y + jc1*Cr; g = Y - jc2*Cb - jc3*Cr; b = Y + jc4*Cb; rpic[pici + x] = clampb[(r >> 11) + CLAMPBOFF]; gpic[pici + x] = clampb[(g >> 11) + CLAMPBOFF]; bpic[pici + x] = clampb[(b >> 11) + CLAMPBOFF]; } if(x0*H0/Hmax >= 8) { x0 = 0; b0++; } if(x1*H1/Hmax >= 8) { x1 = 0; b1++; } if(x2*H2/Hmax >= 8) { x2 = 0; b2++; } } pici += h->X; } } // decode next 8-bit value from entropy-coded input. chart F-26 static int jdecode(ImageSource* is, Huffman* t) { Jpegstate* h; int* maxcode; int code; int v; int cnt; int m; int sr; int i; h = is->jstate; maxcode = t->maxcode; if(h->cnt < 8) jnextbyte(is); // fast lookup code = (h->sr >> (h->cnt - 8))&255; v = t->value[code]; if(v >= 0) { h->cnt -= t->shift[code]; return v; } h->cnt -= 8; if(h->cnt == 0) jnextbyte(is); h->cnt--; cnt = h->cnt; m = 1 << cnt; sr = h->sr; code <<= 1; for(i = 9; ; i++) { if(sr&m) code |= 1; if(code <= maxcode[i]) break; code <<= 1; m >>= 1; if(m == 0) { sr = jnextbyte(is); m = 0x80; cnt = 8; } cnt--; } h->cnt = cnt; return t->val[t->valptr[i] + (code - t->mincode[i])]; } // load next byte of input static int jnextbyte(ImageSource* is) { int b; Jpegstate* h; int b2; b = getc(is); if(b == 0xFF) { b2 = getc(is); if(b2 != 0) { if(b2 == DNL) imgerror(is, ERRimgbad); // DNL marker unimplemented ungetc2(is); } } h = is->jstate; h->cnt += 8; h->sr = (h->sr << 8)|b; return b; } // like jnextbyte, but look for marker too static int jnextborm(ImageSource* is) { int b; Jpegstate* h; b = getc(is); if(b == 0xFF) return b; h = is->jstate; h->cnt += 8; h->sr = (h->sr << 8)|b; return b; } // return next s bits of input, MSB first, and level shift it static int jreceive(ImageSource* is, int s) { Jpegstate* h; int v; int m; h = is->jstate; while(h->cnt < s) jnextbyte(is); h->cnt -= s; v = h->sr >> h->cnt; m = (1 << s); v &= m - 1; // level shift if(v < (m >> 1)) v += ~(m - 1) + 1; return v; } // return next s bits of input, decode as EOB static int jreceiveEOB(ImageSource* is, int s) { Jpegstate* h; int v; int m; h = is->jstate; while(h->cnt < s) jnextbyte(is); h->cnt -= s; v = h->sr >> h->cnt; m = (1 << s); v &= m - 1; // level shift v += m; return v; } // return next bit of input static int jreceivebit(ImageSource* is) { Jpegstate* h; h = is->jstate; if(h->cnt < 1) jnextbyte(is); h->cnt--; return (h->sr >> h->cnt)&1; } static void nibbles(int c, int* p1, int* p2) { *p1 = c >> 4; *p2 = c & 15; } // Scaled integer implementation. // inverse two dimensional DCT, Chen-Wang algorithm // (IEEE ASSP-32, pp. 803-816, Aug. 1984) // 32-bit integer arithmetic (8 bit coefficients) // 11 mults, 29 adds per DCT // // coefficients extended to 12 bit for IEEE1180-1990 // compliance static void idct(int* b) { int y; int x; int v; int eighty; int x0; int x1; int x2; int x3; int x4; int x5; int x6; int x7; int x8; // transform horizontally for(y = 0; y < 8; y++) { eighty = y << 3; // if all non-DC components are zero, just propagate the DC term if(b[eighty + 1] == 0) if(b[eighty + 2] == 0 && b[eighty + 3] == 0) if(b[eighty + 4] == 0 && b[eighty + 5] == 0) if(b[eighty + 6] == 0 && b[eighty + 7] == 0) { v = b[eighty] << 3; b[eighty + 0] = v; b[eighty + 1] = v; b[eighty + 2] = v; b[eighty + 3] = v; b[eighty + 4] = v; b[eighty + 5] = v; b[eighty + 6] = v; b[eighty + 7] = v; continue; } // prescale x0 = (b[eighty + 0] << 11) + 128; x1 = b[eighty + 4] << 11; x2 = b[eighty + 6]; x3 = b[eighty + 2]; x4 = b[eighty + 1]; x5 = b[eighty + 7]; x6 = b[eighty + 5]; x7 = b[eighty + 3]; // first stage x8 = W7*(x4 + x5); x4 = x8 + W1mW7*x4; x5 = x8 - W1pW7*x5; x8 = W3*(x6 + x7); x6 = x8 - W3mW5*x6; x7 = x8 - W3pW5*x7; // second stage x8 = x0 + x1; x0 -= x1; x1 = W6*(x3 + x2); x2 = x1 - W2pW6*x2; x3 = x1 + W2mW6*x3; x1 = x4 + x6; x4 -= x6; x6 = x5 + x7; x5 -= x7; // third stage x7 = x8 + x3; x8 -= x3; x3 = x0 + x2; x0 -= x2; x2 = (R2*(x4 + x5) + 128) >> 8; x4 = (R2*(x4 - x5) + 128) >> 8; // fourth stage b[eighty + 0] = (x7 + x1) >> 8; b[eighty + 1] = (x3 + x2) >> 8; b[eighty + 2] = (x0 + x4) >> 8; b[eighty + 3] = (x8 + x6) >> 8; b[eighty + 4] = (x8 - x6) >> 8; b[eighty + 5] = (x0 - x4) >> 8; b[eighty + 6] = (x3 - x2) >> 8; b[eighty + 7] = (x7 - x1) >> 8; } // transform vertically for(x = 0; x < 8; x++) { // if all non-DC components are zero, just propagate the DC term if(b[x + 8*1] == 0) if(b[x + 8*2] == 0 && b[x + 8*3] == 0) if(b[x + 8*4] == 0 && b[x + 8*5] == 0) if(b[x + 8*6] == 0 && b[x + 8*7] == 0) { v = (b[x + 8*0] + 32) >> 6; b[x + 8*0] = v; b[x + 8*1] = v; b[x + 8*2] = v; b[x + 8*3] = v; b[x + 8*4] = v; b[x + 8*5] = v; b[x + 8*6] = v; b[x + 8*7] = v; continue; } // prescale x0 = (b[x + 8*0] << 8) + 8192; x1 = b[x + 8*4] << 8; x2 = b[x + 8*6]; x3 = b[x + 8*2]; x4 = b[x + 8*1]; x5 = b[x + 8*7]; x6 = b[x + 8*5]; x7 = b[x + 8*3]; // first stage x8 = W7*(x4 + x5) + 4; x4 = (x8 + W1mW7*x4) >> 3; x5 = (x8 - W1pW7*x5) >> 3; x8 = W3*(x6 + x7) + 4; x6 = (x8 - W3mW5*x6) >> 3; x7 = (x8 - W3pW5*x7) >> 3; // second stage x8 = x0 + x1; x0 -= x1; x1 = W6*(x3 + x2) + 4; x2 = (x1 - W2pW6*x2) >> 3; x3 = (x1 + W2mW6*x3) >> 3; x1 = x4 + x6; x4 -= x6; x6 = x5 + x7; x5 -= x7; // third stage x7 = x8 + x3; x8 -= x3; x3 = x0 + x2; x0 -= x2; x2 = (R2*(x4 + x5) + 128) >> 8; x4 = (R2*(x4 - x5) + 128) >> 8; // fourth stage b[x + 8*0] = (x7 + x1) >> 14; b[x + 8*1] = (x3 + x2) >> 14; b[x + 8*2] = (x0 + x4) >> 14; b[x + 8*3] = (x8 + x6) >> 14; b[x + 8*4] = (x8 - x6) >> 14; b[x + 8*5] = (x0 - x4) >> 14; b[x + 8*6] = (x3 - x2) >> 14; b[x + 8*7] = (x7 - x1) >> 14; } } // Remap colors and dither int closest_rgbpix(int r, int g, int b) { int pix; int dr; int dg; int db; int d; int bestdist; int i; pix = closestrgb[((r >> 4) << 8) + ((g >> 4) << 4) + (b >> 4)]; // If white is the closest but original r,g,b wasn't white, // look for another color, because web page designer probably // cares more about contrast than actual color if(pix == 255 && !(r == 255 && g == 255 && b == 255)) { bestdist = 1000000; for(i = 1; i < 256; i++) { dr = r - rgbvmap_r[i]; dg = g - rgbvmap_g[i]; db = b - rgbvmap_b[i]; d = dr*dr + dg*dg + db*db; if(d < bestdist) { bestdist = d; pix = i; } } } return pix; } static void init_tabs(void) { int j; int t; for(j = 0; j < CLAMPNOFF; j++) { clampn_b[j] = 0; clampn_g[j] = 0; clampn_r[j] = 0; } for(j = 0; j < 256; j++) { t = j >> 4; clampn_b[CLAMPNOFF + j] = t; clampn_g[CLAMPNOFF + j] = t << 4; clampn_r[CLAMPNOFF + j] = t << 8; } for(j = 0; j < CLAMPNOFF; j++) { clampn_b[CLAMPNOFF + 256 + j] = 15; clampn_g[CLAMPNOFF + 256 + j] = 240; clampn_r[CLAMPNOFF + 256 + j] = 3840; } for(j = 0; j < CLAMPBOFF; j++) clampb[j] = (uchar)0; for(j = 0; j < 256; j++) clampb[CLAMPBOFF + j] = j; for(j = 0; j < CLAMPBOFF; j++) clampb[CLAMPBOFF + 256 + j] = 255; } // Remap pixels of pic[] into the closest colors in the rgbv map, // and do error diffusion of the result. // pic is a one-channel image whose rgb values are given by looking // up values in cmap. static void remap1(uchar* pic, int dx, int dy, uchar* cmap, int cmaplen) { int i; int j; int* ered; int* egrn; int* eblu; int p; int y; int x1; int in; int r; int g; int b; int col; int t; int er; int eg; int eb; int x; if(dbgimg) trace("remap1, pic len %d, dx=%d, dy=%d\n", dx*dy, dx, dy); i = 0; for(j = 0; j < 256 && i < cmaplen; j++) { cmap_r[j] = cmap[i++]; cmap_g[j] = cmap[i++]; cmap_b[j] = cmap[i++]; } // in case input has bad indices for(; j < 256; j++) { cmap_r[j] = 0; cmap_g[j] = 0; cmap_b[j] = 0; } // modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 ered = (int*)emallocz((dx + 1) * sizeof(int)); egrn = (int*)emallocz((dx + 1) * sizeof(int)); eblu = (int*)emallocz((dx + 1) * sizeof(int)); p = 0; for(y = 0; y < dy; y++) { er = 0; eg = 0; eb = 0; x = 0; while(x < dx) { x1 = x + 1; in = pic[p]; r = cmap_r[in] + ered[x]; g = cmap_g[in] + egrn[x]; b = cmap_b[in] + eblu[x]; col = (pic[p++] = closestrgb[clampn_r[r + CLAMPNOFF] + clampn_g[g + CLAMPNOFF] + clampn_b[b + CLAMPNOFF]]); r -= rgbvmap_r[col]; t = (3*r) >> 4; ered[x] = t + er; ered[x1] += t; er = r - 3*t; g -= rgbvmap_g[col]; t = (3*g) >> 4; egrn[x] = t + eg; egrn[x1] += t; eg = g - 3*t; b -= rgbvmap_b[col]; t = (3*b) >> 4; eblu[x] = t + eb; eblu[x1] += t; eb = b - 3*t; x = x1; } } } // Remap pixels of pic[] into the closest greyscale colors in the rgbv map, // and do error diffusion of the result. // pic is a one-channel greyscale image. static void remapgrey(uchar* pic, int dx, int dy) { int* e; int p; int y; int x1; int b; int b1; int col; int t; int eb; int x; if(dbgimg) trace("remapgrey, pic len %d, dx=%d, dy=%d\n", dx*dy, dx, dy); // modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 e = (int*)emallocz((dx + 1) * sizeof(int)); p = 0; for(y = 0; y < dy; y++) { eb = 0; x = 0; while(x < dx) { x1 = x + 1; b = pic[p] + e[x]; b1 = clampn_b[b + CLAMPNOFF]; col = 17*b1; pic[p++] = col; b -= rgbvmap_b[col]; t = (3*b) >> 4; e[x] = t + eb; e[x1] += t; eb = b - 3*t; x = x1; } } } // Remap pixels of chans into the closest colors in the rgbv map, // and do error diffusion of the result. // chans is a 3-channel image whose channels are either (y,cb,cr) or // (r,g,b), depending on whether jpegcolorspace is CYCbCr or CRGB. // Variable names use r,g,b (historical). // (right now we only call this routine from jpeg code). static void remaprgb(uchar** chans, int dx, int dy) { uchar* rpic; uchar* gpic; uchar* bpic; uchar* pic; int* ered; int* egrn; int* eblu; uchar* closest; int* map0; int* map1; int* map2; int p; int y; int x1; int r; int g; int b; int col; int t; int er; int eg; int eb; int x; if(dbgimg) trace("remaprgb, pic len %d, dx=%d, dy=%d\n", dx*dy, dx, dy); rpic = chans[0]; gpic = chans[1]; bpic = chans[2]; pic = chans[0]; // modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 ered = (int*)emallocz((dx + 1) * sizeof(int)); egrn = (int*)emallocz((dx + 1) * sizeof(int)); eblu = (int*)emallocz((dx + 1) * sizeof(int)); if(jpegcolorspace == CRGB) { closest = closestrgb; map0 = rgbvmap_r; map1 = rgbvmap_g; map2 = rgbvmap_b; } else { closest = closestycbcr; map0 = rgbvmap_y; map1 = rgbvmap_cb; map2 = rgbvmap_cr; } p = 0; for(y = 0; y < dy; y++) { er = 0; eg = 0; eb = 0; x = 0; while(x < dx) { x1 = x + 1; r = rpic[p] + ered[x]; g = gpic[p] + egrn[x]; b = bpic[p] + eblu[x]; // Errors can be uncorrectable if converting from YCbCr, // since we can't guarantee that an extremal value of one of // the components selects a color with an extremal value. // If we don't, the errors accumulate without bound. This // doesn't happen in RGB because the closest table can guarantee // a color on the edge of the gamut, producing a zero error in // that component. For the rotation YCbCr space, there may be // no color that can guarantee zero error at the edge. // Therefore we must clamp explicitly rather than by assuming // an upper error bound of CLAMPOFF. The performance difference // is miniscule anyway. if(r < 0) r = 0; else if(r > 255) r = 255; if(g < 0) g = 0; else if(g > 255) g = 255; if(b < 0) b = 0; else if(b > 255) b = 255; col = (pic[p++] = closest[(b >> 4) + 16*((g >> 4) + 16*(r >> 4))]); r -= map0[col]; t = (3*r) >> 4; ered[x] = t + er; ered[x1] += t; er = r - 3*t; g -= map1[col]; t = (3*g) >> 4; egrn[x] = t + eg; egrn[x1] += t; eg = g - 3*t; b -= map2[col]; t = (3*b) >> 4; eblu[x] = t + eb; eblu[x1] += t; eb = b - 3*t; x = x1; } } } // Given src array, representing sw*sh pixel values, resample them into // the returned array, with dimensions dw*dh. // // Quick and dirty resampling: just interpolate. // This lets us resample arrays of pixels indices (e.g., result of gif decoding). // The filter-based resampling methods need conversion to rgb or grayscale. // Also, although the results won't look good, people really shouldn't be // asking the browser to resample except for special purposes (like the common // case of resizing a 1x1 image to make a spacer). static uchar* resample(uchar* src, int sw, int sh, int dw, int dh) { double xfac; double yfac; int totpix; uchar* dst; int dindex; int* sindices; int dx; int dy; int sx; int sy; int soffset; if(dbgev) logtime("IMAGE_RESAMPLE_START", 0); if(src == nil || sw == 0 || sh == 0 || dw == 0 || dh == 0) return src; xfac = (double)sw/(double)dw; yfac = (double)sh/(double)dh; totpix = dw*dh; dst = (uchar*)emalloc(totpix); dindex = 0; // precompute index in src row corresponding to each index in dst row sindices = (int*)emalloc(dw * sizeof(int)); for(dx = 0; dx < dw; dx++) { sx = (int)((xfac*(double)dx)+.5); if(sx >= sw) sx = sw - 1; sindices[dx] = sx; } for(dy = 0; dy < dh; dy++) { sy = (int)((yfac*(double)dy)+.5); if(sy >= sh) sy = sh - 1; soffset = sy*sw; for(dx = 0; dx < dw; dx++) dst[dindex++] = src[soffset + sindices[dx]]; } if(dbgev) logtime("IMAGE_RESAMPLE_END", 0); return dst; }