ref: fe1eb39db7ae6904924f3ab1f6f9b34416f2eb1b
dir: /sys/src/cmd/jpg/writejpg.c/
/* * code/documentation from: * http://www.w3.org/Graphics/JPEG/jfif3.pdf * http://www.w3.org/Graphics/JPEG/itu-t81.pdf * http://en.wikipedia.org/wiki/JPEG * http://en.wikibooks.org/wiki/JPEG_-_Idea_and_Practice * http://code.google.com/p/go/source/browse/src/pkg/image/jpeg/writer.go * /sys/src/cmd/jpg * * fdct code from: * http://www.ijg.org/files/jpegsrc.v8c.tar.gz * http://code.google.com/p/go/source/browse/src/pkg/image/jpeg/fdct.go */ #include <u.h> #include <libc.h> #include <draw.h> #include <memdraw.h> #include <bio.h> #include "imagefile.h" /* * imported from libdraw/arith.c to permit * extern log2 function */ static int log2[] = { -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, -1, -1, -1, -1, -1, -1, -1, 5 }; /* fdct constants */ enum { Fix02 = 2446, /* 0.298631336 */ Fix03 = 3196, /* 0.390180644 */ Fix05 = 4433, /* 0.541196100 */ Fix07 = 6270, /* 0.765366865 */ Fix08 = 7373, /* 0.899976223 */ Fix11 = 9633, /* 1.175875602 */ Fix15 = 12299, /* 1.501321110 */ Fix18 = 15137, /* 1.847759065 */ Fix19 = 16069, /* 1.961570560 */ Fix20 = 16819, /* 2.053119869 */ Fix25 = 20995, /* 2.562915447 */ Fix30 = 25172 /* 3.072711026 */ }; static int zigzag[64] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 }; static int invzigzag[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; /* section K.1 for quantization tables */ static int qt[2][64] = { /* luminance */ {16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99}, /* chrominance */ {17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99} }; /* section K.3.3 for huffman tables */ static int dcbits[2][16] = { /* luminance */ {0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* chrominance */ {0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} }; static int dchuffval[2][12] = { /* luminance */ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b}, /* chrominance */ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b} }; static int acbits[2][16] = { /* luminance */ {0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d}, /* chrominance */ {0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77} }; static int achuffval[2][162] = { /* luminance */ {0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa}, /* chrominance */ {0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa} }; static int ehufcod[2][12]; /* dc codes */ static int ehufsid[2][12]; /* dc sizes */ static int ehufcoa[2][251]; /* ac codes */ static int ehufsia[2][251]; /* ac sizes */ static int byte; static int nbyte; /* utilities */ static int Bputs(Biobuf *, int); static int min(int, int); /* encoding */ static void grey2rgb(int *, int *, int *, int, int); static void rgb2ycc(int *, int *, int *, int, int, int); static void fdct(int *, int); static int csize(int, int); static void writebyte(Biobuf *); static void writebits(Biobuf *, int, int); static void writecode(Biobuf *, int, int); static int huf(Biobuf *, int *, int, int, int); static char *toycc1(int *, int *, int *, int, int, int, int, int, uchar *, int, int); static char *toycc2(int *, int *, int *, int, int, int, int, int, uchar *, int, int); static char *encode(Biobuf *, Rectangle, uchar *, ulong, int, int, int); /* huffman tables */ static void makehuf(int *, int *, int *, int *, int); /* tables, markers, headers, trailers */ static void writejfif(Biobuf *, int, int); static void writecomment(Biobuf *, char *); static void writequant(Biobuf *, int, int); static void writehuffman(Biobuf *, int, int); static void writeframe(Biobuf *, int, int, int); static void writescan(Biobuf *, int); static void writeheader(Biobuf *, int, int, char *, int, int); static void writetrailer(Biobuf *); static char *writedata(Biobuf *, Image *, Memimage *, int, int); static char *writejpg0(Biobuf *, Image *, Memimage *, Rectangle, ulong, char *, int, int); static int Bputs(Biobuf *b, int s) { if(Bputc(b, s>>8) < 0) return -1; return Bputc(b, s); } static int min(int a, int b) { return a < b? a: b; } static void grey2rgb(int *r, int *g, int *b, int c, int depth) { if(depth == 1) { if(c != 0) c = 0xff; } else if(depth == 2) c = (c << 6) | (c << 4) | (c << 2) | c; else c = (c << 4) | c; c = cmap2rgb(c); *r = (c >> 16) & 0xff; *g = (c >> 8) & 0xff; *b = c & 0xff; } static void rgb2ycc(int *y, int *cb, int *cr, int r, int g, int b) { *y = (int)(0.299*r + 0.587*g + 0.114*b); *cb = (int)(128.0 - 0.1687*r - 0.3313*g + 0.5*b); *cr = (int)(128.0 + 0.5*r - 0.4187*g - 0.0813*b); } /* coefficients remain scaled up by 8 at the end */ static void fdct(int *b, int sflag) { int x, y, z, tmp0, tmp1, tmp2, tmp3; int tmp10, tmp12, tmp11, tmp13; /* rows */ for(y = 0; y < 8; y++) { tmp0 = b[y*8+0] + b[y*8+7]; tmp1 = b[y*8+1] + b[y*8+6]; tmp2 = b[y*8+2] + b[y*8+5]; tmp3 = b[y*8+3] + b[y*8+4]; tmp10 = tmp0 + tmp3; tmp12 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp13 = tmp1 - tmp2; tmp0 = b[y*8+0] - b[y*8+7]; tmp1 = b[y*8+1] - b[y*8+6]; tmp2 = b[y*8+2] - b[y*8+5]; tmp3 = b[y*8+3] - b[y*8+4]; b[y*8+0] = (tmp10 + tmp11 - 8*128) << 2; b[y*8+4] = (tmp10 - tmp11) << 2; z = (tmp12 + tmp13) * Fix05; z += 1 << 10; b[y*8+2] = (z + tmp12*Fix07) >> 11; b[y*8+6] = (z - tmp13*Fix18) >> 11; tmp10 = tmp0 + tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp0 + tmp2; tmp13 = tmp1 + tmp3; z = (tmp12 + tmp13) * Fix11; z += 1 << 10; tmp0 *= Fix15; tmp1 *= Fix30; tmp2 *= Fix20; tmp3 *= Fix02; tmp10 *= -Fix08; tmp11 *= -Fix25; tmp12 *= -Fix03; tmp13 *= -Fix19; tmp12 += z; tmp13 += z; b[y*8+1] = (tmp0 + tmp10 + tmp12) >> 11; b[y*8+3] = (tmp1 + tmp11 + tmp13) >> 11; b[y*8+5] = (tmp2 + tmp11 + tmp12) >> 11; b[y*8+7] = (tmp3 + tmp10 + tmp13) >> 11; } /* columns */ for(x = 0; x < 8; x++) { tmp0 = b[0*8+x] + b[7*8+x]; tmp1 = b[1*8+x] + b[6*8+x]; tmp2 = b[2*8+x] + b[5*8+x]; tmp3 = b[3*8+x] + b[4*8+x]; if(sflag) tmp10 = (tmp0 + tmp3 + 1) << 1; else tmp10 = tmp0 + tmp3 + (1 << 1); tmp12 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp13 = tmp1 - tmp2; tmp0 = b[0*8+x] - b[7*8+x]; tmp1 = b[1*8+x] - b[6*8+x]; tmp2 = b[2*8+x] - b[5*8+x]; tmp3 = b[3*8+x] - b[4*8+x]; b[0*8+x] = (tmp10 + tmp11) >> 2; b[4*8+x] = (tmp10 - tmp11) >> 2; z = (tmp12 + tmp13) * Fix05; z += 1 << 14; b[2*8+x] = (z + tmp12*Fix07) >> 15; b[6*8+x] = (z - tmp13*Fix18) >> 15; tmp10 = tmp0 + tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp0 + tmp2; tmp13 = tmp1 + tmp3; z = (tmp12 + tmp13) * Fix11; z += 1 << 14; tmp0 *= Fix15; tmp1 *= Fix30; tmp2 *= Fix20; tmp3 *= Fix02; tmp10 *= -Fix08; tmp11 *= -Fix25; tmp12 *= -Fix03; tmp13 *= -Fix19; tmp12 += z; tmp13 += z; b[1*8+x] = (tmp0 + tmp10 + tmp12) >> 15; b[3*8+x] = (tmp1 + tmp11 + tmp13) >> 15; b[5*8+x] = (tmp2 + tmp11 + tmp12) >> 15; b[7*8+x] = (tmp3 + tmp10 + tmp13) >> 15; } } static int csize(int coeff, int ac) { int i, max; max = 1 << 10; if(!ac) max <<= 1; if(coeff < 0) coeff *= -1; if(coeff > max) sysfatal("csize: coeff too big: %d", coeff); i = ac? 1: 0; while(coeff >= (1<<i)) i++; if(ac && (i < 1 || i > 10)) sysfatal("csize: invalid ac ssss: %d", i); if(!ac && (i < 0 || i > 11)) sysfatal("csize: invalid dc ssss: %d", i); return i; } static void writebyte(Biobuf *fd) { Bputc(fd, byte); if(byte == 0xff) /* byte stuffing */ Bputc(fd, 0x00); byte = 0; nbyte = 7; } static void writebits(Biobuf *fd, int co, int si) { int i, bit; for(i = si-1; i >= 0; i--) { bit = (co >> i) & 0x1; byte |= bit << nbyte; nbyte--; if(nbyte < 0) writebyte(fd); } } static void writecode(Biobuf *fd, int co, int si) { if(si > 8) { writebits(fd, co>>8, si-8); si = 8; } writebits(fd, co, si); } static int huf(Biobuf *fd, int *b, int pred, int chr, int sflag) { int k, r, s, rs, si, co, dc, diff, zz[64], p, q, z; if(sflag) { for(k = 0; k < 64; k++) { p = b[zigzag[k]]; q = qt[chr][zigzag[k]]; zz[k] = p / q; } } else { for(k = 0; k < 64; k++) { p = b[k]; q = (qt[chr][k] << 3); /* rounding */ if(p >= 0) z = (p + (q >> 1)) / q; else z = -(-p + (q >> 1)) / q; zz[zigzag[k]] = z; } } /* dc coefficient */ dc = zz[0]; zz[0] = diff = dc - pred; s = csize(diff, 0); si = ehufsid[chr][s]; co = ehufcod[chr][s]; writecode(fd, co, si); if(diff < 0) diff -= 1; writecode(fd, diff, s); /* figure F.2 */ for(k = 1, r = 0; k < 64; k++) { if(zz[k] == 0) { if(k < 63) r++; else { si = ehufsia[chr][0x00]; co = ehufcoa[chr][0x00]; writecode(fd, co, si); } } else { while(r > 15) { si = ehufsia[chr][0xf0]; co = ehufcoa[chr][0xf0]; writecode(fd, co, si); r -= 16; } /* figure F.3 */ s = csize(zz[k], 1); rs = (r << 4) | s; si = ehufsia[chr][rs]; co = ehufcoa[chr][rs]; writecode(fd, co, si); if(zz[k] < 0) zz[k] -= 1; writecode(fd, zz[k], s); r = 0; } } return dc; } static char * toycc1(int *y, int *cb, int *cr, int jx, int jy, int dx, int dy, int bpl, uchar *data, int ndata, int depth) { int i, j, k, l, m, n, u, v, pos, pmask, nmask, pix; int r, g, b; m = 8 / depth; pmask = (1 << depth) - 1; nmask = 7 >> log2[depth]; for(i = jy, k = 0; i < jy+8; i++) { v = min(i, dy-1); for(l = 0, j = jx/m; l < 8; l++, k++) { u = min(j, (dx-1)/m); n = l+jx >= dx? dx-jx-1: l; pos = v*bpl + u; if(pos >= ndata) return "WriteJPG: overflow"; /* thanks writeppm */ pix = (data[pos] >> depth*((nmask - n) & nmask)) & pmask; if(((n + 1) & nmask) == 0) j++; grey2rgb(&r, &g, &b, pix, depth); rgb2ycc(&y[k], &cb[k], &cr[k], r, g, b); } } return nil; } static char * toycc2(int *y, int *cb, int *cr, int jx, int jy, int dx, int dy, int bpl, uchar *data, int ndata, int depth) { int i, j, k, m, u, v, pos; m = depth / 8; for(i = jy, k = 0; i < jy+8; i++) { v = min(i, dy-1); for(j = jx*m; j < (jx+8)*m; j+=m, k++) { u = min(j, (dx-1)*m); pos = v*bpl + u; if(pos+m-1 >= ndata) return "WriteJPG: overflow"; rgb2ycc(&y[k], &cb[k], &cr[k], data[pos+2*m/3], data[pos+m/3], data[pos]); } } return nil; } static char * encode(Biobuf *fd, Rectangle r, uchar *data, ulong chan, int ndata, int kflag, int sflag) { int k, x, y, dx, dy, depth, bpl, ncomp; int b[3][64], pred[3]; char *err; char *(*toycc)(int *, int *, int *, int, int, int, int, int, uchar *, int, int); byte = 0; nbyte = 7; switch(chan) { case GREY1: case GREY2: case GREY4: toycc = toycc1; break; case GREY8: case RGB24: toycc = toycc2; break; default: return "WriteJPG: can't handle channel type"; } /* * if dx or dy is not a multiple of 8, * the decoder should continue until reaching * the last mcu, even if the extra pixels go beyond * 0xffff. they are not shown anyway. */ dx = min(Dx(r), 0xffff); dy = min(Dy(r), 0xffff); depth = chantodepth(chan); bpl = bytesperline(r, depth); ncomp = kflag? 1: 3; memset(pred, 0, sizeof pred); for(x = 0, y = 0;;) { err = (*toycc)(b[0], b[1], b[2], x, y, dx, dy, bpl, data, ndata, depth); if(err != nil) return err; for(k = 0; k < ncomp; k++) { fdct(b[k], sflag); pred[k] = huf(fd, b[k], pred[k], k>0, sflag); } if((x += 8) >= dx) { if((y += 8) >= dy) break; x = 0; } } if(nbyte < 7) { /* bit padding */ for(; nbyte >= 0; nbyte--) byte |= 0x1 << nbyte; writebyte(fd); } return err; } static void makehuf(int *ehufco, int *ehufsi, int *bits, int *huffval, int n) { int i, j, k, code, si, lastk, *huffcode, *huffsize; /* n+1 for lastk */ if((huffcode = malloc((n+1)*sizeof *huffcode)) == nil) sysfatal("malloc: %r"); if((huffsize = malloc((n+1)*sizeof *huffsize)) == nil) sysfatal("malloc: %r"); /* figure C.1 */ for(k = 0, i = 1, j = 1; i <= 16;) { if(j > bits[i-1]) { /* bits[i] in T.81: bug? */ i++; j = 1; } else { huffsize[k++] = i; j++; } } huffsize[k] = 0; lastk = k; /* figure C.2 */ for(k = 0, code = 0, si = huffsize[0];;) { do { huffcode[k++] = code++; } while(huffsize[k] == si); if(huffsize[k] == 0) break; while(huffsize[k] != si) { code <<= 1; si++; } } /* figure C.3 */ for(k = 0; k < lastk; k++) { i = huffval[k]; ehufco[i] = huffcode[k]; ehufsi[i] = huffsize[k]; } free(huffcode); free(huffsize); } static void writejfif(Biobuf *fd, int dx, int dy) { if(dx > 0xffff || dy > 0xffff) sysfatal("writejfif: dx or dy too big"); Bputs(fd, 0xffe0); Bputs(fd, 0x0010); Bputc(fd, 0x4a); Bputc(fd, 0x46); Bputc(fd, 0x49); Bputc(fd, 0x46); Bputc(fd, 0x00); Bputs(fd, 0x0102); Bputc(fd, 0x01); Bputs(fd, dx); Bputs(fd, dy); Bputc(fd, 0x00); Bputc(fd, 0x00); } static void writecomment(Biobuf *fd, char *com) { int n; if(com != nil && com[0] != '\0') { n = min(strlen(com)+2, 0xffff); Bputs(fd, 0xfffe); Bputs(fd, n); Bwrite(fd, com, n-2); } } static void writequant(Biobuf *fd, int tq, int sflag) { int i, *p, *q; if(tq != 0x0 && tq != 0x1) sysfatal("writequant: invalid Tq"); q = qt[tq]; Bputs(fd, 0xffdb); Bputs(fd, 0x0043); Bputc(fd, (0x0<<4)|tq); p = sflag? zigzag: invzigzag; for(i = 0; i < 64; i++) Bputc(fd, q[p[i]]); } static void writehuffman(Biobuf *fd, int tc, int th) { int i, n, m, *b, *hv; if((tc != 0x0 && tc != 0x1) || (th != 0x0 && th != 0x1)) sysfatal("writehuffman: invalid Tc or Th"); n = 0x0013; if(tc == 0x0) { b = dcbits[th]; hv = dchuffval[th]; m = nelem(dchuffval[th]); } else { b = acbits[th]; hv = achuffval[th]; m = nelem(achuffval[th]); } Bputs(fd, 0xffc4); Bputs(fd, n+m); Bputc(fd, (tc<<4)|th); for(i = 0; i < 16; i++) Bputc(fd, b[i]); for(i = 0; i < m; i++) Bputc(fd, hv[i]); } static void writeframe(Biobuf *fd, int y, int x, int kflag) { int n, nf; nf = kflag? 0x01: 0x03; n = 0x0008 + 0x0003*nf; Bputs(fd, 0xffc0); Bputs(fd, n); Bputc(fd, 0x08); Bputs(fd, y); Bputs(fd, x); Bputc(fd, nf); /* Y component */ Bputc(fd, 0x00); Bputc(fd, (0x1<<4)|0x1); Bputc(fd, 0x00); if(!kflag) { /* Cb component */ Bputc(fd, 0x01); Bputc(fd, (0x1<<4)|0x1); Bputc(fd, 0x01); /* Cr component */ Bputc(fd, 0x02); Bputc(fd, (0x1<<4)|0x1); Bputc(fd, 0x01); } } static void writescan(Biobuf *fd, int kflag) { int n, ns; ns = kflag? 0x01: 0x03; n = 0x0006 + 0x0002*ns; Bputs(fd, 0xffda); Bputs(fd, n); Bputc(fd, ns); /* Y component */ Bputc(fd, 0x00); Bputc(fd, (0x0<<4)|0x0); if(!kflag) { /* Cb component */ Bputc(fd, 0x01); Bputc(fd, (0x1<<4)|0x1); /* Cr component */ Bputc(fd, 0x02); Bputc(fd, (0x1<<4)|0x1); } Bputc(fd, 0x00); Bputc(fd, 0x3f); Bputc(fd, (0x0<<4)|0x0); } static void writeheader(Biobuf *fd, int dx, int dy, char *s, int kflag, int sflag) { int i; dx = min(dx, 0xffff); dy = min(dy, 0xffff); Bputs(fd, 0xffd8); writejfif(fd, dx, dy); writecomment(fd, s); writequant(fd, 0, sflag); if(!kflag) writequant(fd, 1, sflag); writeframe(fd, dy, dx, kflag); for(i = 0; i < 2; i++) { writehuffman(fd, i, 0); if(!kflag) writehuffman(fd, i, 1); } writescan(fd, kflag); } static void writetrailer(Biobuf *fd) { Bputs(fd, 0xffd9); } static char * writedata(Biobuf *fd, Image *i, Memimage *m, int kflag, int sflag) { char *err; uchar *data; int ndata, depth; ulong chan; Rectangle r; if(m != nil) { r = m->r; depth = m->depth; chan = m->chan; } else { r = i->r; depth = i->depth; chan = i->chan; } /* * potentially one extra byte on each * end of each scan line */ ndata = Dy(r) * (2 + Dx(r)*depth/8); if((data = malloc(ndata)) == nil) return "WriteJPG: malloc failed"; if(m != nil) ndata = unloadmemimage(m, r, data, ndata); else ndata = unloadimage(i, r, data, ndata); if(ndata < 0) { if((err = malloc(ERRMAX)) == nil) { free(data); return "WriteJPG: malloc failed"; } snprint(err, ERRMAX, "WriteJPG: %r"); } else err = encode(fd, r, data, chan, ndata, kflag, sflag); free(data); return err; } static char * writejpg0(Biobuf *fd, Image *image, Memimage *memimage, Rectangle r, ulong chan, char *s, int kflag, int sflag) { int i; char *err; switch(chan) { case GREY1: case GREY2: case GREY4: case GREY8: kflag = 1; break; case RGB24: break; default: return "WriteJPG: can't handle channel type"; } for(i = 0; i < 2; i++) { memset(ehufcod[i], 0, sizeof ehufcod[i]); memset(ehufsid[i], 0, sizeof ehufsid[i]); memset(ehufcoa[i], 0, sizeof ehufcoa[i]); memset(ehufsia[i], 0, sizeof ehufsia[i]); makehuf(ehufcod[i], ehufsid[i], dcbits[i], dchuffval[i], nelem(dchuffval[i])); makehuf(ehufcoa[i], ehufsia[i], acbits[i], achuffval[i], nelem(achuffval[i])); } writeheader(fd, Dx(r), Dy(r), s, kflag, sflag); err = writedata(fd, image, memimage, kflag, sflag); writetrailer(fd); return err; } char * writejpg(Biobuf *fd, Image *i, char *s, int kflag, int sflag) { return writejpg0(fd, i, nil, i->r, i->chan, s, kflag, sflag); } char * memwritejpg(Biobuf *fd, Memimage *m, char *s, int kflag, int sflag) { return writejpg0(fd, nil, m, m->r, m->chan, s, kflag, sflag); }