1 /*
2     This file is part of darktable,
3     Copyright (C) 2012-2020 darktable developers.
4 
5     darktable is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     darktable is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with darktable.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #ifdef HAVE_GRAPHICSMAGICK
20 #include "imageio_gm.h"
21 #include "common/colorspaces.h"
22 #include "common/darktable.h"
23 #include "common/exif.h"
24 #include "control/conf.h"
25 #include "develop/develop.h"
26 #include "imageio.h"
27 
28 #include <assert.h>
29 #include <inttypes.h>
30 #include <magick/api.h>
31 #include <memory.h>
32 #include <stdio.h>
33 #include <strings.h>
34 
35 
36 // we only support images with certain filename extensions via GraphicsMagick;
37 // RAWs are excluded as GraphicsMagick would render them with third party
38 // libraries in reduced quality - slow and only 8-bit
_supported_image(const gchar * filename)39 static gboolean _supported_image(const gchar *filename)
40 {
41   const char *extensions_whitelist[] = { "tif",  "tiff", "gif", "jpc", "jp2", "bmp", "dcm", "jng",
42                                          "miff", "mng",  "pbm", "pnm", "ppm", "pgm", NULL };
43   gboolean supported = FALSE;
44   char *ext = g_strrstr(filename, ".");
45   if(!ext) return FALSE;
46   ext++;
47   for(const char **i = extensions_whitelist; *i != NULL; i++)
48     if(!g_ascii_strncasecmp(ext, *i, strlen(*i)))
49     {
50       supported = TRUE;
51       break;
52     }
53   return supported;
54 }
55 
56 
dt_imageio_open_gm(dt_image_t * img,const char * filename,dt_mipmap_buffer_t * mbuf)57 dt_imageio_retval_t dt_imageio_open_gm(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *mbuf)
58 {
59   int err = DT_IMAGEIO_FILE_CORRUPTED;
60   ExceptionInfo exception;
61   Image *image = NULL;
62   ImageInfo *image_info = NULL;
63   uint32_t width, height;
64 
65   if(!_supported_image(filename)) return DT_IMAGEIO_FILE_CORRUPTED;
66 
67   if(!img->exif_inited) (void)dt_exif_read(img, filename);
68 
69   GetExceptionInfo(&exception);
70   image_info = CloneImageInfo((ImageInfo *)NULL);
71 
72   g_strlcpy(image_info->filename, filename, sizeof(image_info->filename));
73 
74   image = ReadImage(image_info, &exception);
75   if(exception.severity != UndefinedException) CatchException(&exception);
76   if(!image)
77   {
78     fprintf(stderr, "[GraphicsMagick_open] image `%s' not found\n", img->filename);
79     err = DT_IMAGEIO_FILE_NOT_FOUND;
80     goto error;
81   }
82 
83   dt_print(DT_DEBUG_IMAGEIO, "[GraphicsMagick_open] image `%s' loading\n", img->filename);
84 
85   if(IsCMYKColorspace(image->colorspace))
86   {
87     fprintf(stderr, "[GraphicsMagick_open] error: CMYK images are not supported.\n");
88     err =  DT_IMAGEIO_FILE_CORRUPTED;
89     goto error;
90   }
91 
92   width = image->columns;
93   height = image->rows;
94 
95   img->width = width;
96   img->height = height;
97 
98   img->buf_dsc.channels = 4;
99   img->buf_dsc.datatype = TYPE_FLOAT;
100 
101   float *mipbuf = (float *)dt_mipmap_cache_alloc(mbuf, img);
102   if(!mipbuf)
103   {
104     fprintf(stderr, "[GraphicsMagick_open] could not alloc full buffer for image `%s'\n", img->filename);
105     err = DT_IMAGEIO_CACHE_FULL;
106     goto error;
107   }
108 
109   for(uint32_t row = 0; row < height; row++)
110   {
111     float *bufprt = mipbuf + (size_t)4 * row * img->width;
112     int ret = DispatchImage(image, 0, row, width, 1, "RGBP", FloatPixel, bufprt, &exception);
113     if(exception.severity != UndefinedException) CatchException(&exception);
114     if(ret != MagickPass)
115     {
116       fprintf(stderr, "[GraphicsMagick_open] error reading image `%s'\n", img->filename);
117       err = DT_IMAGEIO_FILE_CORRUPTED;
118       goto error;
119     }
120   }
121 
122   if(image) DestroyImage(image);
123   if(image_info) DestroyImageInfo(image_info);
124   DestroyExceptionInfo(&exception);
125 
126   img->buf_dsc.filters = 0u;
127   img->flags &= ~DT_IMAGE_RAW;
128   img->flags &= ~DT_IMAGE_HDR;
129   img->flags &= ~DT_IMAGE_S_RAW;
130   img->flags |= DT_IMAGE_LDR;
131 
132   return DT_IMAGEIO_OK;
133 
134 error:
135   if(image) DestroyImage(image);
136   if(image_info) DestroyImageInfo(image_info);
137   DestroyExceptionInfo(&exception);
138   return err;
139 }
140 #endif
141 
142 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
143 // vim: shiftwidth=2 expandtab tabstop=2 cindent
144 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
145