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