Dillo v3.1.1-119-g140d9ebd
Loading...
Searching...
No Matches
jpeg.c
Go to the documentation of this file.
1/*
2 * File: jpeg.c
3 *
4 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
5 * Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 */
12
20#include <config.h>
21#ifdef ENABLE_JPEG
22
23#include <stdio.h>
24#include <setjmp.h>
25
26/* avoid a redefinition of HAVE_STDLIB_H with old jpeglib.h */
27#ifdef HAVE_STDLIB_H
28# undef HAVE_STDLIB_H
29#endif
30#include <jpeglib.h>
31#ifdef HAVE_STDLIB_H
32# undef HAVE_STDLIB_H
33#endif
34
35#include "image.hh"
36#include "cache.h"
37#include "dicache.h"
38#include "capi.h" /* get cache entry status */
39#include "msg.h"
40
41typedef enum {
42 DILLO_JPEG_INIT,
43 DILLO_JPEG_STARTING,
44 DILLO_JPEG_READ_BEGIN_SCAN,
45 DILLO_JPEG_READ_IN_SCAN,
46 DILLO_JPEG_READ_END_SCAN,
47 DILLO_JPEG_DONE,
48 DILLO_JPEG_ERROR
49} DilloJpegState;
50
51typedef struct DilloJpeg DilloJpeg;
52
53/* An implementation of a suspending source manager */
54
55typedef struct {
56 struct jpeg_source_mgr pub;
57 DilloJpeg *jpeg;
58} my_source_mgr;
59
60struct my_error_mgr {
61 struct jpeg_error_mgr pub;
62 jmp_buf setjmp_buffer;
63};
64typedef struct my_error_mgr *my_error_ptr;
65
66struct DilloJpeg {
67 DilloImage *Image;
68 DilloUrl *url;
69 int version;
70
71 my_source_mgr Src;
72
73 DilloJpegState state;
74 size_t Start_Ofs, Skip, NewStart;
75 char *Data;
76
77 uint_t y;
78
79 struct jpeg_decompress_struct cinfo;
80 struct my_error_mgr jerr;
81};
82
83/*
84 * Forward declarations
85 */
86static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize);
87
88
90METHODDEF(void) Jpeg_errorexit (j_common_ptr cinfo)
91{
92 /* display message and return to setjmp buffer */
93 my_error_ptr myerr = (my_error_ptr) cinfo->err;
94 if (prefs.show_msg) {
95 DilloJpeg *jpeg =
96 ((my_source_mgr *) ((j_decompress_ptr) cinfo)->src)->jpeg;
97 MSG_WARN("\"%s\": ", URL_STR(jpeg->url));
98 (*cinfo->err->output_message) (cinfo);
99 }
100 longjmp(myerr->setjmp_buffer, 1);
101}
102
106static void Jpeg_free(DilloJpeg *jpeg)
107{
108 _MSG("Jpeg_free: jpeg=%p\n", jpeg);
109 jpeg_destroy_decompress(&(jpeg->cinfo));
110 dFree(jpeg);
111}
112
116static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client)
117{
118 _MSG("Jpeg_close\n");
119 a_Dicache_close(jpeg->url, jpeg->version, Client);
120 Jpeg_free(jpeg);
121}
122
123static void init_source(struct jpeg_decompress_struct *p)
124{
125 (void) p; /* unused */
126}
127
128static boolean fill_input_buffer(j_decompress_ptr cinfo)
129{
130 DilloJpeg *jpeg = ((my_source_mgr *) cinfo->src)->jpeg;
131
132 _MSG("fill_input_buffer\n");
133#if 0
134 if (!cinfo->src->bytes_in_buffer) {
135 _MSG("fill_input_buffer: %ld bytes in buffer\n",
136 (long)cinfo->src->bytes_in_buffer);
137
138 jpeg->Start_Ofs = (ulong_t) jpeg->cinfo.src->next_input_byte -
139 (ulong_t) jpeg->Data;
140#endif
141 if (jpeg->Skip) {
142 jpeg->Start_Ofs = jpeg->NewStart + jpeg->Skip - 1;
143 jpeg->Skip = 0;
144 } else {
145 jpeg->Start_Ofs = (ulong_t) jpeg->cinfo.src->next_input_byte -
146 (ulong_t) jpeg->Data;
147 }
148 return FALSE;
149#if 0
150 }
151 return TRUE;
152#endif
153}
154
155static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
156{
157 DilloJpeg *jpeg;
158
159 if (num_bytes < 1)
160 return;
161 jpeg = ((my_source_mgr *) cinfo->src)->jpeg;
162
163 _MSG("skip_input_data: Start_Ofs = %lu, num_bytes = %ld,"
164 " %ld bytes in buffer\n",
165 (ulong_t)jpeg->Start_Ofs, num_bytes,(long)cinfo->src->bytes_in_buffer);
166
167 cinfo->src->next_input_byte += num_bytes;
168 if (num_bytes < (long)cinfo->src->bytes_in_buffer) {
169 cinfo->src->bytes_in_buffer -= num_bytes;
170 } else {
171 jpeg->Skip += num_bytes - cinfo->src->bytes_in_buffer + 1;
172 cinfo->src->bytes_in_buffer = 0;
173 }
174}
175
176/*
177 * The proper signature is:
178 * static void term_source(j_decompress_ptr cinfo)
179 * (declaring it with no parameter avoids a compiler warning)
180 */
181static void term_source(struct jpeg_decompress_struct *p)
182{
183 (void) p; /* unused */
184}
185
186void *a_Jpeg_new(DilloImage *Image, DilloUrl *url, int version)
187{
188 my_source_mgr *src;
189 DilloJpeg *jpeg = dMalloc(sizeof(*jpeg));
190 _MSG("a_Jpeg_new: jpeg=%p\n", jpeg);
191
192 jpeg->Image = Image;
193 jpeg->url = url;
194 jpeg->version = version;
195
196 jpeg->state = DILLO_JPEG_INIT;
197 jpeg->Start_Ofs = 0;
198 jpeg->Skip = 0;
199
200 /* decompression step 1 (see libjpeg.doc) */
201 jpeg->cinfo.err = jpeg_std_error(&(jpeg->jerr.pub));
202 jpeg->jerr.pub.error_exit = Jpeg_errorexit;
203
204 jpeg_create_decompress(&(jpeg->cinfo));
205
206 /* decompression step 2 (see libjpeg.doc) */
207 jpeg->cinfo.src = &jpeg->Src.pub;
208 src = &jpeg->Src;
209 src->pub.init_source = init_source;
210 src->pub.fill_input_buffer = fill_input_buffer;
211 src->pub.skip_input_data = skip_input_data;
212 src->pub.resync_to_restart = jpeg_resync_to_restart;/* use default method */
213 src->pub.term_source = term_source;
214 src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
215 src->pub.next_input_byte = NULL;/* until buffer loaded */
216
217 src->jpeg = jpeg;
218
219 /* decompression steps continue in write method */
220 return jpeg;
221}
222
223void a_Jpeg_callback(int Op, void *data)
224{
225 if (Op == CA_Send) {
226 CacheClient_t *Client = data;
227 Jpeg_write(Client->CbData, Client->Buf, Client->BufSize);
228 } else if (Op == CA_Close) {
229 CacheClient_t *Client = data;
230 Jpeg_close(Client->CbData, Client);
231 } else if (Op == CA_Abort) {
232 Jpeg_free(data);
233 }
234}
235
236const char *a_Jpeg_version(void)
237{
238#define QUOTE(x) #x
239#define STR(x) QUOTE(x)
240
241#if defined(LIBJPEG_TURBO_VERSION)
242 return STR(LIBJPEG_TURBO_VERSION);
243#else
244 return STR(JPEG_LIB_VERSION);
245#endif
246
247#undef STR
248#undef QUOTE
249}
250
254static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
255{
256 DilloImgType type;
258 JSAMPLE *array[1];
259 int num_read;
260
261 _MSG("Jpeg_write: (%p) Bytes in buff: %ld Ofs: %lu\n", jpeg,
262 (long) BufSize, (ulong_t)jpeg->Start_Ofs);
263
264 /* See if we are supposed to skip ahead. */
265 if (BufSize <= jpeg->Start_Ofs)
266 return;
267
268 /* Concatenate with the partial input, if any. */
269 jpeg->cinfo.src->next_input_byte = (uchar_t *)Buf + jpeg->Start_Ofs;
270 jpeg->cinfo.src->bytes_in_buffer = BufSize - jpeg->Start_Ofs;
271 jpeg->NewStart = BufSize;
272 jpeg->Data = Buf;
273
274 if (setjmp(jpeg->jerr.setjmp_buffer)) {
275 /* If we get here, the JPEG code has signaled an error. */
276 jpeg->state = DILLO_JPEG_ERROR;
277 }
278
279 /* Process the bytes in the input buffer. */
280 if (jpeg->state == DILLO_JPEG_INIT) {
281
282 /* decompression step 3 (see libjpeg.doc) */
283 if (jpeg_read_header(&(jpeg->cinfo), TRUE) != JPEG_SUSPENDED) {
284 type = DILLO_IMG_TYPE_GRAY;
285 if (jpeg->cinfo.num_components == 1) {
286 type = DILLO_IMG_TYPE_GRAY;
287 } else if (jpeg->cinfo.num_components == 3) {
288 type = DILLO_IMG_TYPE_RGB;
289 } else {
290 if (jpeg->cinfo.jpeg_color_space == JCS_YCCK)
291 MSG("YCCK JPEG. Are the colors wrong?\n");
292 if (!jpeg->cinfo.saw_Adobe_marker)
293 MSG("No adobe marker! Is the image shown in reverse video?\n");
295 }
296 /*
297 * If a multiple-scan image is not completely in cache,
298 * use progressive display, updating as it arrives.
299 */
300 if (jpeg_has_multiple_scans(&jpeg->cinfo) &&
301 !(a_Capi_get_flags(jpeg->url) & CAPI_Completed))
302 jpeg->cinfo.buffered_image = TRUE;
303
304 /* check max image size */
305 if (jpeg->cinfo.image_width <= 0 || jpeg->cinfo.image_height <= 0 ||
306 jpeg->cinfo.image_width >
307 IMAGE_MAX_AREA / jpeg->cinfo.image_height) {
308 MSG("Jpeg_write: suspicious image size request %u x %u\n",
309 (uint_t)jpeg->cinfo.image_width,
310 (uint_t)jpeg->cinfo.image_height);
311 jpeg->state = DILLO_JPEG_ERROR;
312 return;
313 }
314
316 a_Dicache_set_parms(jpeg->url, jpeg->version, jpeg->Image,
317 (uint_t)jpeg->cinfo.image_width,
318 (uint_t)jpeg->cinfo.image_height,
319 type, 1 / 2.2);
320 jpeg->Image = NULL; /* safeguard: may be freed by its owner later */
321
322 /* decompression step 4 (see libjpeg.doc) */
323 jpeg->state = DILLO_JPEG_STARTING;
324 }
325 }
326 if (jpeg->state == DILLO_JPEG_STARTING) {
327 /* decompression step 5 (see libjpeg.doc) */
328 if (jpeg_start_decompress(&(jpeg->cinfo))) {
329 jpeg->y = 0;
330 jpeg->state = jpeg->cinfo.buffered_image ?
331 DILLO_JPEG_READ_BEGIN_SCAN : DILLO_JPEG_READ_IN_SCAN;
332 }
333 }
334
335 /*
336 * A progressive jpeg contains multiple scans that can be used to display
337 * an increasingly sharp image as it is being received. The reading of each
338 * scan must be surrounded by jpeg_start_output()/jpeg_finish_output().
339 */
340
341 if (jpeg->state == DILLO_JPEG_READ_END_SCAN) {
342 if (jpeg_finish_output(&jpeg->cinfo)) {
343 if (jpeg_input_complete(&jpeg->cinfo)) {
344 jpeg->state = DILLO_JPEG_DONE;
345 } else {
346 jpeg->state = DILLO_JPEG_READ_BEGIN_SCAN;
347 }
348 }
349 }
350
351 if (jpeg->state == DILLO_JPEG_READ_BEGIN_SCAN) {
352 if (jpeg_start_output(&jpeg->cinfo, jpeg->cinfo.input_scan_number)) {
353 a_Dicache_new_scan(jpeg->url, jpeg->version);
354 jpeg->state = DILLO_JPEG_READ_IN_SCAN;
355 }
356 }
357
358 if (jpeg->state == DILLO_JPEG_READ_IN_SCAN) {
359 linebuf = dMalloc(jpeg->cinfo.image_width *
360 jpeg->cinfo.num_components);
361 array[0] = linebuf;
362
363 while (1) {
364 num_read = jpeg_read_scanlines(&(jpeg->cinfo), array, 1);
365 if (num_read == 0) {
366 /* out of input */
367 break;
368 }
369 a_Dicache_write(jpeg->url, jpeg->version, linebuf, jpeg->y);
370
371 jpeg->y++;
372
373 if (jpeg->y == jpeg->cinfo.image_height) {
374 /* end of scan */
375 if (!jpeg->cinfo.buffered_image) {
376 /* single scan */
377 jpeg->state = DILLO_JPEG_DONE;
378 break;
379 } else {
380 jpeg->y = 0;
381 if (jpeg_input_complete(&jpeg->cinfo)) {
382 if (jpeg->cinfo.input_scan_number ==
383 jpeg->cinfo.output_scan_number) {
384 jpeg->state = DILLO_JPEG_DONE;
385 break;
386 } else {
387 /* one final loop through the scanlines */
388 jpeg_finish_output(&jpeg->cinfo);
389 jpeg_start_output(&jpeg->cinfo,
390 jpeg->cinfo.input_scan_number);
391 continue;
392 }
393 }
394 jpeg->state = DILLO_JPEG_READ_END_SCAN;
395 if (!jpeg_finish_output(&jpeg->cinfo)) {
396 /* out of input */
397 break;
398 } else {
399 if (jpeg_input_complete(&jpeg->cinfo)) {
400 jpeg->state = DILLO_JPEG_DONE;
401 break;
402 } else {
403 jpeg->state = DILLO_JPEG_READ_BEGIN_SCAN;
404 }
405 }
406 if (!jpeg_start_output(&jpeg->cinfo,
407 jpeg->cinfo.input_scan_number)) {
408 /* out of input */
409 break;
410 }
411 a_Dicache_new_scan(jpeg->url, jpeg->version);
412 jpeg->state = DILLO_JPEG_READ_IN_SCAN;
413 }
414 }
415 }
416 dFree(linebuf);
417 }
418}
419
420#else /* ENABLE_JPEG */
421
422void *a_Jpeg_new() { return 0; }
423void a_Jpeg_callback() { return; }
424const char *a_Jpeg_version(void) { return 0; }
425
426#endif /* ENABLE_JPEG */
#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
int a_Capi_get_flags(const DilloUrl *Url)
Return status information of an URL's content-transfer process.
Definition capi.c:522
#define CAPI_Completed
Definition capi.h:19
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:248
void a_Dicache_new_scan(const DilloUrl *url, int version)
Reset for a new scan from a multiple-scan image.
Definition dicache.c:306
void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client)
Implement the close method of the decoding process.
Definition dicache.c:349
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:329
void dFree(void *mem)
Definition dlib.c:68
void * dMalloc(size_t size)
Definition dlib.c:45
#define TRUE
Definition dlib.h:23
#define FALSE
Definition dlib.h:19
#define IMAGE_MAX_AREA
Definition fltkimgbuf.cc:27
static uchar_t * linebuf
Definition imgbuf.cc:23
void a_Jpeg_callback()
Definition jpeg.c:423
const char * a_Jpeg_version(void)
Definition jpeg.c:424
void * a_Jpeg_new()
Definition jpeg.c:422
#define MSG_WARN(...)
Definition msg.h:26
DilloPrefs prefs
Global Data.
Definition prefs.c:33
DilloImgType
Definition image.hh:42
@ DILLO_IMG_TYPE_GRAY
Definition image.hh:45
@ DILLO_IMG_TYPE_CMYK_INV
Definition image.hh:46
@ 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
bool_t show_msg
Definition prefs.h:121
Definition url.h:88
#define URL_STR(u)
Definition url.h:76