file-exr.c 11.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * 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
19
20
21
22
23
24
25
26
 */

#include "config.h"

#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

#include "libgimp/stdplugins-intl.h"

#include "openexr-wrapper.h"

27
28
29
#define LOAD_PROC       "file-exr-load"
#define PLUG_IN_BINARY  "file-exr"
#define PLUG_IN_VERSION "0.0.0"
30
31


32
33
typedef struct _Exr      Exr;
typedef struct _ExrClass ExrClass;
34

35
36
37
38
struct _Exr
{
  GimpPlugIn      parent_instance;
};
39

40
struct _ExrClass
41
{
42
  GimpPlugInClass parent_class;
43
44
45
};


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#define EXR_TYPE  (exr_get_type ())
#define EXR (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXR_TYPE, Exr))

GType                   exr_get_type         (void) G_GNUC_CONST;

static GList          * exr_query_procedures (GimpPlugIn           *plug_in);
static GimpProcedure  * exr_create_procedure (GimpPlugIn           *plug_in,
                                              const gchar          *name);

static GimpValueArray * exr_load             (GimpProcedure        *procedure,
                                              GimpRunMode           run_mode,
                                              GFile                *file,
                                              const GimpValueArray *args,
                                              gpointer              run_data);

61
static GimpImage      * load_image           (GFile                *file,
62
63
64
65
66
67
68
69
                                              gboolean              interactive,
                                              GError              **error);
static void             sanitize_comment     (gchar                *comment);


G_DEFINE_TYPE (Exr, exr, GIMP_TYPE_PLUG_IN)

GIMP_MAIN (EXR_TYPE)
70

71

72
static void
73
exr_class_init (ExrClass *klass)
74
{
75
76
77
78
  GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);

  plug_in_class->query_procedures = exr_query_procedures;
  plug_in_class->create_procedure = exr_create_procedure;
79
80
81
}

static void
82
exr_init (Exr *exr)
83
{
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
}

static GList *
exr_query_procedures (GimpPlugIn *plug_in)
{
  return g_list_append (NULL, g_strdup (LOAD_PROC));
}

static GimpProcedure *
exr_create_procedure (GimpPlugIn  *plug_in,
                      const gchar *name)
{
  GimpProcedure *procedure = NULL;

  if (! strcmp (name, LOAD_PROC))
    {
100
101
      procedure = gimp_load_procedure_new (plug_in, name,
                                           GIMP_PDB_PROC_TYPE_PLUGIN,
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
                                           exr_load, NULL, NULL);

      gimp_procedure_set_menu_label (procedure, N_("OpenEXR image"));

      gimp_procedure_set_documentation (procedure,
                                        "Loads files in the OpenEXR file format",
                                        "This plug-in loads OpenEXR files. ",
                                        name);
      gimp_procedure_set_attribution (procedure,
                                      "Dominik Ernst <dernst@gmx.de>, "
                                      "Mukund Sivaraman <muks@banu.com>",
                                      "Dominik Ernst <dernst@gmx.de>, "
                                      "Mukund Sivaraman <muks@banu.com>",
                                      NULL);

      gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
                                          "image/x-exr");
      gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
                                          "exr");
      gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure),
                                      "0,long,0x762f3101");
    }

  return procedure;
}

static GimpValueArray *
exr_load (GimpProcedure        *procedure,
          GimpRunMode           run_mode,
          GFile                *file,
          const GimpValueArray *args,
          gpointer              run_data)
{
  GimpValueArray *return_vals;
136
  GimpImage      *image;
137
  GError         *error  = NULL;
138
139
140
141

  INIT_I18N ();
  gegl_init (NULL, NULL);

142
  image = load_image (file, run_mode == GIMP_RUN_INTERACTIVE,
143
                      &error);
144

145
  if (! image)
146
147
148
    return gimp_procedure_new_return_values (procedure,
                                             GIMP_PDB_EXECUTION_ERROR,
                                             error);
149

150
151
152
  return_vals =  gimp_procedure_new_return_values (procedure,
                                                   GIMP_PDB_SUCCESS,
                                                   NULL);
153

154
  GIMP_VALUES_SET_IMAGE (return_vals, 1, image);
155

156
  return return_vals;
157
158
}

