Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * See PNG 1.2 spec, also RFC 2083.
3
 */
4
#include <u.h>
5
#include <libc.h>
6
#include <draw.h>
7
#include <memdraw.h>
8
#include <ctype.h>
9
#include <bio.h>
10
#include <flate.h>
11
#include "imagefile.h"
12
 
13
enum
14
{
15
	IDATSIZE = 	20000,
16
	FilterNone =	0,
17
};
18
 
19
typedef struct ZlibR ZlibR;
20
typedef struct ZlibW ZlibW;
21
 
22
struct ZlibR
23
{
24
	uchar *data;
25
	int width;
26
	int dx;
27
	int dy;
28
	int x;
29
	int y;
30
	int pixwid;
31
};
32
 
33
struct ZlibW
34
{
35
	Biobuf *io;
36
	uchar *buf;
37
	uchar *b;
38
	uchar *e;
39
};
40
 
41
static ulong *crctab;
42
static uchar PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'};
43
 
44
static void
45
put4(uchar *a, ulong v)
46
{
47
	a[0] = v>>24;
48
	a[1] = v>>16;
49
	a[2] = v>>8;
50
	a[3] = v;
51
}
52
 
53
static void
54
chunk(Biobuf *bo, char *type, uchar *d, int n)
55
{
56
	uchar buf[4];
57
	ulong crc = 0;
58
 
59
	if(strlen(type) != 4)
60
		return;
61
	put4(buf, n);
62
	Bwrite(bo, buf, 4);
63
	Bwrite(bo, type, 4);
64
	Bwrite(bo, d, n);
65
	crc = blockcrc(crctab, crc, type, 4);
66
	crc = blockcrc(crctab, crc, d, n);
67
	put4(buf, crc);
68
	Bwrite(bo, buf, 4);
69
}
70
 
71
static int
72
zread(void *va, void *buf, int n)
73
{
74
	int a, i, pixels, pixwid;
75
	uchar *b, *e, *img;
76
	ZlibR *z;
77
 
78
	z = va;
79
	pixwid = z->pixwid;
80
	b = buf;
81
	e = b+n;
82
	while(b+pixwid < e){	/* one less for filter alg byte */
83
		if(z->y >= z->dy)
84
			break;
85
		if(z->x == 0)
86
			*b++ = FilterNone;
87
		pixels = (e-b)/pixwid;
88
		if(pixels > z->dx - z->x)
89
			pixels = z->dx - z->x;
90
		img = z->data + z->width*z->y + pixwid*z->x;
91
		memmove(b, img, pixwid*pixels);
92
		if(pixwid == 4){
93
			/*
94
			 * Convert to non-premultiplied alpha.
95
			 */
96
			for(i=0; i<pixels; i++, b+=4){
97
				a = b[3];
98
				if(a == 0)
99
					b[0] = b[1] = b[2] = 0;
100
				else if(a != 255){
101
					if(b[0] >= a)
102
						b[0] = a;
103
					b[0] = (b[0]*255)/a;
104
					if(b[1] >= a)
105
						b[1] = a;
106
					b[1] = (b[1]*255)/a;
107
					if(b[2] >= a)
108
						b[2] = a;
109
					b[2] = (b[2]*255)/a;
110
				}
111
			}
112
		}else	
113
			b += pixwid*pixels;
114
 
115
		z->x += pixels;
116
		if(z->x >= z->dx){
117
			z->x = 0;
118
			z->y++;
119
		}
120
	}
121
	return b - (uchar*)buf;
122
}
123
 
124
static void
125
IDAT(ZlibW *z)
126
{
127
	chunk(z->io, "IDAT", z->buf, z->b - z->buf);
128
	z->b = z->buf;
129
}
130
 
131
static int
132
zwrite(void *va, void *buf, int n)
133
{
134
	int m;
135
	uchar *b, *e;
136
	ZlibW *z;
137
 
138
	z = va;
139
	b = buf;
140
	e = b+n;
141
 
142
	while(b < e){
143
		m = z->e - z->b;
144
		if(m > e - b)
145
			m = e - b;
146
		memmove(z->b, b, m);
147
		z->b += m;
148
		b += m;
149
		if(z->b >= z->e)
150
			IDAT(z);
151
	}
152
	return n;
153
}
154
 
