1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <libgimp/gimp.h>
21 #include <libgimp/gimpui.h>
22 
23 #include "libgimp/stdplugins-intl.h"
24 
25 #include "openexr-wrapper.h"
26 
27 #define LOAD_PROC       "file-exr-load"
28 #define PLUG_IN_BINARY  "file-exr"
29 #define PLUG_IN_VERSION "0.0.0"
30 
31 
32 /*
33  * Declare some local functions.
34  */
35 static void     query            (void);
36 static void     run              (const gchar      *name,
37                                   gint              nparams,
38                                   const GimpParam  *param,
39                                   gint             *nreturn_vals,
40                                   GimpParam       **return_vals);
41 
42 static gint32   load_image       (const gchar      *filename,
43                                   gboolean          interactive,
44                                   GError          **error);
45 
46 static void     sanitize_comment (gchar            *comment);
47 
48 
49 /*
50  * Some global variables.
51  */
52 const GimpPlugInInfo PLUG_IN_INFO =
53 {
54   NULL,  /* init_proc  */
55   NULL,  /* quit_proc  */
56   query, /* query_proc */
57   run,   /* run_proc   */
58 };
59 
60 
MAIN()61 MAIN ()
62 
63 
64 static void
65 query (void)
66 {
67   static const GimpParamDef load_args[] =
68   {
69     { GIMP_PDB_INT32,  "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
70     { GIMP_PDB_STRING, "filename",     "The name of the file to load" },
71     { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
72   };
73   static const GimpParamDef load_return_vals[] =
74   {
75     { GIMP_PDB_IMAGE, "image", "Output image" }
76   };
77 
78   gimp_install_procedure (LOAD_PROC,
79                           "Loads files in the OpenEXR file format",
80                           "This plug-in loads OpenEXR files. ",
81                           "Dominik Ernst <dernst@gmx.de>, "
82                           "Mukund Sivaraman <muks@banu.com>",
83                           "Dominik Ernst <dernst@gmx.de>, "
84                           "Mukund Sivaraman <muks@banu.com>",
85                           PLUG_IN_VERSION,
86                           N_("OpenEXR image"),
87                           NULL,
88                           GIMP_PLUGIN,
89                           G_N_ELEMENTS (load_args),
90                           G_N_ELEMENTS (load_return_vals),
91                           load_args, load_return_vals);
92 
93   gimp_register_file_handler_mime (LOAD_PROC, "image/x-exr");
94   gimp_register_magic_load_handler (LOAD_PROC,
95                                     "exr",
96                                     "",
97                                     "0,long,0x762f3101");
98 }
99 
100 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)101 run (const gchar      *name,
102      gint              nparams,
103      const GimpParam  *param,
104      gint             *nreturn_vals,
105      GimpParam       **return_vals)
106 {
107   static GimpParam  values[2];
108   GimpRunMode       run_mode;
109   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
110   gint32            image_ID;
111   GError           *error  = NULL;
112 
113   INIT_I18N ();
114   gegl_init (NULL, NULL);
115 
116   *nreturn_vals = 1;
117   *return_vals  = values;
118 
119   values[0].type          = GIMP_PDB_STATUS;
120   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
121 
122   if (strcmp (name, LOAD_PROC) == 0)
123     {
124       run_mode = param[0].data.d_int32;
125 
126       image_ID = load_image (param[1].data.d_string,
127                              run_mode == GIMP_RUN_INTERACTIVE, &error);
128 
129       if (image_ID != -1)
130         {
131           *nreturn_vals = 2;
132           values[1].type = GIMP_PDB_IMAGE;
133           values[1].data.d_image = image_ID;
134         }
135       else
136         {
137           status = GIMP_PDB_EXECUTION_ERROR;
138         }
139     }
140   else
141     {
142       status = GIMP_PDB_CALLING_ERROR;
143     }
144 
145   if (status != GIMP_PDB_SUCCESS && error)
146     {
147       *nreturn_vals = 2;
148       values[1].type          = GIMP_PDB_STRING;
149       values[1].data.d_string = error->message;
150     }
151 
152   values[0].data.d_status = status;
153 }
154 
155 static gint32
load_image(const gchar * filename,gboolean interactive,GError ** error)156 load_image (const gchar  *filename,
157             gboolean      interactive,
158             GError      **error)
159 {
160   EXRLoader        *loader;
161   gint              width;
162   gint              height;
163   gboolean          has_alpha;
164   GimpImageBaseType image_type;
165   GimpPrecision     image_precision;
166   gint32            image = -1;
167   GimpImageType     layer_type;
168   gint32            layer;
169   const Babl       *format;
170   GeglBuffer       *buffer = NULL;
171   gint              bpp;
172   gint              tile_height;
173   gchar            *pixels = NULL;
174   gint              begin;
175   gint32            success = FALSE;
176   gchar            *comment = NULL;
177   GimpColorProfile *profile = NULL;
178   guchar           *exif_data;
179   guint             exif_size;
180   guchar           *xmp_data;
181   guint             xmp_size;
182 
183   gimp_progress_init_printf (_("Opening '%s'"),
184                              gimp_filename_to_utf8 (filename));
185 
186   loader = exr_loader_new (filename);
187 
188   if (! loader)
189     {
190       g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
191                    _("Error opening file '%s' for reading"),
192                    gimp_filename_to_utf8 (filename));
193       goto out;
194     }
195 
196   width  = exr_loader_get_width (loader);
197   height = exr_loader_get_height (loader);
198 
199   if ((width < 1) || (height < 1))
200     {
201       g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
202                    _("Error querying image dimensions from '%s'"),
203                    gimp_filename_to_utf8 (filename));
204       goto out;
205     }
206 
207   has_alpha = exr_loader_has_alpha (loader) ? TRUE : FALSE;
208 
209   switch (exr_loader_get_precision (loader))
210     {
211     case PREC_UINT:
212       image_precision = GIMP_PRECISION_U32_LINEAR;
213       break;
214     case PREC_HALF:
215       image_precision = GIMP_PRECISION_HALF_LINEAR;
216       break;
217     case PREC_FLOAT:
218       image_precision = GIMP_PRECISION_FLOAT_LINEAR;
219       break;
220     default:
221       g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
222                    _("Error querying image precision from '%s'"),
223                    gimp_filename_to_utf8 (filename));
224       goto out;
225     }
226 
227   switch (exr_loader_get_image_type (loader))
228     {
229     case IMAGE_TYPE_RGB:
230       image_type = GIMP_RGB;
231       layer_type = has_alpha ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
232       break;
233     case IMAGE_TYPE_GRAY:
234       image_type = GIMP_GRAY;
235       layer_type = has_alpha ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE;
236       break;
237     default:
238       g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
239                    _("Error querying image type from '%s'"),
240                    gimp_filename_to_utf8 (filename));
241       goto out;
242     }
243 
244   image = gimp_image_new_with_precision (width, height,
245                                          image_type, image_precision);
246   if (image == -1)
247     {
248       g_set_error (error, 0, 0,
249                    _("Could not create new image for '%s': %s"),
250                    gimp_filename_to_utf8 (filename), gimp_get_pdb_error ());
251       goto out;
252     }
253 
254   gimp_image_set_filename (image, filename);
255 
256   /* try to load an icc profile, it will be generated on the fly if
257    * chromaticities are given
258    */
259   if (image_type == GIMP_RGB)
260     {
261       profile = exr_loader_get_profile (loader);
262 
263       if (profile)
264         gimp_image_set_color_profile (image, profile);
265     }
266 
267   layer = gimp_layer_new (image, _("Background"), width, height,
268                           layer_type, 100,
269                           gimp_image_get_default_new_layer_mode (image));
270   gimp_image_insert_layer (image, layer, -1, 0);
271 
272   buffer = gimp_drawable_get_buffer (layer);
273   format = gimp_drawable_get_format (layer);
274   bpp = babl_format_get_bytes_per_pixel (format);
275 
276   tile_height = gimp_tile_height ();
277   pixels = g_new0 (gchar, tile_height * width * bpp);
278 
279   for (begin = 0; begin < height; begin += tile_height)
280     {
281       gint end;
282       gint num;
283       gint i;
284 
285       end = MIN (begin + tile_height, height);
286       num = end - begin;
287 
288       for (i = 0; i < num; i++)
289         {
290           gint retval;
291 
292           retval = exr_loader_read_pixel_row (loader,
293                                               pixels + (i * width * bpp),
294                                               bpp, begin + i);
295           if (retval < 0)
296             {
297               g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
298                            _("Error reading pixel data from '%s'"),
299                            gimp_filename_to_utf8 (filename));
300               goto out;
301             }
302         }
303 
304       gegl_buffer_set (buffer, GEGL_RECTANGLE (0, begin, width, num),
305                        0, NULL, pixels, GEGL_AUTO_ROWSTRIDE);
306 
307       gimp_progress_update ((gdouble) begin / (gdouble) height);
308     }
309 
310   /* try to read the file comment */
311   comment = exr_loader_get_comment (loader);
312   if (comment)
313     {
314       GimpParasite *parasite;
315 
316       sanitize_comment (comment);
317       parasite = gimp_parasite_new ("gimp-comment",
318                                     GIMP_PARASITE_PERSISTENT,
319                                     strlen (comment) + 1,
320                                     comment);
321       gimp_image_attach_parasite (image, parasite);
322       gimp_parasite_free (parasite);
323     }
324 
325   /* check if the image contains Exif or Xmp data and read it */
326   exif_data = exr_loader_get_exif (loader, &exif_size);
327   xmp_data  = exr_loader_get_xmp  (loader, &xmp_size);
328 
329   if (exif_data || xmp_data)
330     {
331       GimpMetadata          *metadata = gimp_metadata_new ();
332       GimpMetadataLoadFlags  flags    = GIMP_METADATA_LOAD_ALL;
333 
334       if (exif_data)
335         {
336           gimp_metadata_set_from_exif (metadata, exif_data, exif_size, NULL);
337           g_free (exif_data);
338         }
339 
340       if (xmp_data)
341         {
342           gimp_metadata_set_from_xmp (metadata, xmp_data, xmp_size, NULL);
343           g_free (xmp_data);
344         }
345 
346       if (comment)
347         flags &= ~GIMP_METADATA_LOAD_COMMENT;
348 
349       if (profile)
350         flags &= ~GIMP_METADATA_LOAD_COLORSPACE;
351 
352       gimp_image_metadata_load_finish (image, "image/exr",
353                                        metadata, flags, interactive);
354       g_object_unref (metadata);
355     }
356 
357   gimp_progress_update (1.0);
358 
359   success = TRUE;
360 
361  out:
362   g_clear_object (&profile);
363   g_clear_object (&buffer);
364   g_clear_pointer (&pixels, g_free);
365   g_clear_pointer (&comment, g_free);
366   g_clear_pointer (&loader, exr_loader_unref);
367 
368   if (success)
369     return image;
370 
371   if (image != -1)
372     gimp_image_delete (image);
373 
374   return -1;
375 }
376 
377 /* copy & pasted from file-jpeg/jpeg-load.c */
378 static void
sanitize_comment(gchar * comment)379 sanitize_comment (gchar *comment)
380 {
381   const gchar *start_invalid;
382 
383   if (! g_utf8_validate (comment, -1, &start_invalid))
384     {
385       guchar *c;
386 
387       for (c = (guchar *) start_invalid; *c; c++)
388         {
389           if (*c > 126 || (*c < 32 && *c != '\t' && *c != '\n' && *c != '\r'))
390             *c = '?';
391         }
392     }
393 }
394