Dillo v3.1.1-46-g8a360e32
Loading...
Searching...
No Matches
png.c
Go to the documentation of this file.
1/*
2 * Copyright Geoff Lane nov 1999 zzassgl@twirl.mcc.ac.uk
3 * Copyright Luca Rota, Jorge Arellano Cid, Eric Gaudet 2000
4 * Copyright Jorge Arellano Cid 2009
5 */
6
16#include <config.h>
17#ifdef ENABLE_PNG
18
19#include <stdlib.h> /* For abort() */
20
21#ifdef HAVE_LIBPNG_PNG_H
22#include <libpng/png.h>
23#else
24#include <png.h>
25#endif
26
27#include "msg.h"
28#include "image.hh"
29#include "cache.h"
30#include "dicache.h"
31
35
36#if 0
37static char *prog_state_name[] =
38{
39 "IS_finished", "IS_init", "IS_nextdata"
40};
41#endif
42
63typedef struct {
64 DilloImage *Image;
65 DilloUrl *url;
66 int version;
67 int bgcolor;
69 png_uint_32 width;
70 png_uint_32 height;
71 png_structp png_ptr;
72 png_infop info_ptr;
73 uchar_t *image_data;
74 uchar_t **row_pointers;
75 jmp_buf jmpbuf;
76 int error;
77 png_uint_32 previous_row;
78 int rowbytes;
79 short channels;
81/*
82 * 0 last byte
83 * +-------+-+-----------------------------------+-+
84 * | | | -- data to be processed -- | |
85 * +-------+-+-----------------------------------+-+
86 * ^ ^ ^
87 * ipbuf ipbufstart ipbufsize
88 */
89
90 uchar_t *ipbuf;
91 int ipbufstart;
92 int ipbufsize;
94 enum prog_state state;
98} DilloPng;
99
100#define DATASIZE (png->ipbufsize - png->ipbufstart)
101
102
103static
104void Png_error_handling(png_structp png_ptr, png_const_charp msg)
105{
106 DilloPng *png;
107
108 png = png_get_error_ptr(png_ptr);
109 MSG("Png_error_handling: %s: %s\n", URL_STR(png->url), msg);
110
111 png->error = 1;
112 png->state = IS_finished;
113
114 longjmp(png->jmpbuf, 1);
115}
116
117static
118void Png_warning_handler(png_structp png_ptr, png_const_charp msg)
119{
120 DilloPng *png;
121
122 png = png_get_error_ptr(png_ptr);
123 MSG_WARN("Png warning: %s in %s\n", msg, URL_STR(png->url));
124}
125
126static void
127Png_datainfo_callback(png_structp png_ptr, png_infop info_ptr)
128{
129 DilloPng *png;
130 int color_type;
131 int bit_depth;
132 int interlace_type;
133 uint_t i;
134 double file_gamma = 1 / 2.2;
135
136 _MSG("Png_datainfo_callback:\n");
137
138 png = png_get_progressive_ptr(png_ptr);
139 dReturn_if_fail (png != NULL);
140
141 png_get_IHDR(png_ptr, info_ptr, &png->width, &png->height,
142 &bit_depth, &color_type, &interlace_type, NULL, NULL);
143
144 /* check max image size */
145 if (png->width == 0 || png->height == 0 ||
146 png->width > IMAGE_MAX_AREA / png->height) {
147 MSG("Png_datainfo_callback: suspicious image size request %lu x %lu\n",
148 (ulong_t) png->width, (ulong_t) png->height);
149 Png_error_handling(png_ptr, "Aborting...");
150 return; /* not reached */
151 }
152
153 _MSG("Png_datainfo_callback: png->width = %lu\n"
154 "Png_datainfo_callback: png->height = %lu\n",
155 (ulong_t) png->width, (ulong_t) png->height);
156
157 /* we need RGB/RGBA in the end */
158 if (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8) {
159 /* Convert indexed images to RGB */
160 png_set_expand (png_ptr);
161 } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
162 /* Convert grayscale to RGB */
163 png_set_expand (png_ptr);
164 } else if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) {
165 /* We have transparency header, convert it to alpha channel */
166 png_set_expand(png_ptr);
167 } else if (bit_depth < 8) {
168 png_set_expand(png_ptr);
169 }
170
171 if (bit_depth == 16) {
172 png_set_strip_16(png_ptr);
173 }
174
175 /* Get and set gamma information. Beware: gamma correction 2.2 will
176 only work on PC's. TODO: select screen gamma correction for other
177 platforms. */
178 if (png_get_gAMA(png_ptr, info_ptr, &file_gamma))
179 png_set_gamma(png_ptr, 2.2, file_gamma);
180
181 /* Convert gray scale to RGB */
182 if (color_type == PNG_COLOR_TYPE_GRAY ||
183 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
184 png_set_gray_to_rgb(png_ptr);
185 }
186
187 /* Interlaced */
188 if (interlace_type != PNG_INTERLACE_NONE) {
189 png_set_interlace_handling(png_ptr);
190 }
191
192 /* get libpng to update its state */
193 png_read_update_info(png_ptr, info_ptr);
194
195 png_get_IHDR(png_ptr, info_ptr, &png->width, &png->height,
196 &bit_depth, &color_type, &interlace_type, NULL, NULL);
197
198 png->rowbytes = png_get_rowbytes(png_ptr, info_ptr);
199 png->channels = png_get_channels(png_ptr, info_ptr);
200
201 /* init Dillo specifics */
202 _MSG("Png_datainfo_callback: rowbytes = %d\n"
203 "Png_datainfo_callback: width = %lu\n"
204 "Png_datainfo_callback: height = %lu\n",
205 png->rowbytes, (ulong_t) png->width, (ulong_t) png->height);
206
207 png->image_data = (uchar_t *) dMalloc(png->rowbytes * png->height);
208 png->row_pointers = (uchar_t **) dMalloc(png->height * sizeof(uchar_t *));
209
210 for (i = 0; i < png->height; i++)
211 png->row_pointers[i] = png->image_data + (i * png->rowbytes);
212
213 png->linebuf = dMalloc(3 * png->width);
214
215 /* Initialize the dicache-entry here */
216 a_Dicache_set_parms(png->url, png->version, png->Image,
217 (uint_t)png->width, (uint_t)png->height,
218 DILLO_IMG_TYPE_RGB, file_gamma);
219 png->Image = NULL; /* safeguard: hereafter it may be freed by its owner */
220}
221
222static void
223 Png_datarow_callback(png_structp png_ptr, png_bytep new_row,
224 png_uint_32 row_num, int pass)
225{
226 DilloPng *png;
227 uint_t i;
228
229 if (!new_row) /* work to do? */
230 return;
231
232 _MSG("Png_datarow_callback: row_num = %ld\n", row_num);
233
234 png = png_get_progressive_ptr(png_ptr);
235
236 png_progressive_combine_row(png_ptr, png->row_pointers[row_num], new_row);
237
238 _MSG("png: row_num=%u previous_row=%u\n", row_num, png->previous_row);
239 if (row_num < png->previous_row) {
240 a_Dicache_new_scan(png->url, png->version);
241 }
242 png->previous_row = row_num;
243
244 switch (png->channels) {
245 case 3:
246 a_Dicache_write(png->url, png->version,
247 png->image_data + (row_num * png->rowbytes),
248 (uint_t)row_num);
249 break;
250 case 4:
251 {
252 /* TODO: get the backgound color from the parent
253 * of the image widget -- Livio. */
254 int a, bg_red, bg_green, bg_blue;
255 uchar_t *pl = png->linebuf;
256 uchar_t *data = png->image_data + (row_num * png->rowbytes);
257
258 /* TODO: maybe change prefs.bg_color to `a_Dw_widget_get_bg_color`,
259 * when background colors are correctly implementated */
260 bg_blue = (png->bgcolor) & 0xFF;
261 bg_green = (png->bgcolor>>8) & 0xFF;
262 bg_red = (png->bgcolor>>16) & 0xFF;
263
264 for (i = 0; i < png->width; i++) {
265 a = *(data+3);
266
267 if (a == 255) {
268 *(pl++) = *(data++);
269 *(pl++) = *(data++);
270 *(pl++) = *(data++);
271 data++;
272 } else if (a == 0) {
273 *(pl++) = bg_red;
274 *(pl++) = bg_green;
275 *(pl++) = bg_blue;
276 data += 4;
277 } else {
278 png_composite(*(pl++), *(data++), a, bg_red);
279 png_composite(*(pl++), *(data++), a, bg_green);
280 png_composite(*(pl++), *(data++), a, bg_blue);
281 data++;
282 }
283 }
284 a_Dicache_write(png->url, png->version, png->linebuf, (uint_t)row_num);
285 break;
286 }
287 default:
288 MSG("Png_datarow_callback: unexpected number of channels=%d pass=%d\n",
289 png->channels, pass);
290 abort();
291 }
292}
293
294static void Png_dataend_callback(png_structp png_ptr, png_infop info_ptr)
295{
296 DilloPng *png;
297
298 _MSG("Png_dataend_callback:\n");
299 if (!info_ptr)
300 MSG("Png_dataend_callback: info_ptr = NULL\n");
301
302 png = png_get_progressive_ptr(png_ptr);
303 png->state = IS_finished;
304}
305
309static void Png_free(DilloPng *png)
310{
311 _MSG("Png_free: png=%p\n", png);
312
313 dFree(png->image_data);
314 dFree(png->row_pointers);
315 dFree(png->linebuf);
316 if (setjmp(png->jmpbuf))
317 MSG_WARN("PNG: can't destroy read structure\n");
318 else if (png->png_ptr)
319 png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
320 dFree(png);
321}
322
326static void Png_close(DilloPng *png, CacheClient_t *Client)
327{
328 _MSG("Png_close\n");
329 /* Let dicache know decoding is over */
330 a_Dicache_close(png->url, png->version, Client);
331 Png_free(png);
332}
333
337static void Png_write(DilloPng *png, void *Buf, uint_t BufSize)
338{
339 dReturn_if_fail ( Buf != NULL && BufSize > 0 );
340
341 /* Keep local copies so we don't have to pass multiple args to
342 * a number of functions. */
343 png->ipbuf = Buf;
344 png->ipbufsize = BufSize;
345
346 /* start/resume the FSM here */
347 while (png->state != IS_finished && DATASIZE) {
348 _MSG("State = %s\n", prog_state_name[png->state]);
349
350 switch (png->state) {
351 case IS_init:
352 if (DATASIZE < 8) {
353 return; /* need MORE data */
354 }
355 /* check the image signature - DON'T update ipbufstart! */
356 if (png_sig_cmp(png->ipbuf, 0, DATASIZE)) {
357 MSG_WARN("\"%s\" is not a PNG file.\n", URL_STR(png->url));
358 png->state = IS_finished;
359 break;
360 }
361 /* OK, it looks like a PNG image, lets do some set up stuff */
362 png->png_ptr = png_create_read_struct(
363 PNG_LIBPNG_VER_STRING,
364 png,
365 (png_error_ptr)Png_error_handling,
366 (png_error_ptr)Png_warning_handler);
367 dReturn_if_fail (png->png_ptr != NULL);
368 png->info_ptr = png_create_info_struct(png->png_ptr);
369 dReturn_if_fail (png->info_ptr != NULL);
370
371 setjmp(png->jmpbuf);
372 if (!png->error) {
373 png_set_progressive_read_fn(
374 png->png_ptr,
375 png,
376 Png_datainfo_callback, /* performs local init functions */
377 Png_datarow_callback, /* performs per row action */
378 Png_dataend_callback); /* performs cleanup actions */
379 png->state = IS_nextdata;
380 }
381 break;
382
383 case IS_nextdata:
384 if (setjmp(png->jmpbuf)) {
385 png->state = IS_finished;
386 } else if (!png->error) {
387 png_process_data( png->png_ptr,
388 png->info_ptr,
389 png->ipbuf + png->ipbufstart,
390 (png_size_t)DATASIZE);
391
392 png->ipbufstart += DATASIZE;
393 }
394 break;
395
396 default:
397 MSG_WARN("PNG decoder: bad state = %d\n", png->state);
398 abort();
399 }
400 }
401}
402
426void a_Png_callback(int Op, void *data)
427{
428 if (Op == CA_Send) {
429 CacheClient_t *Client = data;
430 Png_write(Client->CbData, Client->Buf, Client->BufSize);
431 } else if (Op == CA_Close) {
432 CacheClient_t *Client = data;
433 Png_close(Client->CbData, Client);
434 } else if (Op == CA_Abort) {
435 Png_free(data);
436 }
437}
438
442void *a_Png_new(DilloImage *Image, DilloUrl *url, int version)
443{
444 DilloPng *png = dNew0(DilloPng, 1);
445 _MSG("a_Png_new: png=%p\n", png);
446
447 png->Image = Image;
448 png->url = url;
449 png->version = version;
450 png->bgcolor = Image->bg_color;
451 png->error = 0;
452 png->ipbuf = NULL;
453 png->ipbufstart = 0;
454 png->ipbufsize = 0;
455 png->state = IS_init;
456 png->linebuf = NULL;
457 png->image_data = NULL;
458 png->row_pointers = NULL;
459 png->previous_row = 0;
460
461 return png;
462}
463
464#else /* ENABLE_PNG */
465
466void *a_Png_new() { return 0; }
467void a_Png_callback() { return; }
468
469#endif /* ENABLE_PNG */
#define _MSG(...)
Definition bookmarks.c:45
#define MSG(...)
Definition bookmarks.c:46
#define CA_Send
Definition cache.h:15
#define CA_Abort
Definition cache.h:17
#define CA_Close
Definition cache.h:16
unsigned char uchar_t
Definition d_size.h:17
unsigned int uint_t
Definition d_size.h:20
unsigned long ulong_t
Definition d_size.h:19
void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image, uint_t width, uint_t height, DilloImgType type, double gamma)
Set image's width, height & type.
Definition dicache.c:228
void a_Dicache_new_scan(const DilloUrl *url, int version)
Reset for a new scan from a multiple-scan image.
Definition dicache.c:286
void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client)
Implement the close method of the decoding process.
Definition dicache.c:329
void a_Dicache_write(DilloUrl *url, int version, const uchar_t *buf, uint_t Y)
Implement the write method (Write a scan line into the Dicache entry) buf: row buffer Y : row number.
Definition dicache.c:309
void dFree(void *mem)
Definition dlib.c:68
void * dMalloc(size_t size)
Definition dlib.c:45
#define dReturn_if_fail(expr)
Definition dlib.h:72
#define dNew0(type, count)
Definition dlib.h:51
static void error(char *msg)
Definition dpidc.c:39
#define IMAGE_MAX_AREA
Definition fltkimgbuf.cc:27
static uchar_t * linebuf
Definition imgbuf.cc:23
#define MSG_WARN(...)
Definition msg.h:26
static void Png_datainfo_callback(png_structp png_ptr, png_infop info_ptr)
Definition png.c:127
static void Png_datarow_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass)
Definition png.c:223
prog_state
Definition png.c:32
@ IS_finished
Definition png.c:33
@ IS_init
Definition png.c:33
@ IS_nextdata
Definition png.c:33
static void Png_write(DilloPng *png, void *Buf, uint_t BufSize)
Receive and process new chunks of PNG image data.
Definition png.c:337
void * a_Png_new(DilloImage *Image, DilloUrl *url, int version)
Create the image state data that must be kept between calls.
Definition png.c:442
static void Png_close(DilloPng *png, CacheClient_t *Client)
Finish the decoding process (and free the memory)
Definition png.c:326
static void Png_dataend_callback(png_structp png_ptr, png_infop info_ptr)
Definition png.c:294
#define DATASIZE
Definition png.c:100
static void Png_warning_handler(png_structp png_ptr, png_const_charp msg)
Definition png.c:118
static void Png_error_handling(png_structp png_ptr, png_const_charp msg)
Definition png.c:104
void a_Png_callback(int Op, void *data)
PNG callback.
Definition png.c:426
static void Png_free(DilloPng *png)
Free up the resources for this image.
Definition png.c:309
The DilloImage data-structure and methods.
@ DILLO_IMG_TYPE_RGB
Definition image.hh:44
Data structure for cache clients.
Definition cache.h:48
void * CbData
Client function data.
Definition cache.h:55
uint_t BufSize
Valid size of cache-data.
Definition cache.h:53
void * Buf
Pointer to cache-data.
Definition cache.h:52
Definition url.h:88
int32_t bg_color
Background color.
Definition image.hh:68
#define URL_STR(u)
Definition url.h:76