159
static GimpImage *
160
load_image (GFile        *file,
161
162
163
            gboolean      interactive,
            GError      **error)
{
164
165
166
167
  EXRLoader        *loader;
  gint              width;
  gint              height;
  gboolean          has_alpha;
168
  GimpImageBaseType image_type;
169
  GimpPrecision     image_precision;
170
  GimpImage        *image = NULL;
171
  GimpImageType     layer_type;
172
  GimpLayer        *layer;
173
174
175
176
177
178
179
  const Babl       *format;
  GeglBuffer       *buffer = NULL;
  gint              bpp;
  gint              tile_height;
  gchar            *pixels = NULL;
  gint              begin;
  gint32            success = FALSE;
180
181
  gchar            *comment = NULL;
  GimpColorProfile *profile = NULL;
182
183
  guchar           *exif_data;
  guint             exif_size;
184
185
  guchar           *xmp_data;
  guint             xmp_size;
186

187
  gimp_progress_init_printf (_("Opening '%s'"),
188
                             gimp_file_get_utf8_name (file));
189

190
  loader = exr_loader_new (g_file_peek_path (file));
191
192

  if (! loader)
193
194
195
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Error opening file '%s' for reading"),
196
                   gimp_file_get_utf8_name (file));
197
198
199
      goto out;
    }

200
  width  = exr_loader_get_width (loader);
201
  height = exr_loader_get_height (loader);
202

203
204
205
206
  if ((width < 1) || (height < 1))
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Error querying image dimensions from '%s'"),
207
                   gimp_file_get_utf8_name (file));
208
209
210
211
212
213
214
215
      goto out;
    }

  has_alpha = exr_loader_has_alpha (loader) ? TRUE : FALSE;

  switch (exr_loader_get_precision (loader))
    {
    case PREC_UINT:
216
      image_precision = GIMP_PRECISION_U32_LINEAR;
217
218
      break;
    case PREC_HALF:
219
      image_precision = GIMP_PRECISION_HALF_LINEAR;
220
221
      break;
    case PREC_FLOAT:
222
      image_precision = GIMP_PRECISION_FLOAT_LINEAR;
223
224
225
226
      break;
    default:
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Error querying image precision from '%s'"),
227
                   gimp_file_get_utf8_name (file));
228
229
230
231
232
233
234
235
236
237
      goto out;
    }

  switch (exr_loader_get_image_type (loader))
    {
    case IMAGE_TYPE_RGB:
      image_type = GIMP_RGB;
      layer_type = has_alpha ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
      break;
    case IMAGE_TYPE_GRAY:
238
      image_type = GIMP_GRAY;
239
240
241
242
243
      layer_type = has_alpha ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE;
      break;
    default:
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Error querying image type from '%s'"),
244
                   gimp_file_get_utf8_name (file));
245
246
247
248
249
      goto out;
    }

  image = gimp_image_new_with_precision (width, height,
                                         image_type, image_precision);
250
  if (! image)
251
252
253
    {
      g_set_error (error, 0, 0,
                   _("Could not create new image for '%s': %s"),
254
                   gimp_file_get_utf8_name (file),
255
                   gimp_pdb_get_last_error (gimp_get_pdb ()));
256
257
258
      goto out;
    }

259
  gimp_image_set_file (image, file);
260

261
262
263
264
265
266
267
268
269
270
271
  /* try to load an icc profile, it will be generated on the fly if
   * chromaticities are given
   */
  if (image_type == GIMP_RGB)
    {
      profile = exr_loader_get_profile (loader);

      if (profile)
        gimp_image_set_color_profile (image, profile);
    }

272
  layer = gimp_layer_new (image, _("Background"), width, height,
273
274
                          layer_type, 100,
                          gimp_image_get_default_new_layer_mode (image));
