Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <ctype.h>
4
#include <bio.h>
5
#include <flate.h>
6
#include <draw.h>
7
#include "imagefile.h"
8
 
9
int debug;
10
 
11
enum
12
{
13
	IDATSIZE = 1000000,
14
 
15
	/* filtering algorithms */
16
	FilterNone =	0,	/* new[x][y] = buf[x][y] */
17
	FilterSub =	1,	/* new[x][y] = buf[x][y] + new[x-1][y] */ 
18
	FilterUp =	2,	/* new[x][y] = buf[x][y] + new[x][y-1] */ 
19
	FilterAvg =	3,	/* new[x][y] = buf[x][y] + (new[x-1][y]+new[x][y-1])/2 */ 
20
	FilterPaeth =	4,	/* new[x][y] = buf[x][y] + paeth(new[x-1][y],new[x][y-1],new[x-1][y-1]) */
21
	FilterLast =	5,
22
 
23
	PropertyBit = 1<<5,
24
};
25
 
26
typedef struct ZlibR ZlibR;
27
typedef struct ZlibW ZlibW;
28
 
29
struct ZlibW
30
{
31
	uchar *data;		/* Rawimage data */
32
	int ndata;
33
	int noutchan;
34
	int chandesc;
35
	int nchan;
36
 
37
	uchar *scan;		/* new scanline */
38
	uchar *lastscan;	/* previous scan line */
39
	int scanlen;		/* scan line length */
40
	int scanpos;		/* scan position */
41
 
42
	int dx;			/* width of image */
43
	int dy;			/* height of image */
44
	int bpc;			/* bits per channel (per pixel) */
45
	int y;				/* current scan line */
46
	int pass;			/* adam7 pass#; 0 means no adam7 */
47
	uchar palette[3*256];	/* color palette */
48
	int palsize;		/* number of palette entries */
49
};
50
 
51
struct ZlibR
52
{
53
	Biobuf *io;		/* input buffer */
54
	uchar *buf;		/* malloc'ed staging buffer */
55
	uchar *p;			/* next byte to decompress */
56
	uchar *e;			/* end of buffer */
57
	ZlibW *w;
58
};
59
 
60
static ulong *crctab;
61
static uchar PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'};
62
 
63
static ulong
64
get4(uchar *a)
65
{
66
	return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
67
}
68
 
69
static
70
void
71
pnginit(void)
72
{
73
	static int inited;
74
 
75
	if(inited)
76
		return;
77
	inited = 1;
78
	crctab = mkcrctab(0xedb88320);
79
	if(crctab == nil)
80
		sysfatal("mkcrctab error");
81
	inflateinit();
82
}
83
 
84
static
85
void*
86
pngmalloc(ulong n, int clear)
87
{
88
	void *p;
89
 
90
	p = mallocz(n, clear);
91
	if(p == nil)
92
		sysfatal("malloc: %r");
93
	return p;
94
}
95
 
96
static int
97
getchunk(Biobuf *b, char *type, uchar *d, int m)
98
{
99
	uchar buf[8];
100
	ulong crc = 0, crc2;
101
	int n, nr;
102
 
103
	if(Bread(b, buf, 8) != 8)
104
		return -1;
105
	n = get4(buf);
106
	memmove(type, buf+4, 4);
107
	type[4] = 0;
108
	if(n > m)
109
		sysfatal("getchunk needed %d, had %d", n, m);
110
	nr = Bread(b, d, n);
111
	if(nr != n)
112
		sysfatal("getchunk read %d, expected %d", nr, n);
113
	crc = blockcrc(crctab, crc, type, 4);
114
	crc = blockcrc(crctab, crc, d, n);
115
	if(Bread(b, buf, 4) != 4)
116
		sysfatal("getchunk tlr failed");
117
	crc2 = get4(buf);
118
	if(crc != crc2)
119
		sysfatal("getchunk crc failed");
120
	return n;
121
}
122
 
