Fix representing percent in c-format string
[geeqie.git] / src / image-load-cr3.cc
1 /*
2  * Copyright (C) 2020 The Geeqie Team
3  *
4  * Authors: Colin Clark
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 2 of the License, or
9  * (at your option) any later version.
10  *
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.
15  *
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.
19  */
20
21 /** This is a Will Not Fix */
22 #pragma GCC diagnostic ignored "-Wclobbered"
23
24 /** @FIXME This is just a copy of image-load-jpeg.cc, with an adjusted
25  * start address for a .cr3 file
26  */
27 #include <config.h>
28
29 #if HAVE_JPEG
30 #include "image-load-cr3.h"
31
32 #include <csetjmp>
33 #include <cstring>
34
35 #include <gdk-pixbuf/gdk-pixbuf.h>
36 #include <glib-object.h>
37 #include <glib.h>
38 #include <jerror.h>
39 #include <jpeglib.h>
40
41 #include "debug.h"
42 #include "image-load.h"
43 #include "intl.h"
44 #include "jpeg-parser.h"
45 #include "typedefs.h"
46
47 struct ImageLoaderJpeg {
48         ImageLoaderBackendCbAreaUpdated area_updated_cb;
49         ImageLoaderBackendCbSize size_cb;
50         ImageLoaderBackendCbAreaPrepared area_prepared_cb;
51
52         gpointer data;
53
54         GdkPixbuf *pixbuf;
55         guint requested_width;
56         guint requested_height;
57
58         gboolean abort;
59         gboolean stereo;
60
61 };
62
63 /* error handler data */
64 struct error_handler_data {
65         struct jpeg_error_mgr pub;
66         sigjmp_buf setjmp_buffer;
67         GError **error;
68 };
69
70 /* explode gray image data from jpeg library into rgb components in pixbuf */
71 static void
72 explode_gray_into_buf (struct jpeg_decompress_struct *cinfo,
73                        guchar **lines)
74 {
75         gint i;
76         gint j;
77         guint w;
78
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);
82
83         /* Expand grey->colour.  Expand from the end of the
84          * memory down, so we can use the same buffer.
85          */
86         w = cinfo->output_width;
87         for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
88                 guchar *from;
89                 guchar *to;
90
91                 from = lines[i] + w - 1;
92                 to = lines[i] + (w - 1) * 3;
93                 for (j = w - 1; j >= 0; j--) {
94                         to[0] = from[0];
95                         to[1] = from[0];
96                         to[2] = from[0];
97                         to -= 3;
98                         from--;
99                 }
100         }
101 }
102
103
104 static void
105 convert_cmyk_to_rgb (struct jpeg_decompress_struct *cinfo,
106                      guchar **lines)
107 {
108         gint i;
109         guint j;
110
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);
114
115         for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
116                 guchar *p;
117
118                 p = lines[i];
119                 for (j = 0; j < cinfo->output_width; j++) {
120                         int c;
121                         int m;
122                         int y;
123                         int k;
124                         c = p[0];
125                         m = p[1];
126                         y = p[2];
127                         k = p[3];
128                         if (cinfo->saw_Adobe_marker) {
129                                 p[0] = k*c / 255;
130                                 p[1] = k*m / 255;
131                                 p[2] = k*y / 255;
132                         }
133                         else {
134                                 p[0] = (255 - k)*(255 - c) / 255;
135                                 p[1] = (255 - k)*(255 - m) / 255;
136                                 p[2] = (255 - k)*(255 - y) / 255;
137                         }
138                         p[3] = 255;
139                         p += 4;
140                 }
141         }
142 }
143
144
145 static gpointer image_loader_cr3_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
146 {
147         auto loader = g_new0(ImageLoaderJpeg, 1);
148
149         loader->area_updated_cb = area_updated_cb;
150         loader->size_cb = size_cb;
151         loader->area_prepared_cb = area_prepared_cb;
152         loader->data = data;
153         return loader;
154 }
155
156 static void
157 fatal_error_handler (j_common_ptr cinfo)
158 {
159         struct error_handler_data *errmgr;
160         char buffer[JMSG_LENGTH_MAX];
161
162         errmgr = reinterpret_cast<struct error_handler_data *>(cinfo->err);
163
164         /* Create the message */
165         (* cinfo->err->format_message) (cinfo, buffer);
166
167         /* broken check for *error == NULL for robustness against
168          * crappy JPEG library
169          */
170         if (errmgr->error && *errmgr->error == nullptr) {
171                 g_set_error (errmgr->error,
172                              GDK_PIXBUF_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)"),
177                              buffer);
178         }
179
180         siglongjmp (errmgr->setjmp_buffer, 1);
181
182         g_assert_not_reached ();
183 }
184
185 static void
186 output_message_handler (j_common_ptr)
187 {
188   /* This method keeps libjpeg from dumping crap to stderr */
189
190   /* do nothing */
191 }
192
193
194 static void image_loader_cr3_read_scanline(struct jpeg_decompress_struct *cinfo, guchar **dptr, guint rowstride)
195 {
196         guchar *lines[4];
197         guchar **lptr;
198         gint i;
199
200         lptr = lines;
201         for (i = 0; i < cinfo->rec_outbuf_height; i++)
202                 {
203                 *lptr++ = *dptr;
204                 *dptr += rowstride;
205                 }
206
207         jpeg_read_scanlines (cinfo, lines, cinfo->rec_outbuf_height);
208
209         switch (cinfo->out_color_space)
210                 {
211                     case JCS_GRAYSCALE:
212                       explode_gray_into_buf (cinfo, lines);
213                       break;
214                     case JCS_RGB:
215                       /* do nothing */
216                       break;
217                     case JCS_CMYK:
218                       convert_cmyk_to_rgb (cinfo, lines);
219                       break;
220                     default:
221                       break;
222                 }
223 }
224
225
226 static void init_source (j_decompress_ptr) {}
227 static boolean fill_input_buffer (j_decompress_ptr cinfo)
228 {
229         ERREXIT(cinfo, JERR_INPUT_EMPTY);
230         return TRUE;
231 }
232 static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
233 {
234         auto src = static_cast<struct jpeg_source_mgr*>(cinfo->src);
235
236         if (static_cast<gulong>(num_bytes) > src->bytes_in_buffer)
237                 {
238                 ERREXIT(cinfo, JERR_INPUT_EOF);
239                 }
240         else if (num_bytes > 0)
241                 {
242                 src->next_input_byte += static_cast<size_t>(num_bytes);
243                 src->bytes_in_buffer -= static_cast<size_t>(num_bytes);
244                 }
245 }
246 static void term_source (j_decompress_ptr) {}
247 static void set_mem_src (j_decompress_ptr cinfo, void* buffer, long nbytes)
248 {
249         struct jpeg_source_mgr* src;
250
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)));
256                 }
257
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);
266 }
267
268
269 static gboolean image_loader_cr3_load (gpointer loader, const guchar *buf, gsize count, GError **error)
270 {
271         auto lj = static_cast<ImageLoaderJpeg *>(loader);
272         struct jpeg_decompress_struct cinfo;
273         struct jpeg_decompress_struct cinfo2;
274         guchar *dptr;
275         guchar *dptr2;
276         guint rowstride;
277         guchar *stereo_buf2 = nullptr;
278         guint stereo_length = 0;
279
280         struct error_handler_data jerr;
281
282 /** @FIXME Just start search at where full size jpeg should be,
283  * / then search through the file looking for a jpeg end-marker
284  */
285         gboolean found = FALSE;
286         gint i;
287         guint n;
288
289         n = 0;
290         while (n < count - 4 && !found)
291                 {
292                 if (memcmp(&buf[n], "mdat", 4) == 0)
293                         {
294                         if (memcmp(&buf[n + 12], "\xFF\xD8", 2) == 0)
295                                 {
296                                 i = 0;
297                                 while (!found )
298                                         {
299                                         if (memcmp(&buf[n + 12 + i], "\xFF\xD9", 2) == 0)
300                                                 {
301                                                 found = TRUE;
302                                                 }
303                                         i++;
304                                         }
305                                 }
306                         else
307                                 {
308                                 break;
309                                 }
310                         }
311                 else
312                         {
313                         n++;
314                         }
315                 }
316
317         if (!found)
318                 {
319                 return FALSE;
320                 }
321
322         count = i;
323         buf = const_cast<unsigned char *>(buf) + n + 12;
324
325         lj->stereo = FALSE;
326
327         MPOData *mpo = jpeg_get_mpo_data(buf, count);
328         if (mpo && mpo->num_images > 1)
329                 {
330                 guint i;
331                 gint idx1 = -1;
332                 gint idx2 = -1;
333                 guint num2 = 1;
334
335                 for (i = 0; i < mpo->num_images; i++)
336                         {
337                         if (mpo->images[i].type_code == 0x20002)
338                                 {
339                                 if (mpo->images[i].MPIndividualNum == 1)
340                                         {
341                                         idx1 = i;
342                                         }
343                                 else if (mpo->images[i].MPIndividualNum > num2)
344                                         {
345                                         idx2 = i;
346                                         num2 = mpo->images[i].MPIndividualNum;
347                                         }
348                                 }
349                         }
350
351                 if (idx1 >= 0 && idx2 >= 0)
352                         {
353                         lj->stereo = TRUE;
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;
358                         }
359                 }
360         jpeg_mpo_data_free(mpo);
361
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;
367
368         jerr.error = error;
369
370
371         if (sigsetjmp(jerr.setjmp_buffer, 0))
372                 {
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.
375                 */
376                 jpeg_destroy_decompress(&cinfo);
377                 if (lj->stereo) jpeg_destroy_decompress(&cinfo2);
378                 return FALSE;
379                 }
380
381         jpeg_create_decompress(&cinfo);
382
383         set_mem_src(&cinfo, const_cast<unsigned char *>(buf), count);
384
385
386         jpeg_read_header(&cinfo, TRUE);
387
388         if (lj->stereo)
389                 {
390                 jpeg_create_decompress(&cinfo2);
391                 set_mem_src(&cinfo2, stereo_buf2, stereo_length);
392                 jpeg_read_header(&cinfo2, TRUE);
393
394                 if (cinfo.image_width != cinfo2.image_width ||
395                     cinfo.image_height != cinfo2.image_height)
396                         {
397                         DEBUG_1("stereo data with different size");
398                         jpeg_destroy_decompress(&cinfo2);
399                         lj->stereo = FALSE;
400                         }
401                 }
402
403
404
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);
408
409         cinfo.scale_num = 1;
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;
414                         break;
415                 }
416         }
417         jpeg_calc_output_dimensions(&cinfo);
418         if (lj->stereo)
419                 {
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);
424                 }
425
426
427         jpeg_start_decompress(&cinfo);
428
429
430         if (lj->stereo)
431                 {
432                 if (cinfo.output_width != cinfo2.output_width ||
433                     cinfo.output_height != cinfo2.output_height ||
434                     cinfo.out_color_components != cinfo2.out_color_components)
435                         {
436                         DEBUG_1("stereo data with different output size");
437                         jpeg_destroy_decompress(&cinfo2);
438                         lj->stereo = FALSE;
439                         }
440                 }
441
442
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);
446
447         if (!lj->pixbuf)
448                 {
449                 jpeg_destroy_decompress (&cinfo);
450                 if (lj->stereo) jpeg_destroy_decompress (&cinfo2);
451                 return 0;
452                 }
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);
455
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);
459
460
461         while (cinfo.output_scanline < cinfo.output_height && !lj->abort)
462                 {
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);
466                 if (lj->stereo)
467                         {
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);
471                         }
472                 }
473
474         jpeg_finish_decompress(&cinfo);
475         jpeg_destroy_decompress(&cinfo);
476         if (lj->stereo)
477                 {
478                 jpeg_finish_decompress(&cinfo);
479                 jpeg_destroy_decompress(&cinfo);
480                 }
481
482         return TRUE;
483 }
484
485 static void image_loader_cr3_set_size(gpointer loader, int width, int height)
486 {
487         auto lj = static_cast<ImageLoaderJpeg *>(loader);
488         lj->requested_width = width;
489         lj->requested_height = height;
490 }
491
492 static GdkPixbuf* image_loader_cr3_get_pixbuf(gpointer loader)
493 {
494         auto lj = static_cast<ImageLoaderJpeg *>(loader);
495         return lj->pixbuf;
496 }
497
498 static gchar* image_loader_cr3_get_format_name(gpointer)
499 {
500         return g_strdup("cr3");
501 }
502 static gchar** image_loader_cr3_get_format_mime_types(gpointer)
503 {
504         static const gchar *mime[] = {"image/x-canon-cr3", nullptr};
505         return g_strdupv(const_cast<gchar **>(mime));
506 }
507
508 static gboolean image_loader_cr3_close(gpointer, GError **)
509 {
510         return TRUE;
511 }
512
513 static void image_loader_cr3_abort(gpointer loader)
514 {
515         auto lj = static_cast<ImageLoaderJpeg *>(loader);
516         lj->abort = TRUE;
517 }
518
519 static void image_loader_cr3_free(gpointer loader)
520 {
521         auto lj = static_cast<ImageLoaderJpeg *>(loader);
522         if (lj->pixbuf) g_object_unref(lj->pixbuf);
523         g_free(lj);
524 }
525
526
527 void image_loader_backend_set_cr3(ImageLoaderBackend *funcs)
528 {
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;
537
538         funcs->get_format_name = image_loader_cr3_get_format_name;
539         funcs->get_format_mime_types = image_loader_cr3_get_format_mime_types;
540 }
541
542
543
544 #endif
545
546
547 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */