tojpg: add jpeg encoder
This commit is contained in:
parent
71a3ab3f13
commit
410ce8feec
6 changed files with 1040 additions and 6 deletions
2
rc/bin/togeordi
Executable file
2
rc/bin/togeordi
Executable file
|
@ -0,0 +1,2 @@
|
|||
#!/bin/rc
|
||||
tojpg -s $*
|
|
@ -1,6 +1,6 @@
|
|||
.TH JPG 1
|
||||
.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
|
||||
.B jpg
|
||||
[
|
||||
|
@ -58,6 +58,26 @@ jpg, gif, png, ppm, bmp, v210, yuv, ico, tga, togif, toppm, topng, toico \- view
|
|||
.I file ...
|
||||
]
|
||||
.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 -c
|
||||
|
@ -126,11 +146,12 @@ and
|
|||
read files in the corresponding formats and, by default, display
|
||||
them in the current window; options cause them instead to convert the images
|
||||
to Plan 9 image format and write them to standard output.
|
||||
.IR Togif ,
|
||||
.IR Tojpg ,
|
||||
.IR togif ,
|
||||
.IR toppm ,
|
||||
and
|
||||
.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
|
||||
The default behavior of
|
||||
.IR jpg ,
|
||||
|
@ -205,12 +226,13 @@ The input is a motion JPEG file, with multiple images representing frames of the
|
|||
.PD
|
||||
.PP
|
||||
The
|
||||
.IR tojpg ,
|
||||
.IR togif
|
||||
and
|
||||
.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.
|
||||
Both accept an option
|
||||
They all accept an option
|
||||
.B -c
|
||||
to set the comment field of the resulting file.
|
||||
The
|
||||
|
@ -219,6 +241,20 @@ option makes
|
|||
.I toppm
|
||||
output raw 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,
|
||||
.I togif
|
||||
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.
|
||||
.SH SOURCE
|
||||
.B /sys/src/cmd/jpg
|
||||
.br
|
||||
.B /rc/bin/togeordi
|
||||
.SH "SEE ALSO"
|
||||
.IR page (1),
|
||||
.IR image (6).
|
||||
.br
|
||||
.B http://www.w3.org/Graphics/JPEG/jfif3.pdf
|
||||
.br
|
||||
.B http://www.w3.org/Graphics/JPEG/itu-t81.pdf
|
||||
.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
|
||||
.br
|
||||
.B http://www.w3.org/TR/2003/REC-PNG-20031110
|
||||
|
|
|
@ -77,3 +77,6 @@ Image* multichan(Image*);
|
|||
Memimage* memmultichan(Memimage*);
|
||||
|
||||
char* memwritepng(Biobuf*, Memimage*, ImageInfo*);
|
||||
|
||||
char* writejpg(Biobuf*, Image*, char*, int, int);
|
||||
char* memwritejpg(Biobuf*, Memimage*, char*, int, int);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
TARG=jpg\
|
||||
TARG=\
|
||||
jpg\
|
||||
tojpg\
|
||||
gif\
|
||||
togif\
|
||||
ppm\
|
||||
|
@ -46,6 +48,7 @@ c=`{sed -n 's/^O=//p' /$cputype/mkfile}
|
|||
|
||||
$O.tga: $IMFILES readtga.$O tga.$O
|
||||
$O.jpg: $IMFILES readjpg.$O jpg.$O
|
||||
$O.tojpg: writejpg.$O multichan.$O tojpg.$O
|
||||
$O.gif: $IMFILES readgif.$O gif.$O
|
||||
$O.togif: writegif.$O onechan.$O togif.$O torgbv.$O
|
||||
$O.ppm: $IMFILES readppm.$O ppm.$O
|
||||
|
|
70
sys/src/cmd/jpg/tojpg.c
Normal file
70
sys/src/cmd/jpg/tojpg.c
Normal 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
912
sys/src/cmd/jpg/writejpg.c
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue