tojpg: add jpeg encoder

This commit is contained in:
ppatience0 2013-05-26 21:56:56 -04:00
parent 71a3ab3f13
commit 410ce8feec
6 changed files with 1040 additions and 6 deletions

2
rc/bin/togeordi Executable file
View file

@ -0,0 +1,2 @@
#!/bin/rc
tojpg -s $*

View file

@ -1,6 +1,6 @@
.TH JPG 1 .TH JPG 1
.SH NAME .SH NAME
jpg, gif, png, ppm, bmp, v210, yuv, ico, tga, togif, toppm, topng, toico \- view and convert pictures jpg, gif, png, ppm, bmp, v210, yuv, ico, tga, tojpg, togeordi, togif, toppm, topng, toico \- view and convert pictures
.SH SYNOPSIS .SH SYNOPSIS
.B jpg .B jpg
[ [
@ -58,6 +58,26 @@ jpg, gif, png, ppm, bmp, v210, yuv, ico, tga, togif, toppm, topng, toico \- view
.I file ... .I file ...
] ]
.PP .PP
.B tojpg
[
.B -c
.I comment
] [
.B -gs
] [
.I file
]
.br
.B togeordi
[
.B -c
.I comment
] [
.B -g
] [
.I file
]
.br
.B togif .B togif
[ [
.B -c .B -c
@ -126,11 +146,12 @@ and
read files in the corresponding formats and, by default, display read files in the corresponding formats and, by default, display
them in the current window; options cause them instead to convert the images them in the current window; options cause them instead to convert the images
to Plan 9 image format and write them to standard output. to Plan 9 image format and write them to standard output.
.IR Togif , .IR Tojpg ,
.IR togif ,
.IR toppm , .IR toppm ,
and and
.I topng .I topng
read Plan 9 images files, convert them to GIF, PPM, or PNG, and write them to standard output. read Plan 9 images files, convert them to JPEG, GIF, PPM, or PNG, and write them to standard output.
.PP .PP
The default behavior of The default behavior of
.IR jpg , .IR jpg ,
@ -205,12 +226,13 @@ The input is a motion JPEG file, with multiple images representing frames of the
.PD .PD
.PP .PP
The The
.IR tojpg ,
.IR togif .IR togif
and and
.IR toppm .IR toppm
programs go the other way: they convert from Plan 9 images to GIF and PPM, programs go the other way: they convert from Plan 9 images to JPEG, GIF and PPM,
and have no display capability. and have no display capability.
Both accept an option They all accept an option
.B -c .B -c
to set the comment field of the resulting file. to set the comment field of the resulting file.
The The
@ -219,6 +241,20 @@ option makes
.I toppm .I toppm
output raw PPM. output raw PPM.
The default is to output plain PPM. The default is to output plain PPM.
The
.B -g
option makes
.I tojpg
output grayscale images,
and the
.B -s
option makes it output scratched JPEG images.
.I Togeordi
is an
.IR rc (1)
script that invokes
.B tojpg
.BR -s .
If there is only one input picture, If there is only one input picture,
.I togif .I togif
converts the image to GIF format. converts the image to GIF format.
@ -269,12 +305,20 @@ a single icon file. The masks in the icon file will be the white
space in the image. The icon file is written to standard output. space in the image. The icon file is written to standard output.
.SH SOURCE .SH SOURCE
.B /sys/src/cmd/jpg .B /sys/src/cmd/jpg
.br
.B /rc/bin/togeordi
.SH "SEE ALSO" .SH "SEE ALSO"
.IR page (1), .IR page (1),
.IR image (6). .IR image (6).
.br .br
.B http://www.w3.org/Graphics/JPEG/jfif3.pdf
.br
.B http://www.w3.org/Graphics/JPEG/itu-t81.pdf .B http://www.w3.org/Graphics/JPEG/itu-t81.pdf
.br .br
.B http://en.wikibooks.org/wiki/JPEG_-_Idea_and_Practice
.br
.B http://en.wikipedia.org/wiki/JPEG
.br
.B http://www.w3.org/Graphics/GIF/spec-gif89a.txt .B http://www.w3.org/Graphics/GIF/spec-gif89a.txt
.br .br
.B http://www.w3.org/TR/2003/REC-PNG-20031110 .B http://www.w3.org/TR/2003/REC-PNG-20031110

View file

@ -77,3 +77,6 @@ Image* multichan(Image*);
Memimage* memmultichan(Memimage*); Memimage* memmultichan(Memimage*);
char* memwritepng(Biobuf*, Memimage*, ImageInfo*); char* memwritepng(Biobuf*, Memimage*, ImageInfo*);
char* writejpg(Biobuf*, Image*, char*, int, int);
char* memwritejpg(Biobuf*, Memimage*, char*, int, int);

View file

@ -1,6 +1,8 @@
</$objtype/mkfile </$objtype/mkfile
TARG=jpg\ TARG=\
jpg\
tojpg\
gif\ gif\
togif\ togif\
ppm\ ppm\
@ -46,6 +48,7 @@ c=`{sed -n 's/^O=//p' /$cputype/mkfile}
$O.tga: $IMFILES readtga.$O tga.$O $O.tga: $IMFILES readtga.$O tga.$O
$O.jpg: $IMFILES readjpg.$O jpg.$O $O.jpg: $IMFILES readjpg.$O jpg.$O
$O.tojpg: writejpg.$O multichan.$O tojpg.$O
$O.gif: $IMFILES readgif.$O gif.$O $O.gif: $IMFILES readgif.$O gif.$O
$O.togif: writegif.$O onechan.$O togif.$O torgbv.$O $O.togif: writegif.$O onechan.$O togif.$O torgbv.$O
$O.ppm: $IMFILES readppm.$O ppm.$O $O.ppm: $IMFILES readppm.$O ppm.$O

70
sys/src/cmd/jpg/tojpg.c Normal file
View file

@ -0,0 +1,70 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <ctype.h>
#include <bio.h>
#include "imagefile.h"
void
usage(void)
{
fprint(2, "usage: %s [-c 'comment'] [-gs] [file]\n", argv0);
exits("usage");
}
void
main(int argc, char *argv[])
{
Biobuf bout;
Memimage *i, *ni;
int fd, gflag, sflag;
char *err, *file, *com;
gflag = sflag = 0;
com = nil;
ARGBEGIN {
case 'c':
com = EARGF(usage());
break;
case 'g':
gflag = 1;
break;
case 's':
sflag = 1;
break;
default:
usage();
} ARGEND
if(argc > 1)
usage();
if(argc == 0) {
file = "<stdin>";
fd = 0;
} else {
file = argv[0];
if((fd = open(file, OREAD)) < 0)
sysfatal("open %s: %r", file);
}
if(Binit(&bout, 1, OWRITE) < 0)
sysfatal("Binit: %r");
memimageinit();
if((i = readmemimage(fd)) == nil)
sysfatal("readimage %s: %r", file);
close(fd);
if((ni = memmultichan(i)) == nil)
sysfatal("converting image to RGB24: %r");
if(i != ni) {
freememimage(i);
i = ni;
}
err = memwritejpg(&bout, i, com, gflag, sflag);
freememimage(i);
if(err != nil)
fprint(2, "%s: %s\n", argv0, err);
exits(err);
}

912
sys/src/cmd/jpg/writejpg.c Normal file
View file

@ -0,0 +1,912 @@
/*
* 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 gflag, 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 = gflag? 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 gflag)
{
int n, nf;
nf = gflag? 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(!gflag) {
/* 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 gflag)
{
int n, ns;
ns = gflag? 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(!gflag) {
/* 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 gflag, 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(!gflag)
writequant(fd, 1, sflag);
writeframe(fd, dy, dx, gflag);
for(i = 0; i < 2; i++) {
writehuffman(fd, i, 0);
if(!gflag)
writehuffman(fd, i, 1);
}
writescan(fd, gflag);
}
static void
writetrailer(Biobuf *fd)
{
Bputs(fd, 0xffd9);
Bflush(fd);
}
static char *
writedata(Biobuf *fd, Image *i, Memimage *m, int gflag, 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)
return "WriteJPG: malloc failed";
snprint(err, ERRMAX, "WriteJPG: %r");
} else
err = encode(fd, r, data, chan, ndata, gflag, sflag);
free(data);
return err;
}
static char *
writejpg0(Biobuf *fd, Image *image, Memimage *memimage,
Rectangle r, ulong chan, char *s, int gflag, int sflag)
{
int i;
char *err;
switch(chan) {
case GREY1:
case GREY2:
case GREY4:
case GREY8:
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, gflag, sflag);
err = writedata(fd, image, memimage, gflag, sflag);
writetrailer(fd);
return err;
}
char *
writejpg(Biobuf *fd, Image *i, char *s, int gflag, int sflag)
{
return writejpg0(fd, i, nil, i->r, i->chan, s, gflag, sflag);
}
char *
memwritejpg(Biobuf *fd, Memimage *m, char *s, int gflag, int sflag)
{
return writejpg0(fd, nil, m, m->r, m->chan, s, gflag, sflag);
}