123
static int
124
zread(void *va)
125
{
126
	ZlibR *z = va;
127
	char type[5];
128
	int n;
129
 
130
	if(z->p >= z->e){
131
	Again:
132
		z->p = z->buf;
133
		z->e = z->p;
134
		n = getchunk(z->io, type, z->p, IDATSIZE);
135
		if(n < 0 || strcmp(type, "IEND") == 0)
136
			return -1;
137
		z->e = z->p + n;
138
		if(!strcmp(type,"PLTE")){
139
			if(n < 3 || n > 3*256 || n%3)
140
				sysfatal("invalid PLTE chunk len %d", n);
141
			memcpy(z->w->palette, z->p, n);
142
			z->w->palsize = 256;
143
			goto Again;
144
		}
145
		if(type[0] & PropertyBit)
146
			goto Again;  /* skip auxiliary chunks fornow */
147
		if(strcmp(type,"IDAT")){
148
			sysfatal("unrecognized mandatory chunk %s", type);
149
			goto Again;
150
		}
151
	}
152
	return *z->p++;
153
}
154
 
155
static uchar 
156
paeth(uchar a, uchar b, uchar c)
157
{
158
	int p, pa, pb, pc;
159
 
160
	p = a + b - c;
161
	pa = abs(p - a);
162
	pb = abs(p - b);
163
	pc = abs(p - c);
164
 
165
	if(pa <= pb && pa <= pc)
166
		return a;
167
	else if(pb <= pc)
168
		return b;
169
	return c;
170
}
171
 
172
static void
173
unfilter(int alg, uchar *buf, uchar *up, int len, int bypp)
174
{
175
	int i;
176
 
177
	switch(alg){
178
	case FilterNone:
179
		break;
180
 
181
	case FilterSub:
182
		for(i = bypp; i < len; ++i)
183
			buf[i] += buf[i-bypp];
184
		break;
185
 
186
	case FilterUp:
187
		for(i = 0; i < len; ++i)
188
			buf[i] += up[i];
189
		break;
190
 
191
	case FilterAvg:
192
		for(i = 0; i < bypp; ++i)
193
			buf[i] += (0+up[i])/2;
194
		for(; i < len; ++i)
195
			buf[i] += (buf[i-bypp]+up[i])/2;
196
		break;
197
 
198
	case FilterPaeth:
199
		for(i = 0; i < bypp; ++i)
200
			buf[i] += paeth(0, up[i], 0);
201
		for(; i < len; ++i)
202
			buf[i] += paeth(buf[i-bypp], up[i], up[i-bypp]);
203
		break;
204
 
205
	default:
206
		sysfatal("unknown filtering scheme %d\n", alg);
207
	}
208
}
209
 
210
struct {
211
	int x;
212
	int y;
213
	int dx;
214
	int dy;
215
} adam7[] = {
216
	{0,0,1,1},	/* eve alone */
217
	{0,0,8,8},	/* pass 1 */
218
	{4,0,8,8},	/* pass 2 */
219
	{0,4,4,8},	/* pass 3 */
220
	{2,0,4,4},	/* pass 4 */
221
	{0,2,2,4},	/* pass 5 */
222
	{1,0,2,2},	/* pass 6 */
223
	{0,1,1,2},	/* pass 7 */
224
};
225
 
226
static void
227
scan(int len, ZlibW *z)
228
{
229
	int chan, i, j, nbit, off, val;
230
	uchar pixel[4], *p, *w;
231
 
232
	unfilter(z->scan[0], z->scan+1, z->lastscan+1, len-1, (z->nchan*z->bpc+7)/8);
233
 
234
	/*
235
	 * loop over raw bits extracting pixel values and converting to 8-bit
236
	 */
237
	nbit = 0;
238
	chan = 0;
239
	val = 0;
240
	off = z->y*z->dx + adam7[z->pass].x;
241
	w = z->data + z->noutchan*off;
242
	p = z->scan+1;	/* skip alg byte */
243
	len--;
244
	for(i=0; i<len*8; i++){
245
		val <<= 1;
246
		if(p[i>>3] & (1<<(7-(i&7))))
247
			val++;
248
		if(++nbit == z->bpc){
249
			/* finished the value */
250
			pixel[chan++] = (val*255)/((1<<z->bpc)-1);
251
			val = 0;
252
			nbit = 0;
253
			if(chan == z->nchan){
254
				/* finished the pixel */
255
				if(off < z->dx*z->dy){
256
					if(z->nchan < 3 && z->palsize){
257
						j = pixel[0];
258
						if(z->bpc < 8)
259
							j >>= 8-z->bpc;
260
						if(j >= z->palsize)
261
							sysfatal("index %d >= palette size %d", j, z->palsize);
262
						pixel[3] = pixel[1];	/* alpha */
263
						pixel[0] = z->palette[3*j];
264
						pixel[1] = z->palette[3*j+1];
265
						pixel[2] = z->palette[3*j+2];
266
					}
267
					switch(z->chandesc){
268
					case CYA16:
269
					//	print("%.2x%.2x ", pixel[0], pixel[1]);
270
						*w++ = pixel[1];
271
						*w++ += (pixel[0]*pixel[1])/255;
272
						break;
273
					case CRGBA32:
274
					//	print("%.2x%.2x%.2x%.2x ", pixel[0], pixel[1], pixel[2], pixel[3]);
275
						*w++ += pixel[3];
276
						*w++ += (pixel[2]*pixel[3])/255;
277
						*w++ += (pixel[1]*pixel[3])/255;
278
						*w++ += (pixel[0]*pixel[3])/255;
279
						break;
280
					case CRGB24:
281
						*w++ = pixel[2];
282
						*w++ = pixel[1];
283
					case CY:
284
						*w++ = pixel[0];
285
						break;
286
					}
287
					w += (adam7[z->pass].dx-1)*z->noutchan;
288
				}
289
				off += adam7[z->pass].dx;
290
				if(off >= (z->y+1)*z->dx){
291
					/* finished the line */
292
					return;
293
				}
294
				chan = 0;
295
			}
296
		}
297
	}
298
	sysfatal("scan line too short");
299
}
300
 
