/src/mupdf/source/fitz/load-jpeg.c
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (C) 2004-2023 Artifex Software, Inc. |
2 | | // |
3 | | // This file is part of MuPDF. |
4 | | // |
5 | | // MuPDF is free software: you can redistribute it and/or modify it under the |
6 | | // terms of the GNU Affero General Public License as published by the Free |
7 | | // Software Foundation, either version 3 of the License, or (at your option) |
8 | | // any later version. |
9 | | // |
10 | | // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY |
11 | | // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | | // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
13 | | // details. |
14 | | // |
15 | | // You should have received a copy of the GNU Affero General Public License |
16 | | // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> |
17 | | // |
18 | | // Alternative licensing terms are available from the licensor. |
19 | | // For commercial licensing, see <https://www.artifex.com/> or contact |
20 | | // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
21 | | // CA 94129, USA, for further information. |
22 | | |
23 | | #include "mupdf/fitz.h" |
24 | | |
25 | | #include <math.h> |
26 | | #include <stdio.h> |
27 | | #include <string.h> |
28 | | #include <limits.h> |
29 | | |
30 | | #include <jpeglib.h> |
31 | | |
32 | | #ifdef SHARE_JPEG |
33 | | |
34 | | #define JZ_CTX_FROM_CINFO(c) (fz_context *)((c)->client_data) |
35 | | |
36 | | static void fz_jpg_mem_init(j_common_ptr cinfo, fz_context *ctx) |
37 | | { |
38 | | cinfo->client_data = ctx; |
39 | | } |
40 | | |
41 | | #define fz_jpg_mem_term(cinfo) |
42 | | |
43 | | #else /* SHARE_JPEG */ |
44 | | |
45 | | typedef void * backing_store_ptr; |
46 | | #include "jmemcust.h" |
47 | | |
48 | 8.41k | #define JZ_CTX_FROM_CINFO(c) (fz_context *)(GET_CUST_MEM_DATA(c)->priv) |
49 | | |
50 | | static void * |
51 | | fz_jpg_mem_alloc(j_common_ptr cinfo, size_t size) |
52 | 3.59k | { |
53 | 3.59k | fz_context *ctx = JZ_CTX_FROM_CINFO(cinfo); |
54 | 3.59k | return fz_malloc_no_throw(ctx, size); |
55 | 3.59k | } |
56 | | |
57 | | static void |
58 | | fz_jpg_mem_free(j_common_ptr cinfo, void *object, size_t size) |
59 | 3.59k | { |
60 | 3.59k | fz_context *ctx = JZ_CTX_FROM_CINFO(cinfo); |
61 | 3.59k | fz_free(ctx, object); |
62 | 3.59k | } |
63 | | |
64 | | static void |
65 | | fz_jpg_mem_init(j_common_ptr cinfo, fz_context *ctx) |
66 | 1.01k | { |
67 | 1.01k | jpeg_cust_mem_data *custmptr; |
68 | 1.01k | custmptr = fz_malloc_struct(ctx, jpeg_cust_mem_data); |
69 | 1.01k | if (!jpeg_cust_mem_init(custmptr, (void *) ctx, NULL, NULL, NULL, |
70 | 1.01k | fz_jpg_mem_alloc, fz_jpg_mem_free, |
71 | 1.01k | fz_jpg_mem_alloc, fz_jpg_mem_free, NULL)) |
72 | 0 | { |
73 | 0 | fz_free(ctx, custmptr); |
74 | 0 | fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot initialize custom JPEG memory handler"); |
75 | 0 | } |
76 | 1.01k | cinfo->client_data = custmptr; |
77 | 1.01k | } |
78 | | |
79 | | static void |
80 | | fz_jpg_mem_term(j_common_ptr cinfo) |
81 | 1.01k | { |
82 | 1.01k | if (cinfo->client_data) |
83 | 1.01k | { |
84 | 1.01k | fz_context *ctx = JZ_CTX_FROM_CINFO(cinfo); |
85 | 1.01k | fz_free(ctx, cinfo->client_data); |
86 | 1.01k | cinfo->client_data = NULL; |
87 | 1.01k | } |
88 | 1.01k | } |
89 | | |
90 | | #endif /* SHARE_JPEG */ |
91 | | |
92 | | static void output_message(j_common_ptr cinfo) |
93 | 0 | { |
94 | | /* swallow message */ |
95 | 0 | } |
96 | | |
97 | | static void error_exit(j_common_ptr cinfo) |
98 | 199 | { |
99 | 199 | char msg[JMSG_LENGTH_MAX]; |
100 | 199 | fz_context *ctx = JZ_CTX_FROM_CINFO(cinfo); |
101 | | |
102 | 199 | cinfo->err->format_message(cinfo, msg); |
103 | 199 | fz_throw(ctx, FZ_ERROR_LIBRARY, "jpeg error: %s", msg); |
104 | 199 | } |
105 | | |
106 | | static void init_source(j_decompress_ptr cinfo) |
107 | 1.01k | { |
108 | | /* nothing to do */ |
109 | 1.01k | } |
110 | | |
111 | | static void term_source(j_decompress_ptr cinfo) |
112 | 0 | { |
113 | | /* nothing to do */ |
114 | 0 | } |
115 | | |
116 | | static boolean fill_input_buffer(j_decompress_ptr cinfo) |
117 | 149k | { |
118 | 149k | static unsigned char eoi[2] = { 0xFF, JPEG_EOI }; |
119 | 149k | struct jpeg_source_mgr *src = cinfo->src; |
120 | 149k | src->next_input_byte = eoi; |
121 | 149k | src->bytes_in_buffer = 2; |
122 | 149k | return 1; |
123 | 149k | } |
124 | | |
125 | | static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) |
126 | 1.19k | { |
127 | 1.19k | struct jpeg_source_mgr *src = cinfo->src; |
128 | 1.19k | if (num_bytes > 0) |
129 | 1.19k | { |
130 | 1.19k | size_t skip = (size_t)num_bytes; /* size_t may be 64bit */ |
131 | 1.19k | if (skip > src->bytes_in_buffer) |
132 | 39 | skip = (size_t)src->bytes_in_buffer; |
133 | 1.19k | src->next_input_byte += skip; |
134 | 1.19k | src->bytes_in_buffer -= skip; |
135 | 1.19k | } |
136 | 1.19k | } |
137 | | |
138 | | static inline int read_value(const unsigned char *data, int bytes, int is_big_endian) |
139 | 82.5k | { |
140 | 82.5k | int value = 0; |
141 | 82.5k | if (!is_big_endian) |
142 | 24.2k | data += bytes; |
143 | 330k | for (; bytes > 0; bytes--) |
144 | 247k | value = (value << 8) | (is_big_endian ? *data++ : *--data); |
145 | 82.5k | return value; |
146 | 82.5k | } |
147 | | |
148 | | enum { |
149 | | MAX_ICC_PARTS = 256 |
150 | | }; |
151 | | |
152 | | static fz_colorspace *extract_icc_profile(fz_context *ctx, jpeg_saved_marker_ptr init_marker, int output_components, fz_colorspace *colorspace) |
153 | 819 | { |
154 | 819 | #if FZ_ENABLE_ICC |
155 | 819 | const char idseq[] = { 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0'}; |
156 | 819 | jpeg_saved_marker_ptr marker = init_marker; |
157 | 819 | fz_buffer *buf = NULL; |
158 | 819 | fz_colorspace *icc; |
159 | 819 | int part = 1; |
160 | 819 | int parts = MAX_ICC_PARTS; |
161 | 819 | const unsigned char *data; |
162 | 819 | size_t size; |
163 | | |
164 | 819 | fz_var(buf); |
165 | | |
166 | 819 | if (init_marker == NULL) |
167 | 754 | return colorspace; |
168 | | |
169 | 130 | fz_try(ctx) |
170 | 130 | { |
171 | 133 | while (part < parts && marker != NULL) |
172 | 68 | { |
173 | 557 | for (marker = init_marker; marker != NULL; marker = marker->next) |
174 | 498 | { |
175 | 498 | if (marker->marker != JPEG_APP0 + 2) |
176 | 44 | continue; |
177 | 454 | if (marker->data_length < nelem(idseq) + 2) |
178 | 64 | continue; |
179 | 390 | if (memcmp(marker->data, idseq, nelem(idseq))) |
180 | 152 | continue; |
181 | 238 | if (marker->data[nelem(idseq)] != part) |
182 | 229 | continue; |
183 | | |
184 | 9 | if (parts == MAX_ICC_PARTS) |
185 | 9 | parts = marker->data[nelem(idseq) + 1]; |
186 | 0 | else if (marker->data[nelem(idseq) + 1] != parts) |
187 | 0 | fz_warn(ctx, "inconsistent number of icc profile chunks in jpeg"); |
188 | 9 | if (part > parts) |
189 | 0 | { |
190 | 0 | fz_warn(ctx, "skipping out of range icc profile chunk in jpeg"); |
191 | 0 | continue; |
192 | 0 | } |
193 | | |
194 | 9 | data = marker->data + 14; |
195 | 9 | size = marker->data_length - 14; |
196 | | |
197 | 9 | if (!buf) |
198 | 9 | buf = fz_new_buffer_from_copied_data(ctx, data, size); |
199 | 0 | else |
200 | 0 | fz_append_data(ctx, buf, data, size); |
201 | | |
202 | 9 | part++; |
203 | 9 | break; |
204 | 9 | } |
205 | 68 | } |
206 | | |
207 | 65 | if (buf) |
208 | 9 | { |
209 | 9 | icc = fz_new_icc_colorspace(ctx, fz_colorspace_type(ctx, colorspace), 0, NULL, buf); |
210 | 9 | fz_drop_colorspace(ctx, colorspace); |
211 | 9 | colorspace = icc; |
212 | 9 | } |
213 | 65 | } |
214 | 130 | fz_always(ctx) |
215 | 65 | fz_drop_buffer(ctx, buf); |
216 | 65 | fz_catch(ctx) |
217 | 3 | { |
218 | 3 | fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); |
219 | 3 | fz_report_error(ctx); |
220 | 3 | fz_warn(ctx, "ignoring embedded ICC profile in JPEG"); |
221 | 3 | } |
222 | | |
223 | 65 | return colorspace; |
224 | | #else |
225 | | return colorspace; |
226 | | #endif |
227 | 819 | } |
228 | | |
229 | | /* Returns true if <x> can be represented as an integer without overflow. |
230 | | * |
231 | | * We can't use comparisons such as 'return x < INT_MAX' because INT_MAX is |
232 | | * not safely convertible to float - it ends up as INT_MAX+1 so the comparison |
233 | | * doesn't do what we want. |
234 | | * |
235 | | * Instead we do a round-trip conversion and return true if this differs by |
236 | | * less than 1. This relies on high adjacent float values that differ by more |
237 | | * than 1, actually being exact integers, so the round-trip doesn't change the |
238 | | * value. |
239 | | */ |
240 | | static int float_can_be_int(float x) |
241 | 3 | { |
242 | 3 | return fabsf(x - (float)(int) x) < 1; |
243 | 3 | } |
244 | | |
245 | | static uint8_t exif_orientation_to_mupdf[9] = { 0, 1, 5, 3, 7, 6, 4, 8, 2 }; |
246 | | |
247 | | static int extract_exif_resolution(jpeg_saved_marker_ptr marker, |
248 | | int *xres, int *yres, uint8_t *orientation) |
249 | 819 | { |
250 | 819 | int is_big_endian, orient; |
251 | 819 | const unsigned char *data; |
252 | 819 | unsigned int offset, ifd_len, res_type = 0; |
253 | 819 | float x_res = 0, y_res = 0; |
254 | | |
255 | 819 | if (!marker || marker->marker != JPEG_APP0 + 1 || marker->data_length < 14) |
256 | 780 | return 0; |
257 | 39 | data = (const unsigned char *)marker->data; |
258 | 39 | if (read_value(data, 4, 1) != 0x45786966 /* Exif */ || read_value(data + 4, 2, 1) != 0x0000) |
259 | 3 | return 0; |
260 | 36 | if (read_value(data + 6, 4, 1) == 0x49492A00) |
261 | 20 | is_big_endian = 0; |
262 | 16 | else if (read_value(data + 6, 4, 1) == 0x4D4D002A) |
263 | 9 | is_big_endian = 1; |
264 | 7 | else |
265 | 7 | return 0; |
266 | | |
267 | 29 | offset = read_value(data + 10, 4, is_big_endian) + 6; |
268 | 29 | if (offset < 14 || offset > marker->data_length - 2) |
269 | 1 | return 0; |
270 | 28 | ifd_len = read_value(data + offset, 2, is_big_endian); |
271 | 20.6k | for (offset += 2; ifd_len > 0 && offset + 12 < marker->data_length; ifd_len--, offset += 12) |
272 | 20.5k | { |
273 | 20.5k | int tag = read_value(data + offset, 2, is_big_endian); |
274 | 20.5k | int type = read_value(data + offset + 2, 2, is_big_endian); |
275 | 20.5k | int count = read_value(data + offset + 4, 4, is_big_endian); |
276 | 20.5k | unsigned int value_off = read_value(data + offset + 8, 4, is_big_endian) + 6; |
277 | 20.5k | switch (tag) |
278 | 20.5k | { |
279 | 33 | case 0x112: |
280 | 33 | if (type == 3 && count == 1) { |
281 | 24 | orient = read_value(data + offset + 8, 2, is_big_endian); |
282 | 24 | if (orient >= 1 && orient <= 8 && orientation) |
283 | 17 | *orientation = exif_orientation_to_mupdf[orient]; |
284 | 24 | } |
285 | 33 | break; |
286 | 28 | case 0x11A: |
287 | 28 | if (type == 5 && value_off > offset && value_off <= marker->data_length - 8) |
288 | 2 | x_res = 1.0f * read_value(data + value_off, 4, is_big_endian) / read_value(data + value_off + 4, 4, is_big_endian); |
289 | 28 | break; |
290 | 35 | case 0x11B: |
291 | 35 | if (type == 5 && value_off > offset && value_off <= marker->data_length - 8) |
292 | 2 | y_res = 1.0f * read_value(data + value_off, 4, is_big_endian) / read_value(data + value_off + 4, 4, is_big_endian); |
293 | 35 | break; |
294 | 25 | case 0x128: |
295 | 25 | if (type == 3 && count == 1) |
296 | 4 | res_type = read_value(data + offset + 8, 2, is_big_endian); |
297 | 25 | break; |
298 | 20.5k | } |
299 | 20.5k | } |
300 | | |
301 | 28 | if (x_res <= 0 || !float_can_be_int(x_res) || y_res <= 0 || !float_can_be_int(y_res)) |
302 | 27 | return 0; |
303 | 1 | if (res_type == 2) |
304 | 0 | { |
305 | 0 | *xres = (int)x_res; |
306 | 0 | *yres = (int)y_res; |
307 | 0 | } |
308 | 1 | else if (res_type == 3) |
309 | 0 | { |
310 | 0 | *xres = (int)(x_res * 254 / 100); |
311 | 0 | *yres = (int)(y_res * 254 / 100); |
312 | 0 | } |
313 | 1 | else |
314 | 1 | { |
315 | 1 | *xres = 0; |
316 | 1 | *yres = 0; |
317 | 1 | } |
318 | 1 | return 1; |
319 | 28 | } |
320 | | |
321 | | static int extract_app13_resolution(jpeg_saved_marker_ptr marker, int *xres, int *yres) |
322 | 818 | { |
323 | 818 | const unsigned char *data, *data_end; |
324 | | |
325 | 818 | if (!marker || marker->marker != JPEG_APP0 + 13 || marker->data_length < 42 || |
326 | 818 | strcmp((const char *)marker->data, "Photoshop 3.0") != 0) |
327 | 818 | { |
328 | 818 | return 0; |
329 | 818 | } |
330 | | |
331 | 0 | data = (const unsigned char *)marker->data; |
332 | 0 | data_end = data + marker->data_length; |
333 | 0 | for (data += 14; data + 12 < data_end; ) { |
334 | 0 | int data_size = -1; |
335 | 0 | int tag = read_value(data + 4, 2, 1); |
336 | 0 | int value_off = 11 + read_value(data + 6, 2, 1); |
337 | 0 | if (value_off % 2 == 1) |
338 | 0 | value_off++; |
339 | 0 | if (read_value(data, 4, 1) == 0x3842494D /* 8BIM */ && value_off <= data_end - data) |
340 | 0 | data_size = read_value(data + value_off - 4, 4, 1); |
341 | 0 | if (data_size < 0 || data_size > data_end - data - value_off) |
342 | 0 | return 0; |
343 | 0 | if (tag == 0x3ED && data_size == 16) |
344 | 0 | { |
345 | 0 | *xres = read_value(data + value_off, 2, 1); |
346 | 0 | *yres = read_value(data + value_off + 8, 2, 1); |
347 | 0 | return 1; |
348 | 0 | } |
349 | 0 | if (data_size % 2 == 1) |
350 | 0 | data_size++; |
351 | 0 | data += value_off + data_size; |
352 | 0 | } |
353 | | |
354 | 0 | return 0; |
355 | 0 | } |
356 | | |
357 | | static void invert_cmyk(unsigned char *p, int n) |
358 | 0 | { |
359 | 0 | int i; |
360 | 0 | for (i = 0; i < n; ++i) |
361 | 0 | p[i] = 255 - p[i]; |
362 | 0 | } |
363 | | |
364 | | fz_pixmap * |
365 | | fz_load_jpeg(fz_context *ctx, const unsigned char *rbuf, size_t rlen) |
366 | 0 | { |
367 | 0 | struct jpeg_decompress_struct cinfo; |
368 | 0 | struct jpeg_error_mgr err; |
369 | 0 | struct jpeg_source_mgr src; |
370 | 0 | unsigned char *row[1], *sp, *dp; |
371 | 0 | fz_colorspace *colorspace = NULL; |
372 | 0 | unsigned int x; |
373 | 0 | int k; |
374 | 0 | size_t stride; |
375 | 0 | fz_pixmap *image = NULL; |
376 | |
|
377 | 0 | fz_var(colorspace); |
378 | 0 | fz_var(image); |
379 | 0 | fz_var(row); |
380 | |
|
381 | 0 | row[0] = NULL; |
382 | |
|
383 | 0 | cinfo.mem = NULL; |
384 | 0 | cinfo.global_state = 0; |
385 | 0 | cinfo.err = jpeg_std_error(&err); |
386 | 0 | err.output_message = output_message; |
387 | 0 | err.error_exit = error_exit; |
388 | |
|
389 | 0 | cinfo.client_data = NULL; |
390 | 0 | fz_jpg_mem_init((j_common_ptr)&cinfo, ctx); |
391 | |
|
392 | 0 | fz_try(ctx) |
393 | 0 | { |
394 | 0 | jpeg_create_decompress(&cinfo); |
395 | |
|
396 | 0 | cinfo.src = &src; |
397 | 0 | src.init_source = init_source; |
398 | 0 | src.fill_input_buffer = fill_input_buffer; |
399 | 0 | src.skip_input_data = skip_input_data; |
400 | 0 | src.resync_to_restart = jpeg_resync_to_restart; |
401 | 0 | src.term_source = term_source; |
402 | 0 | src.next_input_byte = rbuf; |
403 | 0 | src.bytes_in_buffer = rlen; |
404 | |
|
405 | 0 | jpeg_save_markers(&cinfo, JPEG_APP0+1, 0xffff); |
406 | 0 | jpeg_save_markers(&cinfo, JPEG_APP0+13, 0xffff); |
407 | 0 | jpeg_save_markers(&cinfo, JPEG_APP0+2, 0xffff); |
408 | |
|
409 | 0 | jpeg_read_header(&cinfo, 1); |
410 | |
|
411 | 0 | jpeg_start_decompress(&cinfo); |
412 | |
|
413 | 0 | if (cinfo.output_components == 1) |
414 | 0 | colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx)); |
415 | 0 | else if (cinfo.output_components == 3) |
416 | 0 | colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); |
417 | 0 | else if (cinfo.output_components == 4) |
418 | 0 | colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); |
419 | 0 | colorspace = extract_icc_profile(ctx, cinfo.marker_list, cinfo.output_components, colorspace); |
420 | 0 | if (!colorspace) |
421 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot determine colorspace"); |
422 | | |
423 | 0 | image = fz_new_pixmap(ctx, colorspace, cinfo.output_width, cinfo.output_height, NULL, 0); |
424 | |
|
425 | 0 | if (extract_exif_resolution(cinfo.marker_list, &image->xres, &image->yres, NULL)) |
426 | 0 | /* XPS prefers EXIF resolution to JFIF density */; |
427 | 0 | else if (extract_app13_resolution(cinfo.marker_list, &image->xres, &image->yres)) |
428 | 0 | /* XPS prefers APP13 resolution to JFIF density */; |
429 | 0 | else if (cinfo.density_unit == 1) |
430 | 0 | { |
431 | 0 | image->xres = cinfo.X_density; |
432 | 0 | image->yres = cinfo.Y_density; |
433 | 0 | } |
434 | 0 | else if (cinfo.density_unit == 2) |
435 | 0 | { |
436 | 0 | image->xres = cinfo.X_density * 254 / 100; |
437 | 0 | image->yres = cinfo.Y_density * 254 / 100; |
438 | 0 | } |
439 | |
|
440 | 0 | if (image->xres <= 0) image->xres = 96; |
441 | 0 | if (image->yres <= 0) image->yres = 96; |
442 | |
|
443 | 0 | fz_clear_pixmap(ctx, image); |
444 | |
|
445 | 0 | row[0] = fz_malloc(ctx, (size_t)cinfo.output_components * cinfo.output_width); |
446 | 0 | dp = image->samples; |
447 | 0 | stride = image->stride - image->w * (size_t)image->n; |
448 | 0 | while (cinfo.output_scanline < cinfo.output_height) |
449 | 0 | { |
450 | 0 | jpeg_read_scanlines(&cinfo, row, 1); |
451 | | |
452 | | // Invert CMYK polarity for some CMYK images (see comment in filter-dct for details). |
453 | 0 | if (cinfo.out_color_space == JCS_CMYK && cinfo.Adobe_transform == 2) |
454 | 0 | invert_cmyk(row[0], image->stride); |
455 | |
|
456 | 0 | sp = row[0]; |
457 | 0 | for (x = 0; x < cinfo.output_width; x++) |
458 | 0 | { |
459 | 0 | for (k = 0; k < cinfo.output_components; k++) |
460 | 0 | *dp++ = *sp++; |
461 | 0 | } |
462 | 0 | dp += stride; |
463 | 0 | } |
464 | 0 | } |
465 | 0 | fz_always(ctx) |
466 | 0 | { |
467 | 0 | fz_drop_colorspace(ctx, colorspace); |
468 | 0 | fz_free(ctx, row[0]); |
469 | 0 | row[0] = NULL; |
470 | | |
471 | | /* We call jpeg_abort rather than the more usual |
472 | | * jpeg_finish_decompress here. This has the same effect, |
473 | | * but doesn't spew warnings if we didn't read enough data etc. |
474 | | * Annoyingly jpeg_abort can throw |
475 | | */ |
476 | 0 | fz_try(ctx) |
477 | 0 | jpeg_abort((j_common_ptr)&cinfo); |
478 | 0 | fz_catch(ctx) |
479 | 0 | { |
480 | | /* Ignore any errors here */ |
481 | 0 | } |
482 | |
|
483 | 0 | jpeg_destroy_decompress(&cinfo); |
484 | 0 | fz_jpg_mem_term((j_common_ptr)&cinfo); |
485 | 0 | } |
486 | 0 | fz_catch(ctx) |
487 | 0 | { |
488 | 0 | fz_drop_pixmap(ctx, image); |
489 | 0 | fz_rethrow(ctx); |
490 | 0 | } |
491 | | |
492 | 0 | return image; |
493 | 0 | } |
494 | | |
495 | | void |
496 | | fz_load_jpeg_info(fz_context *ctx, const unsigned char *rbuf, size_t rlen, int *xp, int *yp, int *xresp, int *yresp, fz_colorspace **cspacep, uint8_t *orientation) |
497 | 1.01k | { |
498 | 1.01k | struct jpeg_decompress_struct cinfo; |
499 | 1.01k | struct jpeg_error_mgr err; |
500 | 1.01k | struct jpeg_source_mgr src; |
501 | 1.01k | fz_colorspace *icc = NULL; |
502 | | |
503 | 1.01k | *cspacep = NULL; |
504 | 1.01k | if (orientation) |
505 | 1.01k | *orientation = 0; |
506 | | |
507 | 1.01k | cinfo.mem = NULL; |
508 | 1.01k | cinfo.global_state = 0; |
509 | 1.01k | cinfo.err = jpeg_std_error(&err); |
510 | 1.01k | err.error_exit = error_exit; |
511 | | |
512 | 1.01k | cinfo.client_data = NULL; |
513 | 1.01k | fz_jpg_mem_init((j_common_ptr)&cinfo, ctx); |
514 | | |
515 | 2.03k | fz_try(ctx) |
516 | 2.03k | { |
517 | 1.01k | jpeg_create_decompress(&cinfo); |
518 | | |
519 | 1.01k | cinfo.src = &src; |
520 | 1.01k | src.init_source = init_source; |
521 | 1.01k | src.fill_input_buffer = fill_input_buffer; |
522 | 1.01k | src.skip_input_data = skip_input_data; |
523 | 1.01k | src.resync_to_restart = jpeg_resync_to_restart; |
524 | 1.01k | src.term_source = term_source; |
525 | 1.01k | src.next_input_byte = rbuf; |
526 | 1.01k | src.bytes_in_buffer = rlen; |
527 | | |
528 | 1.01k | jpeg_save_markers(&cinfo, JPEG_APP0+1, 0xffff); |
529 | 1.01k | jpeg_save_markers(&cinfo, JPEG_APP0+13, 0xffff); |
530 | 1.01k | jpeg_save_markers(&cinfo, JPEG_APP0+2, 0xffff); |
531 | | |
532 | 1.01k | jpeg_read_header(&cinfo, 1); |
533 | | |
534 | 1.01k | *xp = cinfo.image_width; |
535 | 1.01k | *yp = cinfo.image_height; |
536 | | |
537 | 1.01k | if (cinfo.num_components == 1) |
538 | 393 | *cspacep = fz_keep_colorspace(ctx, fz_device_gray(ctx)); |
539 | 625 | else if (cinfo.num_components == 3) |
540 | 332 | *cspacep = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); |
541 | 293 | else if (cinfo.num_components == 4) |
542 | 94 | *cspacep = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); |
543 | 1.01k | *cspacep = extract_icc_profile(ctx, cinfo.marker_list, cinfo.num_components, *cspacep); |
544 | 1.01k | if (!*cspacep) |
545 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot determine colorspace"); |
546 | | |
547 | 1.01k | if (extract_exif_resolution(cinfo.marker_list, xresp, yresp, orientation)) |
548 | 1 | /* XPS prefers EXIF resolution to JFIF density */; |
549 | 1.01k | else if (extract_app13_resolution(cinfo.marker_list, xresp, yresp)) |
550 | 0 | /* XPS prefers APP13 resolution to JFIF density */; |
551 | 1.01k | else if (cinfo.density_unit == 1) |
552 | 1 | { |
553 | 1 | *xresp = cinfo.X_density; |
554 | 1 | *yresp = cinfo.Y_density; |
555 | 1 | } |
556 | 1.01k | else if (cinfo.density_unit == 2) |
557 | 16 | { |
558 | 16 | *xresp = cinfo.X_density * 254 / 100; |
559 | 16 | *yresp = cinfo.Y_density * 254 / 100; |
560 | 16 | } |
561 | 1.00k | else |
562 | 1.00k | { |
563 | 1.00k | *xresp = 0; |
564 | 1.00k | *yresp = 0; |
565 | 1.00k | } |
566 | | |
567 | 1.01k | if (*xresp <= 0) *xresp = 96; |
568 | 1.01k | if (*yresp <= 0) *yresp = 96; |
569 | 1.01k | } |
570 | 2.03k | fz_always(ctx) |
571 | 1.01k | { |
572 | 1.01k | jpeg_destroy_decompress(&cinfo); |
573 | 1.01k | fz_jpg_mem_term((j_common_ptr)&cinfo); |
574 | 1.01k | } |
575 | 1.01k | fz_catch(ctx) |
576 | 199 | { |
577 | 199 | fz_drop_colorspace(ctx, icc); |
578 | 199 | fz_rethrow(ctx); |
579 | 199 | } |
580 | 1.01k | } |