1 /*
2  * This file is part of the AdvanceSCAN project.
3  *
4  * Copyright (C) 2002 Andrea Mazzoleni
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 
21 #include "portable.h"
22 
23 #include "lib/endianrw.h"
24 #include "lib/mng.h"
25 
26 #include "pngex.h"
27 
28 #include <iostream>
29 #include <iomanip>
30 
31 using namespace std;
32 
png_compress(shrink_t level,data_ptr & out_ptr,unsigned & out_size,const unsigned char * img_ptr,unsigned img_scanline,unsigned img_pixel,unsigned x,unsigned y,unsigned dx,unsigned dy)33 void png_compress(shrink_t level, data_ptr& out_ptr, unsigned& out_size, const unsigned char* img_ptr, unsigned img_scanline, unsigned img_pixel, unsigned x, unsigned y, unsigned dx, unsigned dy)
34 {
35 	data_ptr fil_ptr;
36 	unsigned fil_size;
37 	unsigned fil_scanline;
38 	data_ptr z_ptr;
39 	unsigned z_size;
40 	unsigned i;
41 	unsigned char* p0;
42 
43 	fil_scanline = dx * img_pixel + 1;
44 	fil_size = dy * fil_scanline;
45 	z_size = oversize_zlib(fil_size);
46 
47 	fil_ptr = data_alloc(fil_size);
48 	z_ptr = data_alloc(z_size);
49 
50 	p0 = fil_ptr;
51 
52 	for(i=0;i<dy;++i) {
53 		const unsigned char* p1 = &img_ptr[x * img_pixel + (i+y) * img_scanline];
54 		*p0++ = 0;
55 		memcpy(p0, p1, dx * img_pixel);
56 		p0 += dx * img_pixel;
57 	}
58 
59 	assert(p0 == fil_ptr + fil_size);
60 
61 	if (!compress_zlib(level, z_ptr, z_size, fil_ptr, fil_size)) {
62 		throw error() << "Failed compression";
63 	}
64 
65 	out_ptr = z_ptr;
66 	out_size = z_size;
67 }
68 
png_compress_delta(shrink_t level,data_ptr & out_ptr,unsigned & out_size,const unsigned char * img_ptr,unsigned img_scanline,unsigned img_pixel,const unsigned char * prev_ptr,unsigned prev_scanline,unsigned x,unsigned y,unsigned dx,unsigned dy)69 void png_compress_delta(shrink_t level, data_ptr& out_ptr, unsigned& out_size, const unsigned char* img_ptr, unsigned img_scanline, unsigned img_pixel, const unsigned char* prev_ptr, unsigned prev_scanline, unsigned x, unsigned y, unsigned dx, unsigned dy)
70 {
71 	data_ptr fil_ptr;
72 	unsigned fil_size;
73 	unsigned fil_scanline;
74 	data_ptr z_ptr;
75 	unsigned z_size;
76 	unsigned i;
77 	unsigned char* p0;
78 
79 	fil_scanline = dx * img_pixel + 1;
80 	fil_size = dy * fil_scanline;
81 	z_size = oversize_zlib(fil_size);
82 
83 	fil_ptr = data_alloc(fil_size);
84 	z_ptr = data_alloc(z_size);
85 
86 	p0 = fil_ptr;
87 
88 	for(i=0;i<dy;++i) {
89 		unsigned j;
90 		const unsigned char* p1 = &img_ptr[x * img_pixel + (i+y) * img_scanline];
91 		const unsigned char* p2 = &prev_ptr[x * img_pixel + (i+y) * prev_scanline];
92 
93 		*p0++ = 0;
94 		for(j=0;j<dx*img_pixel;++j)
95 			*p0++ = *p1++ - *p2++;
96 	}
97 
98 	assert(p0 == fil_ptr + fil_size);
99 
100 	if (!compress_zlib(level, z_ptr, z_size, fil_ptr, fil_size)) {
101 		throw error() << "Failed compression";
102 	}
103 
104 	out_ptr = z_ptr;
105 	out_size = z_size;
106 }
107 
png_compress_palette_delta(data_ptr & out_ptr,unsigned & out_size,const unsigned char * pal_ptr,unsigned pal_size,const unsigned char * prev_ptr,unsigned prev_size)108 void png_compress_palette_delta(data_ptr& out_ptr, unsigned& out_size, const unsigned char* pal_ptr, unsigned pal_size, const unsigned char* prev_ptr, unsigned prev_size)
109 {
110 	unsigned i;
111 	unsigned char* dst_ptr;
112 	unsigned dst_size;
113 
114 	dst_ptr = data_alloc(pal_size * 2);
115 	dst_size = 0;
116 
117 	dst_ptr[dst_size++] = 0; /* replacement */
118 
119 	i = 0;
120 	while (i<pal_size) {
121 		unsigned j;
122 
123 		while (i < pal_size && (i < prev_size && prev_ptr[i] == pal_ptr[i] && prev_ptr[i+1] == pal_ptr[i+1] && prev_ptr[i+2] == pal_ptr[i+2]))
124 			i += 3;
125 
126 		if (i == pal_size)
127 			break;
128 
129 		j = i + 3;
130 
131 		while (j < pal_size && (j >= prev_size || prev_ptr[j] != pal_ptr[j] || prev_ptr[j+1] != pal_ptr[j+1] || prev_ptr[j+2] != pal_ptr[j+2]))
132 			j += 3;
133 
134 		dst_ptr[dst_size++] = i / 3; /* first index */
135 		dst_ptr[dst_size++] = (j / 3) - 1; /* last index */
136 
137 		while (i < j) {
138 			dst_ptr[dst_size++] = pal_ptr[i++];
139 			dst_ptr[dst_size++] = pal_ptr[i++];
140 			dst_ptr[dst_size++] = pal_ptr[i++];
141 		}
142 	}
143 
144 	if (dst_size == 1) {
145 		out_ptr = 0;
146 		out_size = 0;
147 		free(dst_ptr);
148 	} else {
149 		out_ptr = dst_ptr;
150 		out_size = dst_size;
151 	}
152 }
153 
png_print_chunk(unsigned type,unsigned char * data,unsigned size)154 void png_print_chunk(unsigned type, unsigned char* data, unsigned size)
155 {
156 	char tag[5];
157 	unsigned i;
158 
159 	be_uint32_write(tag, type);
160 	tag[4] = 0;
161 
162 	cout << tag << setw(8) << size;
163 
164 	switch (type) {
165 		case ADV_MNG_CN_MHDR :
166 			if (size < 28) {
167 				cout << " invalid chunk size";
168 				break;
169 			}
170 			cout << " width:" << be_uint32_read(data+0) << " height:" << be_uint32_read(data+4) << " frequency:" << be_uint32_read(data+8);
171 			cout << " simplicity:" << be_uint32_read(data+24);
172 			cout << "(bit";
173 			for(i=0;i<32;++i) {
174 				if (be_uint32_read(data+24) & (1 << i)) {
175 					cout << "," << i;
176 				}
177 			}
178 			cout << ")";
179 		break;
180 		case ADV_MNG_CN_DHDR :
181 			if (size < 4) {
182 				cout << " invalid chunk size";
183 				break;
184 			}
185 			cout << " id:" << be_uint16_read(data+0);
186 			switch (data[2]) {
187 				case 0 : cout << " img:unspecified"; break;
188 				case 1 : cout << " img:png"; break;
189 				case 2 : cout << " img:jng"; break;
190 				default: cout << " img:?"; break;
191 			}
192 			switch (data[3]) {
193 				case 0 : cout << " delta:entire_replacement"; break;
194 				case 1 : cout << " delta:block_addition"; break;
195 				case 2 : cout << " delta:block_alpha_addition"; break;
196 				case 3 : cout << " delta:block_color_addition"; break;
197 				case 4 : cout << " delta:block_replacement"; break;
198 				case 5 : cout << " delta:block_alpha_replacement"; break;
199 				case 6 : cout << " delta:block_color_replacement"; break;
200 				case 7 : cout << " delta:no_change"; break;
201 				default: cout << " delta:?"; break;
202 			}
203 			if (size >= 12) {
204 				cout << " width:" << be_uint32_read(data + 4) << " height:" << be_uint32_read(data + 8);
205 			}
206 			if (size >= 20) {
207 				cout << " x:" << (int)be_uint32_read(data + 12) << " y:" << (int)be_uint32_read(data + 16);
208 			}
209 		break;
210 		case ADV_MNG_CN_FRAM :
211 			if (size >= 1) {
212 				cout << " mode:" << (unsigned)data[0];
213 			}
214 			if (size > 1) {
215 				i = 1;
216 				while (i < size && data[i]!=0)
217 					++i;
218 				cout << " len:" << i-1;
219 
220 				if (size >= i+2) {
221 					cout << " delay_mode:" << (unsigned)data[i+1];
222 				}
223 
224 				if (size >= i+3) {
225 					cout << " timeout:" << (unsigned)data[i+2];
226 				}
227 
228 				if (size >= i+4) {
229 					cout << " clip:" << (unsigned)data[i+3];
230 				}
231 
232 				if (size >= i+5) {
233 					cout << " syncid:" << (unsigned)data[i+4];
234 				}
235 
236 				if (size >= i+9) {
237 					cout << " tick:" << be_uint32_read(data+i+5);
238 				}
239 
240 				if (size >= i+13) {
241 					cout << " timeout:" << be_uint32_read(data+i+9);
242 				}
243 
244 				if (size >= i+14) {
245 					cout << " dt:" << (unsigned)data[i+10];
246 				}
247 
248 				if (size >= i+15) {
249 					cout << " ...";
250 				}
251 			}
252 			break;
253 		case ADV_MNG_CN_DEFI :
254 			if (size < 2) {
255 				cout << " invalid chunk size";
256 				break;
257 			}
258 			cout << " id:" << be_uint16_read(data+0);
259 			if (size >= 3) {
260 				switch (data[2]) {
261 					case 0 : cout << " visible:yes"; break;
262 					case 1 : cout << " visible:no"; break;
263 					default : cout << " visible:?"; break;
264 				}
265 			}
266 			if (size >= 4) {
267 				switch (data[3]) {
268 					case 0 : cout << " concrete:abstract"; break;
269 					case 1 : cout << " concrete:concrete"; break;
270 					default : cout << " concrete:?"; break;
271 				}
272 			}
273 			if (size >= 12) {
274 				cout << " x:" << (int)be_uint32_read(data + 4) << " y:" << (int)be_uint32_read(data + 8);
275 			}
276 			if (size >= 28) {
277 				cout << " left:" << be_uint32_read(data + 12) << " right:" << be_uint32_read(data + 16) << " top:" << be_uint32_read(data + 20) << " bottom:" << be_uint32_read(data + 24);
278 			}
279 		break;
280 		case ADV_MNG_CN_MOVE :
281 			if (size < 13) {
282 				cout << " invalid chunk size";
283 				break;
284 			}
285 			cout << " id_from:" << be_uint16_read(data+0) << " id_to:" << be_uint16_read(data+2);
286 			switch (data[4]) {
287 				case 0 : cout << " type:replace"; break;
288 				case 1 : cout << " type:add"; break;
289 				default : cout << " type:?"; break;
290 			}
291 			cout << " x:" << (int)be_uint32_read(data + 5) << " y:" << (int)be_uint32_read(data + 9);
292 			break;
293 		case ADV_MNG_CN_PPLT :
294 			if (size < 1) {
295 				cout << " invalid chunk size";
296 				break;
297 			}
298 			switch (data[0]) {
299 				case 0 : cout << " type:replacement_rgb"; break;
300 				case 1 : cout << " type:delta_rgb"; break;
301 				case 2 : cout << " type:replacement_alpha"; break;
302 				case 3 : cout << " type:delta_alpha"; break;
303 				case 4 : cout << " type:replacement_rgba"; break;
304 				case 5 : cout << " type:delta_rgba"; break;
305 				default : cout << " type:?"; break;
306 			}
307 			i = 1;
308 			while (i + 1 < size) {
309 				unsigned ssize;
310 				cout << " " << (unsigned)data[i] << ":" << (unsigned)data[i+1];
311 				if (data[0] == 0 || data[1] == 1)
312 					ssize = 3;
313 				else if (data[0] == 2 || data[1] == 3)
314 					ssize = 1;
315 				else
316 					ssize = 4;
317 				i += 2 + (data[i+1] - data[i] + 1) * ssize;
318 			}
319 			break;
320 		case ADV_PNG_CN_IHDR :
321 			if (size < 13) {
322 				cout << " invalid chunk size";
323 				break;
324 			}
325 			cout << " width:" << be_uint32_read(data) << " height:" << be_uint32_read(data + 4);
326 			cout << " depth:" << (unsigned)data[8];
327 			cout << " color_type:" << (unsigned)data[9];
328 			cout << " compression:" << (unsigned)data[10];
329 			cout << " filter:" << (unsigned)data[11];
330 			cout << " interlace:" << (unsigned)data[12];
331 		break;
332 	}
333 
334 	cout << endl;
335 }
336 
png_write(adv_fz * f,unsigned pix_width,unsigned pix_height,unsigned pix_pixel,unsigned char * pix_ptr,unsigned pix_scanline,unsigned char * pal_ptr,unsigned pal_size,unsigned char * rns_ptr,unsigned rns_size,shrink_t level)337 void png_write(adv_fz* f, unsigned pix_width, unsigned pix_height, unsigned pix_pixel, unsigned char* pix_ptr, unsigned pix_scanline, unsigned char* pal_ptr, unsigned pal_size, unsigned char* rns_ptr, unsigned rns_size, shrink_t level)
338 {
339 	unsigned char ihdr[13];
340 	data_ptr z_ptr;
341 	unsigned z_size;
342 
343 	if (adv_png_write_signature(f, 0) != 0) {
344 		throw_png_error();
345 	}
346 
347 	be_uint32_write(ihdr + 0, pix_width);
348 	be_uint32_write(ihdr + 4, pix_height);
349 	ihdr[8] = 8; /* bit depth */
350 	if (pix_pixel == 1)
351 		ihdr[9] = 3; /* color type */
352 	else if (pix_pixel == 3)
353 		ihdr[9] = 2; /* color type */
354 	else if (pix_pixel == 4)
355 		ihdr[9] = 6; /* color type */
356 	else
357 		throw error() << "Invalid format";
358 
359 	ihdr[10] = 0; /* compression */
360 	ihdr[11] = 0; /* filter */
361 	ihdr[12] = 0; /* interlace */
362 
363 	if (adv_png_write_chunk(f, ADV_PNG_CN_IHDR, ihdr, sizeof(ihdr), 0) != 0) {
364 		throw_png_error();
365 	}
366 
367 	if (pal_size) {
368 		if (adv_png_write_chunk(f, ADV_PNG_CN_PLTE, pal_ptr, pal_size, 0) != 0) {
369 			throw_png_error();
370 		}
371 	}
372 
373 	if (rns_size) {
374 		if (adv_png_write_chunk(f, ADV_PNG_CN_tRNS, rns_ptr, rns_size, 0) != 0) {
375 			throw_png_error();
376 		}
377 	}
378 
379 	png_compress(level, z_ptr, z_size, pix_ptr, pix_scanline, pix_pixel, 0, 0, pix_width, pix_height);
380 
381 	if (adv_png_write_chunk(f, ADV_PNG_CN_IDAT, z_ptr, z_size, 0) != 0) {
382 		throw_png_error();
383 	}
384 
385 	if (adv_png_write_chunk(f, ADV_PNG_CN_IEND, 0, 0, 0) != 0) {
386 		throw_png_error();
387 	}
388 }
389 
png_convert_4(unsigned pix_width,unsigned pix_height,unsigned pix_pixel,unsigned char * pix_ptr,unsigned pix_scanline,unsigned char * pal_ptr,unsigned pal_size,unsigned char ** dst_ptr,unsigned * dst_pixel,unsigned * dst_scanline)390 void png_convert_4(
391 	unsigned pix_width, unsigned pix_height, unsigned pix_pixel, unsigned char* pix_ptr, unsigned pix_scanline,
392 	unsigned char* pal_ptr, unsigned pal_size,
393 	unsigned char** dst_ptr, unsigned* dst_pixel, unsigned* dst_scanline)
394 {
395 	*dst_pixel = 4;
396 	*dst_scanline = 4 * pix_width;
397 	*dst_ptr = (unsigned char*)malloc(*dst_scanline * pix_height);
398 
399 	if (pix_pixel == 3) {
400 		unsigned i, j;
401 		for(i=0;i<pix_height;++i) {
402 			const unsigned char* p0 = pix_ptr + i * pix_scanline;
403 			unsigned char* p1 = *dst_ptr + i * *dst_scanline;
404 			for(j=0;j<pix_width;++j) {
405 				p1[0] = p0[0];
406 				p1[1] = p0[1];
407 				p1[2] = p0[2];
408 				p1[3] = 0xFF;
409 				p0 += 3;
410 				p1 += 4;
411 			}
412 		}
413 	} else if (pix_pixel == 1) {
414 		unsigned i, j;
415 		for(i=0;i<pix_height;++i) {
416 			const unsigned char* p0 = pix_ptr + i * pix_scanline;
417 			unsigned char* p1 = *dst_ptr + i * *dst_scanline;
418 			for(j=0;j<pix_width;++j) {
419 				unsigned char* c = &pal_ptr[p0[0]*3];
420 				p1[0] = c[0];
421 				p1[1] = c[1];
422 				p1[2] = c[2];
423 				p1[3] = 0xFF;
424 				p0 += 1;
425 				p1 += 4;
426 			}
427 		}
428 	} else if (pix_pixel == 4) {
429 		unsigned i;
430 		for(i=0;i<pix_height;++i) {
431 			const unsigned char* p0 = pix_ptr + i * pix_scanline;
432 			unsigned char* p1 = *dst_ptr + i * *dst_scanline;
433 			memcpy(p1, p0, *dst_scanline);
434 		}
435 	} else {
436 		throw error_unsupported() << "Unsupported format";
437 	}
438 }
439 
440