301
static int
302
scanbytes(ZlibW *z)
303
{
304
	int bits, n, adx, dx;
305
 
306
	if(adam7[z->pass].y >= z->dy || adam7[z->pass].x >= z->dx)
307
		return 0;
308
	adx = adam7[z->pass].dx;
309
	dx = z->dx - adam7[z->pass].x;
310
	if(dx <= 0)
311
		n = 1;
312
	else
313
		n = (dx+adx-1)/adx;
314
	if(n != 1 + (z->dx - (adam7[z->pass].x+1)) / adam7[z->pass].dx){
315
		print("%d/%d != 1+(%d-1)/%d = %d\n",
316
			z->dx - adam7[z->pass].x - 1 + adx, adx,
317
			z->dx - (adam7[z->pass].x+1), adam7[z->pass].dx,
318
			1 + (z->dx - (adam7[z->pass].x+1)) / adam7[z->pass].dx);
319
	}
320
	bits = n*z->bpc*z->nchan;
321
	return 1 + (bits+7)/8;
322
}
323
 
324
static int
325
nextpass(ZlibW *z)
326
{
327
	int len;
328
 
329
	memset(z->lastscan, 0, z->scanlen);
330
	do{
331
		z->pass = (z->pass+1)%8;
332
		z->y = adam7[z->pass].y;
333
		len = scanbytes(z);
334
	}while(len < 2);
335
	return len;
336
}
337
 
338
static int
339
zwrite(void *vz, void *vbuf, int n)
340
{
341
	int oldn, m, len;
342
	uchar *buf, *t;
343
	ZlibW *z;
344
 
345
	z = vz;
346
	buf = vbuf;
347
	oldn = n;
348
 
349
	len = scanbytes(z);
350
	if(len < 2)
351
		len = nextpass(z);
352
 
353
	while(n > 0){
354
		m = len - z->scanpos;
355
		if(m > n){
356
			/* save final partial line */
357
			memmove(z->scan+z->scanpos, buf, n);
358
			z->scanpos += n;
359
			break;
360
		}
361
 
362
		/* fill line */
363
		memmove(z->scan+z->scanpos, buf, m);
364
		buf += m;
365
		n -= m;
366
 
367
		/* process line */
368
		scan(len, z);
369
		t = z->scan;
370
		z->scan = z->lastscan;
371
		z->lastscan = t;
372
 
373
		z->scanpos = 0;
374
		z->y += adam7[z->pass].dy;
375
		if(z->y >= z->dy)
376
			len = nextpass(z);
377
	}
378
	return oldn;
379
}
380
 
381
static Rawimage*
382
readslave(Biobuf *b)
383
{
384
	char type[5];
385
	int bpc, colorfmt, dx, dy, err, n, nchan, nout, useadam7;
386
	uchar *buf, *h;
387
	Rawimage *image;
388
	ZlibR zr;
389
	ZlibW zw;
390
 
391
	buf = pngmalloc(IDATSIZE, 0);
392
	if(Bread(b, buf, sizeof PNGmagic) != sizeof PNGmagic ||
393
	    memcmp(PNGmagic, buf, sizeof PNGmagic) != 0)
394
		sysfatal("bad PNGmagic");
395
 
396
	n = getchunk(b, type, buf, IDATSIZE);
397
	if(n < 13 || strcmp(type,"IHDR") != 0)
398
		sysfatal("missing IHDR chunk");
399
	h = buf;
400
	dx = get4(h);
401
	h += 4;
402
	dy = get4(h);
403
	h += 4;
404
	if(dx <= 0 || dy <= 0)
405
		sysfatal("impossible image size %dx%d", dx, dy);
406
	if(debug)
407
		fprint(2, "readpng %dx%d\n", dx, dy);
408
 
409
	bpc = *h++;
410
	colorfmt = *h++;
411
	nchan = 0;
412
	if(*h++ != 0)
413
		sysfatal("only deflate supported for now [%d]", h[-1]);
414
	if(*h++ != FilterNone)
415
		sysfatal("only FilterNone supported for now [%d]", h[-1]);
416
	useadam7 = *h++;
417
	USED(h);
418
 
419
	image = pngmalloc(sizeof(Rawimage), 1);
420
	image->r = Rect(0, 0, dx, dy);
421
	nout = 0;
422
	switch(colorfmt){
423
	case 0:	/* grey */
424
		image->nchans = 1;
425
		image->chandesc = CY;
426
		nout = 1;
427
		nchan = 1;
428
		break;
429
	case 2:	/* rgb */
430
		image->nchans = 1;
431
		image->chandesc = CRGB24;
432
		nout = 3;
433
		nchan = 3;
434
		break;
435
	case 3: /* indexed rgb with PLTE */
436
		image->nchans = 1;
437
		image->chandesc = CRGB24;
438
		nout = 3;
439
		nchan = 1;
440
		break;
441
	case 4:	/* grey+alpha */
442
		image->nchans = 1;
443
		image->chandesc = CYA16;
444
		nout = 2;
445
		nchan = 2;
446
		break;
447
	case 6:	/* rgb+alpha */
448
		image->nchans = 1;
449
		image->chandesc = CRGBA32;
450
		nout = 4;
451
		nchan = 4;
452
		break;
453
	default:
454
		sysfatal("unsupported color scheme %d", h[-1]);
455
	}
456
	image->chanlen = dx*dy*nout;
457
	image->chans[0] = pngmalloc(image->chanlen, 0);
458
	memset(image->chans[0], 0, image->chanlen);
459
 
460
	memset(&zr, 0, sizeof zr);
461
	zr.w = &zw;
462
	zr.io = b;
463
	zr.buf = buf;
464
 
465
	memset(&zw, 0, sizeof zw);
466
	if(useadam7)
467
		zw.pass = 1;
468
	zw.data = image->chans[0];
469
	zw.ndata = image->chanlen;
470
	zw.chandesc = image->chandesc;
471
	zw.noutchan = nout;
472
 
473
	zw.dx = dx;
474
	zw.dy = dy;
475
	zw.scanlen = (nchan*dx*bpc+7)/8+1;
476
	zw.scan = pngmalloc(zw.scanlen, 1);
477
	zw.lastscan = pngmalloc(zw.scanlen, 1);
478
	zw.nchan = nchan;
479
	zw.bpc = bpc;
480
 
481
	err = inflatezlib(&zw, zwrite, &zr, zread);
482
 
483
	if(err)
484
		sysfatal("inflatezlib %s\n", flateerr(err));
485
 
486
	free(buf);
487
	free(zw.scan);
488
	free(zw.lastscan);
489
	return image;
490
}
491
 
492
Rawimage**
493
Breadpng(Biobuf *b, int colorspace)
494
{
495
	Rawimage **array, *r;
496
 
497
	if(colorspace != CRGB){
498
		werrstr("ReadPNG: unknown color space %d", colorspace);
499
		return nil;
500
	}
501
	pnginit();
502
	array = malloc(2*sizeof(*array));
503
	if(array==nil)
504
		return nil;
505
	r = readslave(b);
506
	array[0] = r;
507
	array[1] = nil;
508
	return array;
509
}
510
 
511
Rawimage**
512
readpng(int fd, int colorspace)
513
{
514
	Biobuf b;
515
	Rawimage **a;
516
 
517
	if(Binit(&b, fd, OREAD) < 0)
518
		return nil;
519
	a = Breadpng(&b, colorspace);
520
	Bterm(&b);
521
	return a;
522
}