2 * Copyright (C) 2020 The Geeqie Team
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 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 /** This is a Will Not Fix */
22 #pragma GCC diagnostic ignored "-Wclobbered"
24 /** @FIXME This is just a copy of image-load-jpeg.cc, with an adjusted
25 * start address for a .cr3 file
30 #include "image-load-cr3.h"
35 #include <gdk-pixbuf/gdk-pixbuf.h>
36 #include <glib-object.h>
42 #include "image-load.h"
44 #include "jpeg-parser.h"
47 struct ImageLoaderJpeg {
48 ImageLoaderBackendCbAreaUpdated area_updated_cb;
49 ImageLoaderBackendCbSize size_cb;
50 ImageLoaderBackendCbAreaPrepared area_prepared_cb;
55 guint requested_width;
56 guint requested_height;
63 /* error handler data */
64 struct error_handler_data {
65 struct jpeg_error_mgr pub;
66 sigjmp_buf setjmp_buffer;
70 /* explode gray image data from jpeg library into rgb components in pixbuf */
72 explode_gray_into_buf (struct jpeg_decompress_struct *cinfo,
79 g_return_if_fail (cinfo != nullptr);
80 g_return_if_fail (cinfo->output_components == 1);
81 g_return_if_fail (cinfo->out_color_space == JCS_GRAYSCALE);
83 /* Expand grey->colour. Expand from the end of the
84 * memory down, so we can use the same buffer.
86 w = cinfo->output_width;
87 for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
91 from = lines[i] + w - 1;
92 to = lines[i] + (w - 1) * 3;
93 for (j = w - 1; j >= 0; j--) {
105 convert_cmyk_to_rgb (struct jpeg_decompress_struct *cinfo,
111 g_return_if_fail (cinfo != nullptr);
112 g_return_if_fail (cinfo->output_components == 4);
113 g_return_if_fail (cinfo->out_color_space == JCS_CMYK);
115 for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
119 for (j = 0; j < cinfo->output_width; j++) {
128 if (cinfo->saw_Adobe_marker) {
134 p[0] = (255 - k)*(255 - c) / 255;
135 p[1] = (255 - k)*(255 - m) / 255;
136 p[2] = (255 - k)*(255 - y) / 255;
145 static gpointer image_loader_cr3_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
147 auto loader = g_new0(ImageLoaderJpeg, 1);
149 loader->area_updated_cb = area_updated_cb;
150 loader->size_cb = size_cb;
151 loader->area_prepared_cb = area_prepared_cb;
157 fatal_error_handler (j_common_ptr cinfo)
159 struct error_handler_data *errmgr;
160 char buffer[JMSG_LENGTH_MAX];
162 errmgr = reinterpret_cast<struct error_handler_data *>(cinfo->err);
164 /* Create the message */
165 (* cinfo->err->format_message) (cinfo, buffer);
167 /* broken check for *error == NULL for robustness against
168 * crappy JPEG library
170 if (errmgr->error && *errmgr->error == nullptr) {
171 g_set_error (errmgr->error,
173 cinfo->err->msg_code == JERR_OUT_OF_MEMORY
174 ? GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY
175 : GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
176 _("Error interpreting JPEG image file (%s)"),
180 siglongjmp (errmgr->setjmp_buffer, 1);
182 g_assert_not_reached ();
186 output_message_handler (j_common_ptr)
188 /* This method keeps libjpeg from dumping crap to stderr */
194 static void image_loader_cr3_read_scanline(struct jpeg_decompress_struct *cinfo, guchar **dptr, guint rowstride)
201 for (i = 0; i < cinfo->rec_outbuf_height; i++)
207 jpeg_read_scanlines (cinfo, lines, cinfo->rec_outbuf_height);
209 switch (cinfo->out_color_space)
212 explode_gray_into_buf (cinfo, lines);
218 convert_cmyk_to_rgb (cinfo, lines);
226 static void init_source (j_decompress_ptr) {}
227 static boolean fill_input_buffer (j_decompress_ptr cinfo)
229 ERREXIT(cinfo, JERR_INPUT_EMPTY);
232 static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
234 auto src = static_cast<struct jpeg_source_mgr*>(cinfo->src);
236 if (static_cast<gulong>(num_bytes) > src->bytes_in_buffer)
238 ERREXIT(cinfo, JERR_INPUT_EOF);
240 else if (num_bytes > 0)
242 src->next_input_byte += static_cast<size_t>(num_bytes);
243 src->bytes_in_buffer -= static_cast<size_t>(num_bytes);
246 static void term_source (j_decompress_ptr) {}
247 static void set_mem_src (j_decompress_ptr cinfo, void* buffer, long nbytes)
249 struct jpeg_source_mgr* src;
251 if (cinfo->src == nullptr)
252 { /* first time for this JPEG object? */
253 cinfo->src = static_cast<struct jpeg_source_mgr *>((*cinfo->mem->alloc_small) (
254 reinterpret_cast<j_common_ptr>(cinfo), JPOOL_PERMANENT,
255 sizeof(struct jpeg_source_mgr)));
258 src = static_cast<struct jpeg_source_mgr*>(cinfo->src);
259 src->init_source = init_source;
260 src->fill_input_buffer = fill_input_buffer;
261 src->skip_input_data = skip_input_data;
262 src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
263 src->term_source = term_source;
264 src->bytes_in_buffer = nbytes;
265 src->next_input_byte = static_cast<JOCTET*>(buffer);
269 static gboolean image_loader_cr3_load (gpointer loader, const guchar *buf, gsize count, GError **error)
271 auto lj = static_cast<ImageLoaderJpeg *>(loader);
272 struct jpeg_decompress_struct cinfo;
273 struct jpeg_decompress_struct cinfo2;
277 guchar *stereo_buf2 = nullptr;
278 guint stereo_length = 0;
280 struct error_handler_data jerr;
282 /** @FIXME Just start search at where full size jpeg should be,
283 * / then search through the file looking for a jpeg end-marker
285 gboolean found = FALSE;
290 while (n < count - 4 && !found)
292 if (memcmp(&buf[n], "mdat", 4) == 0)
294 if (memcmp(&buf[n + 12], "\xFF\xD8", 2) == 0)
299 if (memcmp(&buf[n + 12 + i], "\xFF\xD9", 2) == 0)
323 buf = const_cast<unsigned char *>(buf) + n + 12;
327 MPOData *mpo = jpeg_get_mpo_data(buf, count);
328 if (mpo && mpo->num_images > 1)
335 for (i = 0; i < mpo->num_images; i++)
337 if (mpo->images[i].type_code == 0x20002)
339 if (mpo->images[i].MPIndividualNum == 1)
343 else if (mpo->images[i].MPIndividualNum > num2)
346 num2 = mpo->images[i].MPIndividualNum;
351 if (idx1 >= 0 && idx2 >= 0)
354 stereo_buf2 = const_cast<unsigned char *>(buf) + mpo->images[idx2].offset;
355 stereo_length = mpo->images[idx2].length;
356 buf = const_cast<unsigned char *>(buf) + mpo->images[idx1].offset;
357 count = mpo->images[idx1].length;
360 jpeg_mpo_data_free(mpo);
362 /* setup error handler */
363 cinfo.err = jpeg_std_error (&jerr.pub);
364 if (lj->stereo) cinfo2.err = jpeg_std_error (&jerr.pub);
365 jerr.pub.error_exit = fatal_error_handler;
366 jerr.pub.output_message = output_message_handler;
371 if (sigsetjmp(jerr.setjmp_buffer, 0))
373 /* If we get here, the JPEG code has signaled an error.
374 * We need to clean up the JPEG object, close the input file, and return.
376 jpeg_destroy_decompress(&cinfo);
377 if (lj->stereo) jpeg_destroy_decompress(&cinfo2);
381 jpeg_create_decompress(&cinfo);
383 set_mem_src(&cinfo, const_cast<unsigned char *>(buf), count);
386 jpeg_read_header(&cinfo, TRUE);
390 jpeg_create_decompress(&cinfo2);
391 set_mem_src(&cinfo2, stereo_buf2, stereo_length);
392 jpeg_read_header(&cinfo2, TRUE);
394 if (cinfo.image_width != cinfo2.image_width ||
395 cinfo.image_height != cinfo2.image_height)
397 DEBUG_1("stereo data with different size");
398 jpeg_destroy_decompress(&cinfo2);
405 lj->requested_width = lj->stereo ? cinfo.image_width * 2: cinfo.image_width;
406 lj->requested_height = cinfo.image_height;
407 lj->size_cb(loader, lj->requested_width, lj->requested_height, lj->data);
410 for (cinfo.scale_denom = 2; cinfo.scale_denom <= 8; cinfo.scale_denom *= 2) {
411 jpeg_calc_output_dimensions(&cinfo);
412 if (cinfo.output_width < (lj->stereo ? lj->requested_width / 2 : lj->requested_width) || cinfo.output_height < lj->requested_height) {
413 cinfo.scale_denom /= 2;
417 jpeg_calc_output_dimensions(&cinfo);
420 cinfo2.scale_num = cinfo.scale_num;
421 cinfo2.scale_denom = cinfo.scale_denom;
422 jpeg_calc_output_dimensions(&cinfo2);
423 jpeg_start_decompress(&cinfo2);
427 jpeg_start_decompress(&cinfo);
432 if (cinfo.output_width != cinfo2.output_width ||
433 cinfo.output_height != cinfo2.output_height ||
434 cinfo.out_color_components != cinfo2.out_color_components)
436 DEBUG_1("stereo data with different output size");
437 jpeg_destroy_decompress(&cinfo2);
443 lj->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
444 cinfo.out_color_components == 4 ? TRUE : FALSE,
445 8, lj->stereo ? cinfo.output_width * 2: cinfo.output_width, cinfo.output_height);
449 jpeg_destroy_decompress (&cinfo);
450 if (lj->stereo) jpeg_destroy_decompress (&cinfo2);
453 if (lj->stereo) g_object_set_data(G_OBJECT(lj->pixbuf), "stereo_data", GINT_TO_POINTER(STEREO_PIXBUF_CROSS));
454 lj->area_prepared_cb(loader, lj->data);
456 rowstride = gdk_pixbuf_get_rowstride(lj->pixbuf);
457 dptr = gdk_pixbuf_get_pixels(lj->pixbuf);
458 dptr2 = gdk_pixbuf_get_pixels(lj->pixbuf) + ((cinfo.out_color_components == 4) ? 4 * cinfo.output_width : 3 * cinfo.output_width);
461 while (cinfo.output_scanline < cinfo.output_height && !lj->abort)
463 guint scanline = cinfo.output_scanline;
464 image_loader_cr3_read_scanline(&cinfo, &dptr, rowstride);
465 lj->area_updated_cb(loader, 0, scanline, cinfo.output_width, cinfo.rec_outbuf_height, lj->data);
468 guint scanline = cinfo2.output_scanline;
469 image_loader_cr3_read_scanline(&cinfo2, &dptr2, rowstride);
470 lj->area_updated_cb(loader, cinfo.output_width, scanline, cinfo2.output_width, cinfo2.rec_outbuf_height, lj->data);
474 jpeg_finish_decompress(&cinfo);
475 jpeg_destroy_decompress(&cinfo);
478 jpeg_finish_decompress(&cinfo);
479 jpeg_destroy_decompress(&cinfo);
485 static void image_loader_cr3_set_size(gpointer loader, int width, int height)
487 auto lj = static_cast<ImageLoaderJpeg *>(loader);
488 lj->requested_width = width;
489 lj->requested_height = height;
492 static GdkPixbuf* image_loader_cr3_get_pixbuf(gpointer loader)
494 auto lj = static_cast<ImageLoaderJpeg *>(loader);
498 static gchar* image_loader_cr3_get_format_name(gpointer)
500 return g_strdup("cr3");
502 static gchar** image_loader_cr3_get_format_mime_types(gpointer)
504 static const gchar *mime[] = {"image/x-canon-cr3", nullptr};
505 return g_strdupv(const_cast<gchar **>(mime));
508 static gboolean image_loader_cr3_close(gpointer, GError **)
513 static void image_loader_cr3_abort(gpointer loader)
515 auto lj = static_cast<ImageLoaderJpeg *>(loader);
519 static void image_loader_cr3_free(gpointer loader)
521 auto lj = static_cast<ImageLoaderJpeg *>(loader);
522 if (lj->pixbuf) g_object_unref(lj->pixbuf);
527 void image_loader_backend_set_cr3(ImageLoaderBackend *funcs)
529 funcs->loader_new = image_loader_cr3_new;
530 funcs->set_size = image_loader_cr3_set_size;
531 funcs->load = image_loader_cr3_load;
532 funcs->write = nullptr;
533 funcs->get_pixbuf = image_loader_cr3_get_pixbuf;
534 funcs->close = image_loader_cr3_close;
535 funcs->abort = image_loader_cr3_abort;
536 funcs->free = image_loader_cr3_free;
538 funcs->get_format_name = image_loader_cr3_get_format_name;
539 funcs->get_format_mime_types = image_loader_cr3_get_format_mime_types;
547 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */