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