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
DEFINE_STD_SET_I18N
71

72

73
static void
74
exr_class_init (ExrClass *klass)
75
{
76
77
78
79
  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;
80
  plug_in_class->set_i18n         = STD_SET_I18N;
81
82
83
}

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

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))
    {
102
103
      procedure = gimp_load_procedure_new (plug_in, name,
                                           GIMP_PDB_PROC_TYPE_PLUGIN,
104
105
                                           exr_load, NULL, NULL);

106
      gimp_procedure_set_menu_label (procedure, _("OpenEXR image"));
107
108

      gimp_procedure_set_documentation (procedure,
109
                                        _("Loads files in the OpenEXR file format"),
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
136
137
                                        "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;
138
  GimpImage      *image;
139
  GError         *error  = NULL;
140
141
142

  gegl_init (NULL, NULL);

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

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

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

155
  GIMP_VALUES_SET_IMAGE (return_vals, 1, image);
156

157
  return return_vals;
158
159
}

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

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

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

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

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

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

  has_alpha = exr_loader_has_alpha (loader) ? TRUE : FALSE;

  switch (exr_loader_get_precision (loader))
    {
    case PREC_UINT:
217
      image_precision = GIMP_PRECISION_U32_LINEAR;
218
219
      break;
    case PREC_HALF:
220
      image_precision = GIMP_PRECISION_HALF_LINEAR;
221
222
      break;
    case PREC_FLOAT:
223
      image_precision = GIMP_PRECISION_FLOAT_LINEAR;
224
225
226
227
      break;
    default:
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Error querying image precision from '%s'"),
228
                   gimp_file_get_utf8_name (file));
229
230
231
232
233
234
235
236
237
238
      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:
239
      image_type = GIMP_GRAY;
240
241
242
243
244
      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'"),
245
                   gimp_file_get_utf8_name (file));
246
247
248
249
250
      goto out;
    }

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

260
  gimp_image_set_file (image, file);
261

262
263
264
265
266
267
268
269
270
271
272
  /* 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);
    }

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

278
279
  buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
  format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
280
281
282
283
284
285
286
  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)
    {
287
288
289
290
      gint end;
      gint num;
      gint i;

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

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

298
299
300
301
302
303
304
          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'"),
305
                           gimp_file_get_utf8_name (file));
306
307
308
309
310
311
312
313
314
315
              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);
    }

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

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

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

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

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

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

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

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

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

363
364
  gimp_progress_update (1.0);

365
  success = TRUE;
366
367

 out:
368
369
370
371
372
  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);
373

374
375
376
  if (success)
    return image;

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

380
  return NULL;
381
}
382
383
384

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

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

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