275
  gimp_image_insert_layer (image, layer, NULL, 0);
276

277
278
  buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
  format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
279
280
281
282
283
284
285
  bpp = babl_format_get_bytes_per_pixel (format);

  tile_height = gimp_tile_height ();
  pixels = g_new0 (gchar, tile_height * width * bpp);

  for (begin = 0; begin < height; begin += tile_height)
    {
286
287
288
289
      gint end;
      gint num;
      gint i;

290
291
292
293
294
      end = MIN (begin + tile_height, height);
      num = end - begin;

      for (i = 0; i < num; i++)
        {
295
296
          gint retval;

297
298
299
300
301
302
303
          retval = exr_loader_read_pixel_row (loader,
                                              pixels + (i * width * bpp),
                                              bpp, begin + i);
          if (retval < 0)
            {
              g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                           _("Error reading pixel data from '%s'"),
304
                           gimp_file_get_utf8_name (file));
305
306
307
308
309
310
311
312
313
314
              goto out;
            }
        }

      gegl_buffer_set (buffer, GEGL_RECTANGLE (0, begin, width, num),
                       0, NULL, pixels, GEGL_AUTO_ROWSTRIDE);

      gimp_progress_update ((gdouble) begin / (gdouble) height);
    }

315
316
317
318
319
320
  /* try to read the file comment */
  comment = exr_loader_get_comment (loader);
  if (comment)
    {
      GimpParasite *parasite;

321
      sanitize_comment (comment);
322
323
324
325
326
327
328
329
      parasite = gimp_parasite_new ("gimp-comment",
                                    GIMP_PARASITE_PERSISTENT,
                                    strlen (comment) + 1,
                                    comment);
      gimp_image_attach_parasite (image, parasite);
      gimp_parasite_free (parasite);
    }

330
  /* check if the image contains Exif or Xmp data and read it */
331
  exif_data = exr_loader_get_exif (loader, &exif_size);
332
333
334
  xmp_data  = exr_loader_get_xmp  (loader, &xmp_size);

  if (exif_data || xmp_data)
335
    {
336
337
338
339
      GimpMetadata          *metadata = gimp_metadata_new ();
      GimpMetadataLoadFlags  flags    = GIMP_METADATA_LOAD_ALL;

      if (exif_data)
340
        {
341
342
          gimp_metadata_set_from_exif (metadata, exif_data, exif_size, NULL);
          g_free (exif_data);
343
344
        }

345
      if (xmp_data)
346
        {
347
348
          gimp_metadata_set_from_xmp (metadata, xmp_data, xmp_size, NULL);
          g_free (xmp_data);
349
        }
350

351
352
      if (comment)
        flags &= ~GIMP_METADATA_LOAD_COMMENT;
353

354
355
      if (profile)
        flags &= ~GIMP_METADATA_LOAD_COLORSPACE;
356

357
      gimp_image_metadata_load_finish (image, "image/exr",
358
                                       metadata, flags);
359
360
      g_object_unref (metadata);
    }
361

362
363
  gimp_progress_update (1.0);

364
  success = TRUE;
365
366

 out:
367
368
369
370
371
  g_clear_object (&profile);
  g_clear_object (&buffer);
  g_clear_pointer (&pixels, g_free);
  g_clear_pointer (&comment, g_free);
  g_clear_pointer (&loader, exr_loader_unref);
372

373
374
375
  if (success)
    return image;

376
  if (image)
377
378
    gimp_image_delete (image);

379
  return NULL;
380
}
381
382
383

/* copy & pasted from file-jpeg/jpeg-load.c */
static void
384
sanitize_comment (gchar *comment)
385
{
386
387
388
  const gchar *start_invalid;

  if (! g_utf8_validate (comment, -1, &start_invalid))
389
    {
390
      guchar *c;
391

392
      for (c = (guchar *) start_invalid; *c; c++)
393
394
395
396
        {
          if (*c > 126 || (*c < 32 && *c != '\t' && *c != '\n' && *c != '\r'))
            *c = '?';
        }
397
398
    }
}