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