155
static Memimage*
156
memRGBA(Memimage *i)
157
{
158
	Memimage *ni;
159
	char buf[32];
160
	ulong dst;
161
 
162
	/*
163
	 * [A]BGR because we want R,G,B,[A] in big-endian order.  Sigh.
164
	 */
165
	chantostr(buf, i->chan);
166
	if(strchr(buf, 'a'))
167
		dst = ABGR32;
168
	else
169
		dst = BGR24;
170
 
171
	if(i->chan == dst)
172
		return i;
173
 
174
	ni = allocmemimage(i->r, dst);
175
	if(ni == nil)
176
		return ni;
177
	memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
178
	return ni;
179
}
180
 
181
char*
182
memwritepng(Biobuf *io, Memimage *m, ImageInfo *II)
183
{
184
	int err, n;
185
	uchar buf[200], *h;
186
	ulong vgamma;
187
	Tm *tm;
188
	Memimage *rgb;
189
	ZlibR zr;
190
	ZlibW zw;
191
 
192
	crctab = mkcrctab(0xedb88320);
193
	if(crctab == nil)
194
		sysfatal("mkcrctab error");
195
	deflateinit();
196
 
197
	rgb = memRGBA(m);
198
	if(rgb == nil)
199
		return "allocmemimage nil";
200
 
201
	Bwrite(io, PNGmagic, sizeof PNGmagic);
202
 
203
	/* IHDR chunk */
204
	h = buf;
205
	put4(h, Dx(m->r)); h += 4;
206
	put4(h, Dy(m->r)); h += 4;
207
	*h++ = 8;	/* 8 bits per channel */
208
	if(rgb->chan == BGR24)
209
		*h++ = 2;		/* RGB */
210
	else
211
		*h++ = 6;		/* RGBA */
212
	*h++ = 0;	/* compression - deflate */
213
	*h++ = 0;	/* filter - none */
214
	*h++ = 0;	/* interlace - none */
215
	chunk(io, "IHDR", buf, h-buf);
216
 
217
	/* time - using now is suspect */
218
	tm = gmtime(time(0));
219
	h = buf;
220
	*h++ = (tm->year + 1900)>>8;
221
	*h++ = (tm->year + 1900)&0xff;
222
	*h++ = tm->mon + 1;
223
	*h++ = tm->mday;
224
	*h++ = tm->hour;
225
	*h++ = tm->min;
226
	*h++ = tm->sec;
227
	chunk(io, "tIME", buf, h-buf);
228
 
229
	/* gamma */
230
	if(II->fields_set & II_GAMMA){
231
		vgamma = II->gamma*100000;
232
		put4(buf, vgamma);
233
		chunk(io, "gAMA", buf, 4);
234
	}
235
 
236
	/* comment */
237
	if(II->fields_set & II_COMMENT){
238
		strncpy((char*)buf, "Comment", sizeof buf);
239
		n = strlen((char*)buf)+1; // leave null between Comment and text
240
		strncpy((char*)(buf+n), II->comment, sizeof buf - n);
241
		chunk(io, "tEXt", buf, n+strlen((char*)buf+n));
242
	}
243
 
244
	/* image data */
245
	zr.dx = Dx(m->r);
246
	zr.dy = Dy(m->r);
247
	zr.width = rgb->width * sizeof(ulong);
248
	zr.data = rgb->data->bdata;
249
	zr.x = 0;
250
	zr.y = 0;
251
	zr.pixwid = chantodepth(rgb->chan)/8;
252
	zw.io = io;
253
	zw.buf = malloc(IDATSIZE);
254
	if(zw.buf == nil)
255
		sysfatal("malloc: %r");
256
	zw.b = zw.buf;
257
	zw.e = zw.b + IDATSIZE;
258
	if((err=deflatezlib(&zw, zwrite, &zr, zread, 6, 0)) < 0)
259
		sysfatal("deflatezlib %s", flateerr(err));
260
	if(zw.b > zw.buf)
261
		IDAT(&zw);
262
	free(zw.buf);
263
	chunk(io, "IEND", nil, 0);
264
	if(m != rgb)
265
		freememimage(rgb);
266
	return nil;
267
}