GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
__magick_read__.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2013 CarnĂ« Draug
4 Copyright (C) 2002-2013 Andy Adler
5 Copyright (C) 2008 Thomas L. Scofield
6 Copyright (C) 2010 David Grundberg
7 
8 This file is part of Octave.
9 
10 Octave is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by the
12 Free Software Foundation; either version 3 of the License, or (at your
13 option) any later version.
14 
15 Octave is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with Octave; see the file COPYING. If not, see
22 <http://www.gnu.org/licenses/>.
23 
24 */
25 
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29 
30 #include "file-stat.h"
31 #include "oct-env.h"
32 #include "oct-time.h"
33 
34 #include "defun-dld.h"
35 #include "error.h"
36 #include "ov-struct.h"
37 
38 #include "gripes.h"
39 
40 #ifdef HAVE_MAGICK
41 
42 #include <Magick++.h>
43 #include <clocale>
44 
45 // In theory, it should be enough to check the class:
46 // Magick::ClassType
47 // PseudoClass:
48 // Image is composed of pixels which specify an index in a color palette.
49 // DirectClass:
50 // Image is composed of pixels which represent literal color values.
51 //
52 // GraphicsMagick does not really distinguishes between indexed and
53 // normal images. After reading a file, it decides itself the optimal
54 // way to store the image in memory, independently of the how the
55 // image was stored in the file. That's what ClassType returns. While
56 // it seems to match the original file most of the times, this is
57 // not necessarily true all the times. See
58 // https://sourceforge.net/mailarchive/message.php?msg_id=31180507
59 // In addition to the ClassType, there is also ImageType which has a
60 // type for indexed images (PaletteType and PaletteMatteType). However,
61 // they also don't represent the original image. Not only does DirectClass
62 // can have a PaletteType, but also does a PseudoClass have non Palette
63 // types.
64 //
65 // We can't do better without having format specific code which is
66 // what we are trying to avoid by using a library such as GM. We at
67 // least create workarounds for the most common problems.
68 //
69 // 1) A grayscale jpeg image can report being indexed even though the
70 // JPEG format has no support for indexed images. We can at least
71 // fix this one.
72 // 2) A PNG file is only an indexed image if color type orig is 3 (value comes
73 // from libpng)
74 static bool
75 is_indexed (const Magick::Image& img)
76 {
77  bool retval = false;
78  const std::string format = img.magick ();
79  if (img.classType () == Magick::PseudoClass
80  && format != "JPEG"
81  && (format != "PNG"
82  || const_cast<Magick::Image&> (img).attribute ("PNG:IHDR.color-type-orig") == "3"))
83  retval = true;
84 
85  return retval;
86 }
87 
88 // The depth from depth() is not always correct for us but seems to be the
89 // best value we can get. For example, a grayscale png image with 1 bit
90 // per channel should return a depth of 1 but instead we get 8.
91 // We could check channelDepth() but then, which channel has the data
92 // is not straightforward. So we'd have to check all
93 // the channels and select the highest value. But then, I also
94 // have a 16bit TIFF whose depth returns 16 (correct), but all of the
95 // channels gives 8 (wrong). No idea why, maybe a bug in GM?
96 // Anyway, using depth() seems that only causes problems for binary
97 // images, and the problem with channelDepth() is not making set them
98 // all to 1. So we will guess that if all channels have depth of 1,
99 // then we must have a binary image.
100 // Note that we can't use AllChannels it doesn't work for this.
101 // Instead of checking all of the individual channels, we check one
102 // from RGB, CMYK, grayscale, and transparency.
103 static octave_idx_type
104 get_depth (Magick::Image& img)
105 {
106  octave_idx_type depth = img.depth ();
107  if (depth == 8
108  && img.channelDepth (Magick::RedChannel) == 1
109  && img.channelDepth (Magick::CyanChannel) == 1
110  && img.channelDepth (Magick::OpacityChannel) == 1
111  && img.channelDepth (Magick::GrayChannel) == 1)
112  depth = 1;
113 
114  return depth;
115 }
116 
117 // We need this in case one of the sides of the image being read has
118 // width 1. In those cases, the type will come as scalar instead of range
119 // since that's the behaviour of the colon operator (1:1:1 will be a scalar,
120 // not a range).
121 static Range
123 {
124  Range output;
125  if (region.is_range ())
126  output = region.range_value ();
127  else if (region.is_scalar_type ())
128  {
129  double value = region.scalar_value ();
130  output = Range (value, value);
131  }
132  else
133  error ("__magick_read__: unknow datatype for Region option");
134 
135  return output;
136 }
137 
138 static std::map<std::string, octave_idx_type>
140 {
141  std::map<std::string, octave_idx_type> region;
142  const Cell pixel_region = options.getfield ("region").cell_value ();
143 
144  // Subtract 1 to account for 0 indexing.
145  const Range rows = get_region_range (pixel_region (0));
146  const Range cols = get_region_range (pixel_region (1));
147  region["row_start"] = rows.base () -1;
148  region["col_start"] = cols.base () -1;
149  region["row_end"] = rows.max () -1;
150  region["col_end"] = cols.max () -1;
151 
152  // Length of the area to load into the Image Pixel Cache. We use max and
153  // min to account for cases where last element of range is the range limit.
154  region["row_cache"] = region["row_end"] - region["row_start"] +1;
155  region["col_cache"] = region["col_end"] - region["col_start"] +1;
156 
157  // How much we have to shift in the memory when doing the loops.
158  region["row_shift"] = region["col_cache"] * rows.inc ();
159  region["col_shift"] = region["col_cache"] *
160  (region["row_cache"] + rows.inc () -1) - cols.inc ();
161 
162  // The actual height and width of the output image
163  region["row_out"] = rows.nelem ();
164  region["col_out"] = cols.nelem ();
165 
166  return region;
167 }
168 
169 static octave_value_list
170 read_maps (Magick::Image& img)
171 {
172  // can't call colorMapSize on const Magick::Image
173  const octave_idx_type mapsize = img.colorMapSize ();
174  Matrix cmap = Matrix (mapsize, 3); // colormap
175  ColumnVector amap = ColumnVector (mapsize); // alpha map
176  for (octave_idx_type i = 0; i < mapsize; i++)
177  {
178  const Magick::ColorRGB c = img.colorMap (i);
179  cmap(i,0) = c.red ();
180  cmap(i,1) = c.green ();
181  cmap(i,2) = c.blue ();
182  amap(i) = c.alpha ();
183  }
184  octave_value_list maps;
185  maps(0) = cmap;
186  maps(1) = amap;
187  return maps;
188 }
189 
190 template <class T>
191 static octave_value_list
192 read_indexed_images (const std::vector<Magick::Image>& imvec,
193  const Array<octave_idx_type>& frameidx,
194  const octave_idx_type& nargout,
195  const octave_scalar_map& options)
196 {
197  typedef typename T::element_type P;
198 
199  octave_value_list retval (3, Matrix ());
200 
201  std::map<std::string, octave_idx_type> region = calculate_region (options);
202  const octave_idx_type nFrames = frameidx.length ();
203  const octave_idx_type nRows = region["row_out"];
204  const octave_idx_type nCols = region["col_out"];
205 
206  // imvec has all of the pages of a file, even the ones we are not
207  // interested in. We will use the first image that we will be actually
208  // reading to get information about the image.
209  const octave_idx_type def_elem = frameidx(0);
210 
211  T img = T (dim_vector (nRows, nCols, 1, nFrames));
212  P* img_fvec = img.fortran_vec ();
213 
214  const octave_idx_type row_start = region["row_start"];
215  const octave_idx_type col_start = region["col_start"];
216  const octave_idx_type row_shift = region["row_shift"];
217  const octave_idx_type col_shift = region["col_shift"];
218  const octave_idx_type row_cache = region["row_cache"];
219  const octave_idx_type col_cache = region["col_cache"];
220 
221  // When reading PixelPackets from the Image Pixel Cache, they come in
222  // row major order. So we keep moving back and forth there so we can
223  // write the image in column major order.
224  octave_idx_type idx = 0;
225  for (octave_idx_type frame = 0; frame < nFrames; frame++)
226  {
227  imvec[frameidx(frame)].getConstPixels (col_start, row_start,
228  col_cache, row_cache);
229 
230  const Magick::IndexPacket *pix
231  = imvec[frameidx(frame)].getConstIndexes ();
232 
233  for (octave_idx_type col = 0; col < nCols; col++)
234  {
235  for (octave_idx_type row = 0; row < nRows; row++)
236  {
237  img_fvec[idx++] = static_cast<P> (*pix);
238  pix += row_shift;
239  }
240  pix -= col_shift;
241  }
242  }
243  retval(0) = octave_value (img);
244 
245  // Only bother reading the colormap if it was requested as output.
246  if (nargout > 1)
247  {
248  // In theory, it should be possible for each frame of an image to
249  // have different colormaps but for Matlab compatibility, we only
250  // return the colormap of the first frame. To obtain the colormaps
251  // of different frames, one needs can either use imfinfo or a for
252  // loop around imread.
253  const octave_value_list maps =
254  read_maps (const_cast<Magick::Image&> (imvec[frameidx(def_elem)]));
255 
256  retval(1) = maps(0);
257 
258  // only interpret alpha channel if it exists and was requested as output
259  if (imvec[def_elem].matte () && nargout >= 3)
260  {
261  const Matrix amap = maps(1).matrix_value ();
262  const double* amap_fvec = amap.fortran_vec ();
263 
264  NDArray alpha (dim_vector (nRows, nCols, 1, nFrames));
265  double* alpha_fvec = alpha.fortran_vec ();
266 
267  // GraphicsMagick stores the alpha values inverted, i.e.,
268  // 1 for transparent and 0 for opaque so we fix that here.
269  const octave_idx_type nPixels = alpha.numel ();
270  for (octave_idx_type pix = 0; pix < nPixels; pix++)
271  alpha_fvec[pix] = 1 - amap_fvec[static_cast<int> (img_fvec[3])];
272 
273  retval(2) = alpha;
274  }
275  }
276 
277  return retval;
278 }
279 
280 // This function is highly repetitive, a bunch of for loops that are
281 // very similar to account for different image types. They are different
282 // enough that trying to reduce the copy and paste would decrease its
283 // readability too much.
284 template <class T>
286 read_images (std::vector<Magick::Image>& imvec,
287  const Array<octave_idx_type>& frameidx,
288  const octave_idx_type& nargout,
289  const octave_scalar_map& options)
290 {
291  typedef typename T::element_type P;
292 
293  octave_value_list retval (3, Matrix ());
294 
295  std::map<std::string, octave_idx_type> region = calculate_region (options);
296  const octave_idx_type nFrames = frameidx.length ();
297  const octave_idx_type nRows = region["row_out"];
298  const octave_idx_type nCols = region["col_out"];
299  T img;
300 
301  // imvec has all of the pages of a file, even the ones we are not
302  // interested in. We will use the first image that we will be actually
303  // reading to get information about the image.
304  const octave_idx_type def_elem = frameidx(0);
305 
306  const octave_idx_type row_start = region["row_start"];
307  const octave_idx_type col_start = region["col_start"];
308  const octave_idx_type row_shift = region["row_shift"];
309  const octave_idx_type col_shift = region["col_shift"];
310  const octave_idx_type row_cache = region["row_cache"];
311  const octave_idx_type col_cache = region["col_cache"];
312 
313  // GraphicsMagick (GM) keeps the image values in memory using whatever
314  // QuantumDepth it was built with independently of the original image
315  // bitdepth. Basically this means that if GM was built with quantum 16
316  // all values are scaled in the uint16 range. If the original image
317  // had an 8 bit depth, we need to rescale it for that range.
318  // However, if the image had a bitdepth of 32, then we will be returning
319  // a floating point image. In this case, the values need to be rescaled
320  // for the range [0 1] (this is what Matlab has documented on the page
321  // about image types but in some cases seems to be doing something else.
322  // See bug #39249).
323  // Finally, we must do the division ourselves (set a divisor) instead of
324  // using quantumOperator for the cases where we will be returning floating
325  // point and want things in the range [0 1]. This is the same reason why
326  // the divisor is of type double.
327  // uint64_t is used in expression because default 32-bit value overflows
328  // when depth() is 32.
329  // TODO in the next release of GraphicsMagick, MaxRGB should be replaced
330  // with QuantumRange since MaxRGB is already deprecated in ImageMagick.
331  double divisor;
332  if (imvec[def_elem].depth () == 32)
334  else
335  divisor = MaxRGB / ((uint64_t (1) << imvec[def_elem].depth ()) - 1);
336 
337  // FIXME: this workaround should probably be fixed in GM by creating a
338  // new ImageType BilevelMatteType
339  // Despite what GM documentation claims, opacity is not only on the types
340  // with Matte on the name. It is possible that an image is completely
341  // black (1 color), and have a second channel set for transparency (2nd
342  // color). Its type will be bilevel since there is no BilevelMatte. The
343  // only way to check for this seems to be by checking matte ().
344  Magick::ImageType type = imvec[def_elem].type ();
345  if (type == Magick::BilevelType && imvec[def_elem].matte ())
346  type = Magick::GrayscaleMatteType;
347 
348  // FIXME: ImageType is the type being used to represent the image in memory
349  // by GM. The real type may be different (see among others bug #36820). For
350  // example, a png file where all channels are equal may report being
351  // grayscale or even bilevel. But we must always return the real image in
352  // file. In some cases, the original image attributes are stored in the
353  // attributes but this is undocumented. This should be fixed in GM so that
354  // a method such as original_type returns an actual Magick::ImageType
355  if (imvec[0].magick () == "PNG")
356  {
357  // These values come from libpng, not GM:
358  // Grayscale = 0
359  // Palette = 2 + 1
360  // RGB = 2
361  // RGB + Alpha = 2 + 4
362  // Grayscale + Alpha = 4
363  // We won't bother with case 3 (palette) since those should be
364  // read by the function to read indexed images
365  const std::string type_str
366  = imvec[0].attribute ("PNG:IHDR.color-type-orig");
367 
368  if (type_str == "0")
369  type = Magick::GrayscaleType;
370  else if (type_str == "2")
371  type = Magick::TrueColorType;
372  else if (type_str == "6")
373  type = Magick::TrueColorMatteType;
374  else if (type_str == "4")
375  type = Magick::GrayscaleMatteType;
376  // Color types 0, 2, and 3 can also have alpha channel, conveyed
377  // via the "tRNS" chunk. For 0 and 2, it's limited to GIF-style
378  // binary transparency, while 3 can have any level of alpha per
379  // palette entry. We thus must check matte() to see if the image
380  // really doesn't have an alpha channel.
381  if (imvec[0].matte ())
382  {
383  if (type == Magick::GrayscaleType)
384  type = Magick::GrayscaleMatteType;
385  else if (type == Magick::TrueColorType)
386  type = Magick::TrueColorMatteType;
387  }
388  }
389 
390  // If the alpha channel was not requested, treat images as if
391  // it doesn't exist.
392  if (nargout < 3)
393  {
394  switch (type)
395  {
396  case Magick::GrayscaleMatteType:
397  type = Magick::GrayscaleType;
398  break;
399 
400  case Magick::PaletteMatteType:
401  type = Magick::PaletteType;
402  break;
403 
404  case Magick::TrueColorMatteType:
405  type = Magick::TrueColorType;
406  break;
407 
408  case Magick::ColorSeparationMatteType:
409  type = Magick::ColorSeparationType;
410  break;
411 
412  default:
413  // Do nothing other than silencing warnings about enumeration
414  // values not being handled in switch.
415  ;
416  }
417  }
418 
419  switch (type)
420  {
421  case Magick::BilevelType: // Monochrome bi-level image
422  case Magick::GrayscaleType: // Grayscale image
423  {
424  img = T (dim_vector (nRows, nCols, 1, nFrames));
425  P *img_fvec = img.fortran_vec ();
426 
427  octave_idx_type idx = 0;
428  for (octave_idx_type frame = 0; frame < nFrames; frame++)
429  {
430  const Magick::PixelPacket *pix
431  = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
432  col_cache, row_cache);
433 
434  for (octave_idx_type col = 0; col < nCols; col++)
435  {
436  for (octave_idx_type row = 0; row < nRows; row++)
437  {
438  img_fvec[idx++] = pix->red / divisor;
439  pix += row_shift;
440  }
441  pix -= col_shift;
442  }
443  }
444  break;
445  }
446 
447  case Magick::GrayscaleMatteType: // Grayscale image with opacity
448  {
449  img = T (dim_vector (nRows, nCols, 1, nFrames));
450  T alpha (dim_vector (nRows, nCols, 1, nFrames));
451  P *img_fvec = img.fortran_vec ();
452  P *a_fvec = alpha.fortran_vec ();
453 
454  octave_idx_type idx = 0;
455  for (octave_idx_type frame = 0; frame < nFrames; frame++)
456  {
457  const Magick::PixelPacket *pix
458  = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
459  col_cache, row_cache);
460 
461  for (octave_idx_type col = 0; col < nCols; col++)
462  {
463  for (octave_idx_type row = 0; row < nRows; row++)
464  {
465  img_fvec[idx] = pix->red / divisor;
466  a_fvec[idx] = (MaxRGB - pix->opacity) / divisor;
467  pix += row_shift;
468  idx++;
469  }
470  pix -= col_shift;
471  }
472  }
473  retval(2) = alpha;
474  break;
475  }
476 
477  case Magick::PaletteType: // Indexed color (palette) image
478  case Magick::TrueColorType: // Truecolor image
479  {
480  img = T (dim_vector (nRows, nCols, 3, nFrames));
481  P *img_fvec = img.fortran_vec ();
482 
483  for (octave_idx_type frame = 0; frame < nFrames; frame++)
484  {
485  const Magick::PixelPacket *pix
486  = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
487  col_cache, row_cache);
488 
489  octave_idx_type idx = 0;
490  img_fvec += nRows * nCols * frame;
491  P *rbuf = img_fvec;
492  P *gbuf = img_fvec + nRows * nCols;
493  P *bbuf = img_fvec + nRows * nCols * 2;
494 
495  for (octave_idx_type col = 0; col < nCols; col++)
496  {
497  for (octave_idx_type row = 0; row < nRows; row++)
498  {
499  rbuf[idx] = pix->red / divisor;
500  gbuf[idx] = pix->green / divisor;
501  bbuf[idx] = pix->blue / divisor;
502  pix += row_shift;
503  idx++;
504  }
505  pix -= col_shift;
506  }
507  }
508  break;
509  }
510 
511  case Magick::PaletteMatteType: // Indexed color image with opacity
512  case Magick::TrueColorMatteType: // Truecolor image with opacity
513  {
514  img = T (dim_vector (nRows, nCols, 3, nFrames));
515  T alpha (dim_vector (nRows, nCols, 1, nFrames));
516  P *img_fvec = img.fortran_vec ();
517  P *a_fvec = alpha.fortran_vec ();
518 
519  // Unlike the index for the other channels, this one won't need
520  // to be reset on each frame since it's a separate matrix.
521  octave_idx_type a_idx = 0;
522  for (octave_idx_type frame = 0; frame < nFrames; frame++)
523  {
524  const Magick::PixelPacket *pix
525  = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
526  col_cache, row_cache);
527 
528  octave_idx_type idx = 0;
529  img_fvec += nRows * nCols * frame;
530  P *rbuf = img_fvec;
531  P *gbuf = img_fvec + nRows * nCols;
532  P *bbuf = img_fvec + nRows * nCols * 2;
533 
534  for (octave_idx_type col = 0; col < nCols; col++)
535  {
536  for (octave_idx_type row = 0; row < nRows; row++)
537  {
538  rbuf[idx] = pix->red / divisor;
539  gbuf[idx] = pix->green / divisor;
540  bbuf[idx] = pix->blue / divisor;
541  a_fvec[a_idx++] = (MaxRGB - pix->opacity) / divisor;
542  pix += row_shift;
543  idx++;
544  }
545  pix -= col_shift;
546  }
547  }
548  retval(2) = alpha;
549  break;
550  }
551 
552  case Magick::ColorSeparationType: // Cyan/Magenta/Yellow/Black (CMYK) image
553  {
554  img = T (dim_vector (nRows, nCols, 4, nFrames));
555  P *img_fvec = img.fortran_vec ();
556 
557  for (octave_idx_type frame = 0; frame < nFrames; frame++)
558  {
559  const Magick::PixelPacket *pix
560  = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
561  col_cache, row_cache);
562 
563  octave_idx_type idx = 0;
564  img_fvec += nRows * nCols * frame;
565  P *cbuf = img_fvec;
566  P *mbuf = img_fvec + nRows * nCols;
567  P *ybuf = img_fvec + nRows * nCols * 2;
568  P *kbuf = img_fvec + nRows * nCols * 3;
569 
570  for (octave_idx_type col = 0; col < nCols; col++)
571  {
572  for (octave_idx_type row = 0; row < nRows; row++)
573  {
574  cbuf[idx] = pix->red / divisor;
575  mbuf[idx] = pix->green / divisor;
576  ybuf[idx] = pix->blue / divisor;
577  kbuf[idx] = pix->opacity / divisor;
578  pix += row_shift;
579  idx++;
580  }
581  pix -= col_shift;
582  }
583  }
584  break;
585  }
586 
587  // Cyan, magenta, yellow, and black with alpha (opacity) channel
588  case Magick::ColorSeparationMatteType:
589  {
590  img = T (dim_vector (nRows, nCols, 4, nFrames));
591  T alpha (dim_vector (nRows, nCols, 1, nFrames));
592  P *img_fvec = img.fortran_vec ();
593  P *a_fvec = alpha.fortran_vec ();
594 
595  // Unlike the index for the other channels, this one won't need
596  // to be reset on each frame since it's a separate matrix.
597  octave_idx_type a_idx = 0;
598  for (octave_idx_type frame = 0; frame < nFrames; frame++)
599  {
600  const Magick::PixelPacket *pix
601  = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
602  col_cache, row_cache);
603  // Note that for CMYKColorspace + matte (CMYKA), the opacity is
604  // stored in the assocated IndexPacket.
605  const Magick::IndexPacket *apix
606  = imvec[frameidx(frame)].getConstIndexes ();
607 
608  octave_idx_type idx = 0;
609  img_fvec += nRows * nCols * frame;
610  P *cbuf = img_fvec;
611  P *mbuf = img_fvec + nRows * nCols;
612  P *ybuf = img_fvec + nRows * nCols * 2;
613  P *kbuf = img_fvec + nRows * nCols * 3;
614 
615  for (octave_idx_type col = 0; col < nCols; col++)
616  {
617  for (octave_idx_type row = 0; row < nRows; row++)
618  {
619  cbuf[idx] = pix->red / divisor;
620  mbuf[idx] = pix->green / divisor;
621  ybuf[idx] = pix->blue / divisor;
622  kbuf[idx] = pix->opacity / divisor;
623  a_fvec[a_idx++] = (MaxRGB - *apix) / divisor;
624  pix += row_shift;
625  idx++;
626  }
627  pix -= col_shift;
628  }
629  }
630  retval(2) = alpha;
631  break;
632  }
633 
634  default:
635  error ("__magick_read__: unknown Magick++ image type");
636  return retval;
637  }
638 
639  retval(0) = img;
640  return retval;
641 }
642 
643 // Read a file into vector of image objects.
644 void static
645 read_file (const std::string& filename, std::vector<Magick::Image>& imvec)
646 {
647  try
648  {
649  Magick::readImages (&imvec, filename);
650  }
651  catch (Magick::Warning& w)
652  {
653  warning ("Magick++ warning: %s", w.what ());
654  }
655  catch (Magick::Exception& e)
656  {
657  error ("Magick++ exception: %s", e.what ());
658  error_state = 1;
659  }
660 }
661 
662 static void
664 {
665  static bool initialized = false;
666 
667  if (! initialized)
668  {
669  // Save locale as GraphicsMagick might change this (fixed in
670  // GraphicsMagick since version 1.3.13 released on December 24, 2011)
671  const char *static_locale = setlocale (LC_ALL, NULL);
672  const std::string locale (static_locale);
673 
674  const std::string program_name
676  Magick::InitializeMagick (program_name.c_str ());
677 
678  // Restore locale from before GraphicsMagick initialisation
679  setlocale (LC_ALL, locale.c_str ());
680 
681  if (QuantumDepth < 32)
682  warning ("your version of %s limits images to %d bits per pixel",
683  MagickPackageName, QuantumDepth);
684 
685  initialized = true;
686  }
687 }
688 #endif
689 
690 DEFUN_DLD (__magick_read__, args, nargout,
691  "-*- texinfo -*-\n\
692 @deftypefn {Loadable Function} {[@var{img}, @var{map}, @var{alpha}] =} __magick_read__ (@var{fname}, @var{options})\n\
693 Read image with GraphicsMagick or ImageMagick.\n\
694 \n\
695 This is a private internal function not intended for direct use. Instead\n\
696 use @code{imread}.\n\
697 \n\
698 @seealso{imfinfo, imformats, imread, imwrite}\n\
699 @end deftypefn")
700 {
701  octave_value_list output;
702 
703 #ifndef HAVE_MAGICK
704  gripe_disabled_feature ("imread", "Image IO");
705 #else
706 
708 
709  if (args.length () != 2 || ! args(0).is_string ())
710  {
711  print_usage ();
712  return output;
713  }
714 
715  const octave_scalar_map options = args(1).scalar_map_value ();
716  if (error_state)
717  {
718  error ("__magick_read__: OPTIONS must be a struct");
719  return output;
720  }
721 
722  std::vector<Magick::Image> imvec;
723  read_file (args(0).string_value (), imvec);
724  if (error_state)
725  return output;
726 
727  // Prepare an Array with the indexes for the requested frames.
728  const octave_idx_type nFrames = imvec.size ();
729  Array<octave_idx_type> frameidx;
730  const octave_value indexes = options.getfield ("index");
731  if (indexes.is_string () && indexes.string_value () == "all")
732  {
733  frameidx.resize (dim_vector (1, nFrames));
734  for (octave_idx_type i = 0; i < nFrames; i++)
735  frameidx(i) = i;
736  }
737  else
738  {
739  frameidx = indexes.int_vector_value ();
740  if (error_state)
741  {
742  error ("__magick_read__: invalid value for Index/Frame");
743  return output;
744  }
745  // Fix indexes from base 1 to base 0, and at the same time, make
746  // sure none of the indexes is outside the range of image number.
747  const octave_idx_type n = frameidx.nelem ();
748  for (octave_idx_type i = 0; i < n; i++)
749  {
750  frameidx(i)--;
751  if (frameidx(i) < 0 || frameidx(i) > nFrames - 1)
752  {
753  // We do this check inside the loop because frameidx does not
754  // need to be ordered (this is a feature and even allows for
755  // some frames to be read multiple times).
756  error ("imread: index/frames specified are outside the number of images");
757  return output;
758  }
759  }
760  }
761 
762  // Check that all frames have the same size. We don't do this at the same
763  // time we decode the image because that's done in many different places,
764  // to cover the different types of images which would lead to a lot of
765  // copy and paste.
766  {
767  const unsigned int nRows = imvec[frameidx(0)].rows ();
768  const unsigned int nCols = imvec[frameidx(0)].columns ();
769  const octave_idx_type n = frameidx.nelem ();
770  for (octave_idx_type frame = 0; frame < n; frame++)
771  {
772  if (nRows != imvec[frameidx(frame)].rows () ||
773  nCols != imvec[frameidx(frame)].columns ())
774  {
775  error ("imread: all frames must have the same size but frame %i is different",
776  frameidx(frame) +1);
777  return output;
778  }
779  }
780  }
781 
782  const octave_idx_type depth = get_depth (imvec[frameidx(0)]);
783  if (is_indexed (imvec[frameidx(0)]))
784  {
785  if (depth <= 1)
786  output = read_indexed_images<boolNDArray> (imvec, frameidx,
787  nargout, options);
788  else if (depth <= 8)
789  output = read_indexed_images<uint8NDArray> (imvec, frameidx,
790  nargout, options);
791  else if (depth <= 16)
792  output = read_indexed_images<uint16NDArray> (imvec, frameidx,
793  nargout, options);
794  else
795  {
796  error ("imread: indexed images with depths greater than 16-bit are not supported");
797  return output;
798  }
799  }
800 
801  else
802  {
803  if (depth <= 1)
804  output = read_images<boolNDArray> (imvec, frameidx, nargout, options);
805  else if (depth <= 8)
806  output = read_images<uint8NDArray> (imvec, frameidx, nargout, options);
807  else if (depth <= 16)
808  output = read_images<uint16NDArray> (imvec, frameidx, nargout, options);
809  else if (depth <= 32)
810  output = read_images<FloatNDArray> (imvec, frameidx, nargout, options);
811  else
812  {
813  error ("imread: reading of images with %i-bit depth is not supported",
814  depth);
815  }
816  }
817 
818 #endif
819  return output;
820 }
821 
822 /*
823 ## No test needed for internal helper function.
824 %!assert (1)
825 */
826 
827 #ifdef HAVE_MAGICK
828 
829 template <class T>
830 static uint32NDArray
831 img_float2uint (const T& img)
832 {
833  typedef typename T::element_type P;
834  uint32NDArray out (img.dims ());
835 
836  octave_uint32* out_fvec = out.fortran_vec ();
837  const P* img_fvec = img.fortran_vec ();
838 
840  const octave_idx_type numel = img.numel ();
841  for (octave_idx_type idx = 0; idx < numel; idx++)
842  out_fvec[idx] = img_fvec[idx] * max;
843 
844  return out;
845 }
846 
847 // Gets the bitdepth to be used for an Octave class, i.e, returns 8 for
848 // uint8, 16 for uint16, and 32 for uint32
849 template <class T>
850 static octave_idx_type
852 {
853  typedef typename T::element_type P;
854  const octave_idx_type bitdepth =
855  sizeof (P) * std::numeric_limits<unsigned char>::digits;
856  return bitdepth;
857 }
858 
859 static Magick::Image
861  const octave_idx_type& bitdepth,
862  const Magick::ImageType& type,
863  const Magick::ClassType& klass)
864 {
865  Magick::Image img (Magick::Geometry (nCols, nRows), "black");
866  // Ensure that there are no other references to this image.
867  img.modifyImage ();
868 
869  img.classType (klass);
870  img.type (type);
871  // FIXME: for some reason, setting bitdepth doesn't seem to work for
872  // indexed images.
873  img.depth (bitdepth);
874  switch (type)
875  {
876  case Magick::GrayscaleMatteType:
877  case Magick::TrueColorMatteType:
878  case Magick::ColorSeparationMatteType:
879  case Magick::PaletteMatteType:
880  img.matte (true);
881  break;
882 
883  default:
884  img.matte (false);
885  }
886 
887  return img;
888 }
889 
890 template <class T>
891 static void
892 encode_indexed_images (std::vector<Magick::Image>& imvec,
893  const T& img,
894  const Matrix& cmap)
895 {
896  typedef typename T::element_type P;
897  const octave_idx_type nFrames = img.ndims () < 4 ? 1 : img.dims ()(3);
898  const octave_idx_type nRows = img.rows ();
899  const octave_idx_type nCols = img.columns ();
900  const octave_idx_type cmap_size = cmap.rows ();
901  const octave_idx_type bitdepth = bitdepth_from_class<T> ();
902 
903  // There is no colormap object, we need to build a new one for each frame,
904  // even if it's always the same. We can least get a vector for the Colors.
905  std::vector<Magick::ColorRGB> colormap;
906  {
907  const double* cmap_fvec = cmap.fortran_vec ();
908  const octave_idx_type G_offset = cmap_size;
909  const octave_idx_type B_offset = cmap_size * 2;
910  for (octave_idx_type map_idx = 0; map_idx < cmap_size; map_idx++)
911  colormap.push_back (Magick::ColorRGB (cmap_fvec[map_idx],
912  cmap_fvec[map_idx + G_offset],
913  cmap_fvec[map_idx + B_offset]));
914  }
915 
916  for (octave_idx_type frame = 0; frame < nFrames; frame++)
917  {
918  Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
919  Magick::PaletteType,
920  Magick::PseudoClass);
921 
922  // Insert colormap.
923  m_img.colorMapSize (cmap_size);
924  for (octave_idx_type map_idx = 0; map_idx < cmap_size; map_idx++)
925  m_img.colorMap (map_idx, colormap[map_idx]);
926 
927  // Why are we also setting the pixel values instead of only the
928  // index values? We don't know if a file format supports indexed
929  // images. If we only set the indexes and then try to save the
930  // image as JPEG for example, the indexed values get discarded,
931  // there is no conversion from the indexes, it's the initial values
932  // that get used. An alternative would be to only set the pixel
933  // values (no indexes), then set the image as PseudoClass and GM
934  // would create a colormap for us. However, we wouldn't have control
935  // over the order of that colormap. And that's why we set both.
936  Magick::PixelPacket* pix = m_img.getPixels (0, 0, nCols, nRows);
937  Magick::IndexPacket* ind = m_img.getIndexes ();
938  const P* img_fvec = img.fortran_vec ();
939 
940  octave_idx_type GM_idx = 0;
941  for (octave_idx_type column = 0; column < nCols; column++)
942  {
943  for (octave_idx_type row = 0; row < nRows; row++)
944  {
945  ind[GM_idx] = double (*img_fvec);
946  pix[GM_idx] = m_img.colorMap (double (*img_fvec));
947  img_fvec++;
948  GM_idx += nCols;
949  }
950  GM_idx -= nCols * nRows - 1;
951  }
952 
953  // Save changes to underlying image.
954  m_img.syncPixels ();
955  imvec.push_back (m_img);
956  }
957 }
958 
959 static void
960 encode_bool_image (std::vector<Magick::Image>& imvec, const boolNDArray& img)
961 {
962  const octave_idx_type nFrames = img.ndims () < 4 ? 1 : img.dims ()(3);
963  const octave_idx_type nRows = img.rows ();
964  const octave_idx_type nCols = img.columns ();
965 
966  // The initialized image will be black, this is for the other pixels
967  const Magick::Color white ("white");
968 
969  const bool *img_fvec = img.fortran_vec ();
970  octave_idx_type img_idx = 0;
971  for (octave_idx_type frame = 0; frame < nFrames; frame++)
972  {
973  // For some reason, we can't set the type to Magick::BilevelType or
974  // the output image will be black, changing to white has no effect.
975  // However, this will still work fine and a binary image will be
976  // saved because we are setting the bitdepth to 1.
977  Magick::Image m_img = init_enconde_image (nCols, nRows, 1,
978  Magick::GrayscaleType,
979  Magick::DirectClass);
980 
981  Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
982  octave_idx_type GM_idx = 0;
983  for (octave_idx_type col = 0; col < nCols; col++)
984  {
985  for (octave_idx_type row = 0; row < nRows; row++)
986  {
987  if (img_fvec[img_idx])
988  pix[GM_idx] = white;
989 
990  img_idx++;
991  GM_idx += nCols;
992  }
993  GM_idx -= nCols * nRows - 1;
994  }
995  // Save changes to underlying image.
996  m_img.syncPixels ();
997  // While we could not set it to Bilevel at the start, we can do it
998  // here otherwise some coders won't save it as binary.
999  m_img.type (Magick::BilevelType);
1000  imvec.push_back (m_img);
1001  }
1002 }
1003 
1004 template <class T>
1005 static void
1006 encode_uint_image (std::vector<Magick::Image>& imvec,
1007  const T& img, const T& alpha)
1008 {
1009  typedef typename T::element_type P;
1010  const octave_idx_type channels = img.ndims () < 3 ? 1 : img.dims ()(2);
1011  const octave_idx_type nFrames = img.ndims () < 4 ? 1 : img.dims ()(3);
1012  const octave_idx_type nRows = img.rows ();
1013  const octave_idx_type nCols = img.columns ();
1014  const octave_idx_type bitdepth = bitdepth_from_class<T> ();
1015 
1016  Magick::ImageType type;
1017  const bool has_alpha = ! alpha.is_empty ();
1018  switch (channels)
1019  {
1020  case 1:
1021  if (has_alpha)
1022  type = Magick::GrayscaleMatteType;
1023  else
1024  type = Magick::GrayscaleType;
1025  break;
1026 
1027  case 3:
1028  if (has_alpha)
1029  type = Magick::TrueColorMatteType;
1030  else
1031  type = Magick::TrueColorType;
1032  break;
1033 
1034  case 4:
1035  if (has_alpha)
1036  type = Magick::ColorSeparationMatteType;
1037  else
1038  type = Magick::ColorSeparationType;
1039  break;
1040 
1041  default:
1042  {
1043  // __imwrite should have already filtered this cases
1044  error ("__magick_write__: wrong size on 3rd dimension");
1045  return;
1046  }
1047  }
1048 
1049  // We will be passing the values as integers with depth as specified
1050  // by QuantumDepth (maximum value specified by MaxRGB). This is independent
1051  // of the actual depth of the image. GM will then convert the values but
1052  // while in memory, it always keeps the values as specified by QuantumDepth.
1053  // From GM documentation:
1054  // Color arguments are must be scaled to fit the Quantum size according to
1055  // the range of MaxRGB
1056  const double divisor = static_cast<double>((uint64_t (1) << bitdepth) - 1)
1057  / MaxRGB;
1058 
1059  const P *img_fvec = img.fortran_vec ();
1060  const P *a_fvec = alpha.fortran_vec ();
1061  switch (type)
1062  {
1063  case Magick::GrayscaleType:
1064  {
1065  octave_idx_type GM_idx = 0;
1066  for (octave_idx_type frame = 0; frame < nFrames; frame++)
1067  {
1068  Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
1069  type,
1070  Magick::DirectClass);
1071 
1072  Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
1073  for (octave_idx_type col = 0; col < nCols; col++)
1074  {
1075  for (octave_idx_type row = 0; row < nRows; row++)
1076  {
1077  Magick::Color c;
1078  c.redQuantum (double (*img_fvec) / divisor);
1079  pix[GM_idx] = c;
1080  img_fvec++;
1081  GM_idx += nCols;
1082  }
1083  GM_idx -= nCols * nRows - 1;
1084  }
1085  // Save changes to underlying image.
1086  m_img.syncPixels ();
1087  imvec.push_back (m_img);
1088  }
1089  break;
1090  }
1091 
1092  case Magick::GrayscaleMatteType:
1093  {
1094  octave_idx_type GM_idx = 0;
1095  for (octave_idx_type frame = 0; frame < nFrames; frame++)
1096  {
1097  Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
1098  type,
1099  Magick::DirectClass);
1100 
1101  Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
1102  for (octave_idx_type col = 0; col < nCols; col++)
1103  {
1104  for (octave_idx_type row = 0; row < nRows; row++)
1105  {
1106  Magick::Color c;
1107  c.redQuantum (double (*img_fvec) / divisor);
1108  c.alphaQuantum (MaxRGB - (double (*a_fvec) / divisor));
1109  pix[GM_idx] = c;
1110  img_fvec++;
1111  a_fvec++;
1112  GM_idx += nCols;
1113  }
1114  GM_idx -= nCols * nRows - 1;
1115  }
1116  // Save changes to underlying image.
1117  m_img.syncPixels ();
1118  imvec.push_back (m_img);
1119  }
1120  break;
1121  }
1122 
1123  case Magick::TrueColorType:
1124  {
1125  // The fortran_vec offset for the green and blue channels
1126  const octave_idx_type G_offset = nCols * nRows;
1127  const octave_idx_type B_offset = nCols * nRows * 2;
1128  octave_idx_type GM_idx = 0;
1129  for (octave_idx_type frame = 0; frame < nFrames; frame++)
1130  {
1131  Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
1132  type,
1133  Magick::DirectClass);
1134 
1135  Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
1136  for (octave_idx_type col = 0; col < nCols; col++)
1137  {
1138  for (octave_idx_type row = 0; row < nRows; row++)
1139  {
1140  Magick::Color c (double (*img_fvec) / divisor,
1141  double (img_fvec[G_offset]) / divisor,
1142  double (img_fvec[B_offset]) / divisor);
1143  pix[GM_idx] = c;
1144  img_fvec++;
1145  GM_idx += nCols;
1146  }
1147  GM_idx -= nCols * nRows - 1;
1148  }
1149  // Save changes to underlying image.
1150  m_img.syncPixels ();
1151  imvec.push_back (m_img);
1152  }
1153  break;
1154  }
1155 
1156  case Magick::TrueColorMatteType:
1157  {
1158  // The fortran_vec offset for the green and blue channels
1159  const octave_idx_type G_offset = nCols * nRows;
1160  const octave_idx_type B_offset = nCols * nRows * 2;
1161  octave_idx_type GM_idx = 0;
1162  for (octave_idx_type frame = 0; frame < nFrames; frame++)
1163  {
1164  Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
1165  type,
1166  Magick::DirectClass);
1167 
1168  Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
1169  for (octave_idx_type col = 0; col < nCols; col++)
1170  {
1171  for (octave_idx_type row = 0; row < nRows; row++)
1172  {
1173  Magick::Color c (double (*img_fvec) / divisor,
1174  double (img_fvec[G_offset]) / divisor,
1175  double (img_fvec[B_offset]) / divisor,
1176  MaxRGB - (double (*a_fvec) / divisor));
1177  pix[GM_idx] = c;
1178  img_fvec++;
1179  a_fvec++;
1180  GM_idx += nCols;
1181  }
1182  GM_idx -= nCols * nRows - 1;
1183  }
1184  // Save changes to underlying image.
1185  m_img.syncPixels ();
1186  imvec.push_back (m_img);
1187  }
1188  break;
1189  }
1190 
1191  case Magick::ColorSeparationType:
1192  {
1193  // The fortran_vec offset for the Magenta, Yellow, and blacK channels
1194  const octave_idx_type M_offset = nCols * nRows;
1195  const octave_idx_type Y_offset = nCols * nRows * 2;
1196  const octave_idx_type K_offset = nCols * nRows * 3;
1197  octave_idx_type GM_idx = 0;
1198  for (octave_idx_type frame = 0; frame < nFrames; frame++)
1199  {
1200  Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
1201  type,
1202  Magick::DirectClass);
1203 
1204  Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
1205  for (octave_idx_type col = 0; col < nCols; col++)
1206  {
1207  for (octave_idx_type row = 0; row < nRows; row++)
1208  {
1209  Magick::Color c (double (*img_fvec) / divisor,
1210  double (img_fvec[M_offset]) / divisor,
1211  double (img_fvec[Y_offset]) / divisor,
1212  double (img_fvec[K_offset]) / divisor);
1213  pix[GM_idx] = c;
1214  img_fvec++;
1215  GM_idx += nCols;
1216  }
1217  GM_idx -= nCols * nRows - 1;
1218  }
1219  // Save changes to underlying image.
1220  m_img.syncPixels ();
1221  imvec.push_back (m_img);
1222  }
1223  break;
1224  }
1225 
1226  case Magick::ColorSeparationMatteType:
1227  {
1228  // The fortran_vec offset for the Magenta, Yellow, and blacK channels
1229  const octave_idx_type M_offset = nCols * nRows;
1230  const octave_idx_type Y_offset = nCols * nRows * 2;
1231  const octave_idx_type K_offset = nCols * nRows * 3;
1232  octave_idx_type GM_idx = 0;
1233  for (octave_idx_type frame = 0; frame < nFrames; frame++)
1234  {
1235  Magick::Image m_img = init_enconde_image (nCols, nRows, bitdepth,
1236  type,
1237  Magick::DirectClass);
1238 
1239  Magick::PixelPacket *pix = m_img.getPixels (0, 0, nCols, nRows);
1240  Magick::IndexPacket *ind = m_img.getIndexes ();
1241  for (octave_idx_type col = 0; col < nCols; col++)
1242  {
1243  for (octave_idx_type row = 0; row < nRows; row++)
1244  {
1245  Magick::Color c (double (*img_fvec) / divisor,
1246  double (img_fvec[M_offset]) / divisor,
1247  double (img_fvec[Y_offset]) / divisor,
1248  double (img_fvec[K_offset]) / divisor);
1249  pix[GM_idx] = c;
1250  ind[GM_idx] = MaxRGB - (double (*a_fvec) / divisor);
1251  img_fvec++;
1252  a_fvec++;
1253  GM_idx += nCols;
1254  }
1255  GM_idx -= nCols * nRows - 1;
1256  }
1257  // Save changes to underlying image.
1258  m_img.syncPixels ();
1259  imvec.push_back (m_img);
1260  }
1261  break;
1262  }
1263 
1264  default:
1265  {
1266  error ("__magick_write__: unrecognized Magick::ImageType");
1267  return;
1268  }
1269  }
1270  return;
1271 }
1272 
1273 // Meant to be shared with both imfinfo and imwrite.
1274 static std::map<octave_idx_type, std::string>
1276 {
1277  // GIF Specifications:
1278  //
1279  // Disposal Method - Indicates the way in which the graphic is to
1280  // be treated after being displayed.
1281  //
1282  // 0 - No disposal specified. The decoder is
1283  // not required to take any action.
1284  // 1 - Do not dispose. The graphic is to be left
1285  // in place.
1286  // 2 - Restore to background color. The area used by the
1287  // graphic must be restored to the background color.
1288  // 3 - Restore to previous. The decoder is required to
1289  // restore the area overwritten by the graphic with
1290  // what was there prior to rendering the graphic.
1291  // 4-7 - To be defined.
1292  static std::map<octave_idx_type, std::string> methods;
1293  if (methods.empty ())
1294  {
1295  methods[0] = "doNotSpecify";
1296  methods[1] = "leaveInPlace";
1297  methods[2] = "restoreBG";
1298  methods[3] = "restorePrevious";
1299  }
1300  return methods;
1301 }
1302 static std::map<std::string, octave_idx_type>
1304 {
1305  static std::map<std::string, octave_idx_type> methods;
1306  if (methods.empty ())
1307  {
1308  methods["donotspecify"] = 0;
1309  methods["leaveinplace"] = 1;
1310  methods["restorebg"] = 2;
1311  methods["restoreprevious"] = 3;
1312  }
1313  return methods;
1314 }
1315 
1316 void static
1317 write_file (const std::string& filename,
1318  const std::string& ext,
1319  std::vector<Magick::Image>& imvec)
1320 {
1321  try
1322  {
1323  Magick::writeImages (imvec.begin (), imvec.end (), ext + ":" + filename);
1324  }
1325  catch (Magick::Warning& w)
1326  {
1327  warning ("Magick++ warning: %s", w.what ());
1328  }
1329  catch (Magick::ErrorCoder& e)
1330  {
1331  warning ("Magick++ coder error: %s", e.what ());
1332  }
1333  catch (Magick::Exception& e)
1334  {
1335  error ("Magick++ exception: %s", e.what ());
1336  error_state = 1;
1337  }
1338 }
1339 
1340 #endif
1341 
1342 DEFUN_DLD (__magick_write__, args, ,
1343  "-*- texinfo -*-\n\
1344 @deftypefn {Loadable Function} {} __magick_write__ (@var{fname}, @var{fmt}, @var{img}, @var{map}, @var{options})\n\
1345 Write image with GraphicsMagick or ImageMagick.\n\
1346 \n\
1347 This is a private internal function not intended for direct use. Instead\n\
1348 use @code{imwrite}.\n\
1349 \n\
1350 @seealso{imfinfo, imformats, imread, imwrite}\n\
1351 @end deftypefn")
1352 {
1353  octave_value_list retval;
1354 
1355 #ifndef HAVE_MAGICK
1356  gripe_disabled_feature ("imwrite", "Image IO");
1357 #else
1358 
1360 
1361  if (args.length () != 5 || ! args(0).is_string () || ! args(1).is_string ())
1362  {
1363  print_usage ();
1364  return retval;
1365  }
1366  const std::string filename = args(0).string_value ();
1367  const std::string ext = args(1).string_value ();
1368 
1369  const octave_scalar_map options = args(4).scalar_map_value ();
1370  if (error_state)
1371  {
1372  error ("__magick_write__: OPTIONS must be a struct");
1373  return retval;
1374  }
1375 
1376  const octave_value img = args(2);
1377  const Matrix cmap = args(3).matrix_value ();
1378  if (error_state)
1379  {
1380  error ("__magick_write__: invalid IMG or MAP");
1381  return retval;
1382  }
1383 
1384  std::vector<Magick::Image> imvec;
1385 
1386  if (cmap.is_empty ())
1387  {
1388  const octave_value alpha = options.getfield ("alpha");
1389  if (img.is_bool_type ())
1390  encode_bool_image (imvec, img.bool_array_value ());
1391  else if (img.is_uint8_type ())
1392  encode_uint_image<uint8NDArray> (imvec, img.uint8_array_value (),
1393  alpha.uint8_array_value ());
1394  else if (img.is_uint16_type ())
1395  encode_uint_image<uint16NDArray> (imvec, img.uint16_array_value (),
1396  alpha.uint16_array_value ());
1397  else if (img.is_uint32_type ())
1398  encode_uint_image<uint32NDArray> (imvec, img.uint32_array_value (),
1399  alpha.uint32_array_value ());
1400  else if (img.is_float_type ())
1401  {
1402  // For image formats that support floating point values, we write
1403  // the actual values. For those who don't, we only use the values
1404  // on the range [0 1] and save integer values.
1405  // But here, even for formats that would support floating point
1406  // values, GM seems unable to do that so we at least make them uint32.
1407  uint32NDArray clip_img;
1408  uint32NDArray clip_alpha;
1409  if (img.is_single_type ())
1410  {
1411  clip_img = img_float2uint<FloatNDArray>
1412  (img.float_array_value ());
1413  clip_alpha = img_float2uint<FloatNDArray>
1414  (alpha.float_array_value ());
1415  }
1416  else
1417  {
1418  clip_img = img_float2uint<NDArray> (img.array_value ());
1419  clip_alpha = img_float2uint<NDArray> (alpha.array_value ());
1420  }
1421  encode_uint_image<uint32NDArray> (imvec, clip_img, clip_alpha);
1422  }
1423  else
1424  {
1425  error ("__magick_write__: image type not supported");
1426  return retval;
1427  }
1428  }
1429  else
1430  {
1431  // We should not get floating point indexed images here because we
1432  // converted them in __imwrite__.m. We should probably do it here
1433  // but it would look much messier.
1434  if (img.is_uint8_type ())
1435  encode_indexed_images<uint8NDArray> (imvec, img.uint8_array_value (),
1436  cmap);
1437  else if (img.is_uint16_type ())
1438  encode_indexed_images<uint16NDArray> (imvec, img.uint16_array_value (),
1439  cmap);
1440  else
1441  {
1442  error ("__magick_write__: indexed image must be uint8, uint16 or float.");
1443  return retval;
1444  }
1445  }
1446  static std::map<std::string, octave_idx_type> disposal_methods
1448 
1449  const octave_idx_type nFrames = imvec.size ();
1450 
1451  const octave_idx_type quality = options.getfield ("quality").int_value ();
1452  const ColumnVector delaytime = options.getfield ("delaytime").column_vector_value ();
1453  const Array<std::string> disposalmethod = options.getfield ("disposalmethod").cellstr_value ();
1454  for (octave_idx_type i = 0; i < nFrames; i++)
1455  {
1456  imvec[i].quality (quality);
1457  imvec[i].animationDelay (delaytime(i));
1458  imvec[i].gifDisposeMethod (disposal_methods[disposalmethod(i)]);
1459  }
1460 
1461  // If writemode is set to append, read the image and append to it. Even
1462  // if set to append, make sure that something was read at all.
1463  const std::string writemode = options.getfield ("writemode").string_value ();
1464  if (writemode == "append" && file_stat (filename).exists ())
1465  {
1466  std::vector<Magick::Image> ini_imvec;
1467  read_file (filename, ini_imvec);
1468  if (error_state)
1469  return retval;
1470  if (ini_imvec.size () > 0)
1471  {
1472  ini_imvec.insert (ini_imvec.end (), imvec.begin (), imvec.end ());
1473  ini_imvec.swap (imvec);
1474  }
1475  }
1476 
1477  // FIXME: LoopCount or animationIterations
1478  // How it should work:
1479  //
1480  // This value is only set for the first image in the sequence. Trying
1481  // to set this value with the append mode should have no effect, the
1482  // value used with the first image is the one that counts (that would
1483  // also be Matlab compatible). Thus, the right way to do this would be
1484  // to have an else block on the condition above, and set this only
1485  // when creating a new file. Since Matlab does not interpret a 4D
1486  // matrix as sequence of images to write, its users need to use a for
1487  // loop and set LoopCount only on the first iteration (it actually
1488  // throws warnings otherwise)
1489  //
1490  // Why is this not done the right way:
1491  //
1492  // When GM saves a single image, it discards the value if there is only
1493  // a single image and sets it to "no loop". Since our default is an
1494  // infinite loop, if the user tries to do it the Matlab way (setting
1495  // LoopCount only on the first image) that value will go nowhere.
1496  // See https://sourceforge.net/p/graphicsmagick/bugs/248/
1497  // Because of this, we document to set LoopCount on every iteration
1498  // (in Matlab will cause a lot of warnings), or pass a 4D matrix with
1499  // all frames (won't work in Matlab at all).
1500  // Note that this only needs to be set on the first frame
1501  imvec[0].animationIterations (options.getfield ("loopcount").uint_value ());
1502 
1503  write_file (filename, ext, imvec);
1504  if (error_state)
1505  return retval;
1506 
1507 #endif
1508  return retval;
1509 }
1510 
1511 /*
1512 ## No test needed for internal helper function.
1513 %!assert (1)
1514 */
1515 
1516 // Gets the minimum information from images such as its size and format. Much
1517 // faster than using imfinfo, which slows down a lot since. Note than without
1518 // this, we need to read the image once for imfinfo to set defaults (which is
1519 // done in Octave language), and then again for the actual reading.
1520 DEFUN_DLD (__magick_ping__, args, ,
1521  "-*- texinfo -*-\n\
1522 @deftypefn {Loadable Function} {} __magick_ping__ (@var{fname}, @var{idx})\n\
1523 Ping image information with GraphicsMagick or ImageMagick.\n\
1524 \n\
1525 This is a private internal function not intended for direct use.\n\
1526 \n\
1527 @seealso{imfinfo}\n\
1528 @end deftypefn")
1529 {
1530  octave_value retval;
1531 #ifndef HAVE_MAGICK
1532  gripe_disabled_feature ("imfinfo", "Image IO");
1533 #else
1535 
1536  if (args.length () < 1 || ! args(0).is_string ())
1537  {
1538  print_usage ();
1539  return retval;
1540  }
1541  const std::string filename = args(0).string_value ();
1542  int idx;
1543  if (args.length () > 1)
1544  idx = args(1).int_value () -1;
1545  else
1546  idx = 0;
1547 
1548  Magick::Image img;
1549  img.subImage (idx); // start ping from this image (in case of multi-page)
1550  img.subRange (1); // ping only one of them
1551  try
1552  {
1553  img.ping (filename);
1554  }
1555  catch (Magick::Warning& w)
1556  {
1557  warning ("Magick++ warning: %s", w.what ());
1558  }
1559  catch (Magick::Exception& e)
1560  {
1561  error ("Magick++ exception: %s", e.what ());
1562  return retval;
1563  }
1564 
1565  static const char *fields[] = {"rows", "columns", "format", 0};
1567  ping.setfield ("rows", octave_value (img.rows ()));
1568  ping.setfield ("columns", octave_value (img.columns ()));
1569  ping.setfield ("format", octave_value (img.magick ()));
1570  retval = octave_value (ping);
1571 #endif
1572  return retval;
1573 }
1574 
1575 #ifdef HAVE_MAGICK
1576 static octave_value
1577 magick_to_octave_value (const Magick::CompressionType& magick)
1578 {
1579  switch (magick)
1580  {
1581  case Magick::NoCompression:
1582  return octave_value ("none");
1583  case Magick::BZipCompression:
1584  return octave_value ("bzip");
1585  case Magick::FaxCompression:
1586  return octave_value ("fax3");
1587  case Magick::Group4Compression:
1588  return octave_value ("fax4");
1589  case Magick::JPEGCompression:
1590  return octave_value ("jpeg");
1591  case Magick::LZWCompression:
1592  return octave_value ("lzw");
1593  case Magick::RLECompression:
1594  // This is named "rle" for the HDF, but the same thing is named
1595  // "ccitt" and "PackBits" for binary and non-binary images in TIFF.
1596  return octave_value ("rle");
1597  case Magick::ZipCompression:
1598  return octave_value ("deflate");
1599 
1600  // The following are present only in recent versions of GraphicsMagick.
1601  // At the moment the only use of this would be to have imfinfo report
1602  // the compression method. In the future, someone could implement
1603  // the Compression option for imwrite in which case a macro in
1604  // configure.ac will have to check for their presence of this.
1605  // See bug #39913
1606  // case Magick::LZMACompression:
1607  // return octave_value ("lzma");
1608  // case Magick::JPEG2000Compression:
1609  // return octave_value ("jpeg2000");
1610  // case Magick::JBIG1Compression:
1611  // return octave_value ("jbig1");
1612  // case Magick::JBIG2Compression:
1613  // return octave_value ("jbig2");
1614  default:
1615  return octave_value ("undefined");
1616  }
1617 }
1618 
1619 static octave_value
1620 magick_to_octave_value (const Magick::EndianType& magick)
1621 {
1622  switch (magick)
1623  {
1624  case Magick::LSBEndian:
1625  return octave_value ("little-endian");
1626  case Magick::MSBEndian:
1627  return octave_value ("big-endian");
1628  default:
1629  return octave_value ("undefined");
1630  }
1631 }
1632 
1633 static octave_value
1634 magick_to_octave_value (const Magick::OrientationType& magick)
1635 {
1636  switch (magick)
1637  {
1638  // Values come from the TIFF6 spec
1639  case Magick::TopLeftOrientation:
1640  return octave_value (1);
1641  case Magick::TopRightOrientation:
1642  return octave_value (2);
1643  case Magick::BottomRightOrientation:
1644  return octave_value (3);
1645  case Magick::BottomLeftOrientation:
1646  return octave_value (4);
1647  case Magick::LeftTopOrientation:
1648  return octave_value (5);
1649  case Magick::RightTopOrientation:
1650  return octave_value (6);
1651  case Magick::RightBottomOrientation:
1652  return octave_value (7);
1653  case Magick::LeftBottomOrientation:
1654  return octave_value (8);
1655  default:
1656  return octave_value (1);
1657  }
1658 }
1659 
1660 static octave_value
1661 magick_to_octave_value (const Magick::ResolutionType& magick)
1662 {
1663  switch (magick)
1664  {
1665  case Magick::PixelsPerInchResolution:
1666  return octave_value ("Inch");
1667  case Magick::PixelsPerCentimeterResolution:
1668  return octave_value ("Centimeter");
1669  default:
1670  return octave_value ("undefined");
1671  }
1672 }
1673 
1674 static bool
1675 is_valid_exif (const std::string& val)
1676 {
1677  // Sometimes GM will return the string "unknown" instead of empty
1678  // for an empty value.
1679  return (! val.empty () && val != "unknown");
1680 }
1681 
1682 static void
1683 fill_exif (octave_scalar_map& map, Magick::Image& img,
1684  const std::string& key)
1685 {
1686  const std::string attr = img.attribute ("EXIF:" + key);
1687  if (is_valid_exif (attr))
1688  map.setfield (key, octave_value (attr));
1689  return;
1690 }
1691 
1692 static void
1693 fill_exif_ints (octave_scalar_map& map, Magick::Image& img,
1694  const std::string& key)
1695 {
1696  const std::string attr = img.attribute ("EXIF:" + key);
1697  if (is_valid_exif (attr))
1698  {
1699  // string of the type "float,float,float....."
1700  float number;
1701  ColumnVector values (std::count (attr.begin (), attr.end (), ',') +1);
1702  std::string sub;
1703  std::istringstream sstream (attr);
1704  octave_idx_type n = 0;
1705  while (std::getline (sstream, sub, char (',')))
1706  {
1707  sscanf (sub.c_str (), "%f", &number);
1708  values(n++) = number;
1709  }
1710  map.setfield (key, octave_value (values));
1711  }
1712  return;
1713 }
1714 
1715 static void
1716 fill_exif_floats (octave_scalar_map& map, Magick::Image& img,
1717  const std::string& key)
1718 {
1719  const std::string attr = img.attribute ("EXIF:" + key);
1720  if (is_valid_exif (attr))
1721  {
1722  // string of the type "int/int,int/int,int/int....."
1723  int numerator;
1724  int denominator;
1725  ColumnVector values (std::count (attr.begin (), attr.end (), ',') +1);
1726  std::string sub;
1727  std::istringstream sstream (attr);
1728  octave_idx_type n = 0;
1729  while (std::getline (sstream, sub, ','))
1730  {
1731  sscanf (sub.c_str (), "%i/%i", &numerator, &denominator);
1732  values(n++) = double (numerator) / double (denominator);
1733  }
1734  map.setfield (key, octave_value (values));
1735  }
1736  return;
1737 }
1738 
1739 #endif
1740 
1741 DEFUN_DLD (__magick_finfo__, args, ,
1742  "-*- texinfo -*-\n\
1743 @deftypefn {Loadable Function} {} __magick_finfo__ (@var{fname})\n\
1744 Read image information with GraphicsMagick or ImageMagick.\n\
1745 \n\
1746 This is a private internal function not intended for direct use. Instead\n\
1747 use @code{imfinfo}.\n\
1748 \n\
1749 @seealso{imfinfo, imformats, imread, imwrite}\n\
1750 @end deftypefn")
1751 {
1752  octave_value retval;
1753 
1754 #ifndef HAVE_MAGICK
1755  gripe_disabled_feature ("imfinfo", "Image IO");
1756 #else
1758 
1759  if (args.length () < 1 || ! args(0).is_string ())
1760  {
1761  print_usage ();
1762  return retval;
1763  }
1764  const std::string filename = args(0).string_value ();
1765 
1766  std::vector<Magick::Image> imvec;
1767  read_file (filename, imvec);
1768  if (error_state)
1769  return retval;
1770  const octave_idx_type nFrames = imvec.size ();
1771  const std::string format = imvec[0].magick ();
1772 
1773  // Here's how this function works. We need to return a struct array, one
1774  // struct for each image in the file (remember, there are image
1775  // that allow for multiple images in the same file). Now, Matlab seems
1776  // to have format specific code so the fields on the struct are different
1777  // for each format. It only has a small subset that is common to all
1778  // of them, the others are undocumented. Because we try to abstract from
1779  // the formats we always return the same list of fields (note that with
1780  // GM we support more than 88 formats. That's way more than Matlab, and
1781  // I don't want to write specific code for each of them).
1782  //
1783  // So what we do is we create an octave_scalar_map, fill it with the
1784  // information for that image, and then insert it into an octave_map.
1785  // Because in the same file, different images may have values for
1786  // different fields, we can't create a field only if there's a value.
1787  // Bad things happen if we merge octave_scalar_maps with different
1788  // fields from the others (suppose for example a TIFF file with 4 images,
1789  // where only the third image has a colormap.
1790 
1791  static const char *fields[] =
1792  {
1793  // These are fields that must always appear for Matlab.
1794  "Filename",
1795  "FileModDate",
1796  "FileSize",
1797  "Format",
1798  "FormatVersion",
1799  "Width",
1800  "Height",
1801  "BitDepth",
1802  "ColorType",
1803 
1804  // These are format specific or not existent in Matlab. The most
1805  // annoying thing is that Matlab may have different names for the
1806  // same thing in different formats.
1807  "DelayTime",
1808  "DisposalMethod",
1809  "LoopCount",
1810  "ByteOrder",
1811  "Gamma",
1812  "Chromaticities",
1813  "Comment",
1814  "Quality",
1815  "Compression", // same as CompressionType
1816  "Colormap", // same as ColorTable (in PNG)
1817  "Orientation",
1818  "ResolutionUnit",
1819  "XResolution",
1820  "YResolution",
1821  "Software", // sometimes is an Exif tag
1822  "Make", // actually an Exif tag
1823  "Model", // actually an Exif tag
1824  "DateTime", // actually an Exif tag
1825  "ImageDescription", // actually an Exif tag
1826  "Artist", // actually an Exif tag
1827  "Copyright", // actually an Exif tag
1828  "DigitalCamera",
1829  "GPSInfo",
1830  // Notes for the future: GM allows to get many attributes, and even has
1831  // attribute() to obtain arbitrary ones, that may exist in only some
1832  // cases. The following is a list of some methods and into what possible
1833  // Matlab compatible values they may be converted.
1834  //
1835  // colorSpace() -> PhotometricInterpretation
1836  // backgroundColor() -> BackgroundColor
1837  // interlaceType() -> Interlaced, InterlaceType, and PlanarConfiguration
1838  // label() -> Title
1839  0
1840  };
1841 
1842  // The one we will return at the end
1843  octave_map info (dim_vector (nFrames, 1), string_vector (fields));
1844 
1845  // Some of the fields in the struct are about file information and will be
1846  // the same for all images in the file. So we create a template, fill in
1847  // those values, and make a copy of the template for each image.
1848  octave_scalar_map template_info = (string_vector (fields));
1849 
1850  template_info.setfield ("Format", octave_value (format));
1851  // We can't actually get FormatVersion but even Matlab sometimes can't.
1852  template_info.setfield ("FormatVersion", octave_value (""));
1853 
1854  const file_stat fs (filename);
1855  if (fs)
1856  {
1857  const octave_localtime mtime (fs.mtime ());
1858  const std::string filetime = mtime.strftime ("%e-%b-%Y %H:%M:%S");
1859  template_info.setfield ("Filename", octave_value (filename));
1860  template_info.setfield ("FileModDate", octave_value (filetime));
1861  template_info.setfield ("FileSize", octave_value (fs.size ()));
1862  }
1863  else
1864  {
1865  error ("imfinfo: error reading '%s': %s",
1866  filename.c_str (), fs.error ().c_str ());
1867  return retval;
1868  }
1869 
1870  for (octave_idx_type frame = 0; frame < nFrames; frame++)
1871  {
1872  octave_scalar_map info_frame (template_info);
1873  const Magick::Image img = imvec[frame];
1874 
1875  info_frame.setfield ("Width", octave_value (img.columns ()));
1876  info_frame.setfield ("Height", octave_value (img.rows ()));
1877  info_frame.setfield ("BitDepth",
1878  octave_value (get_depth (const_cast<Magick::Image&> (img))));
1879 
1880  // Stuff related to colormap, image class and type
1881  // Because GM is too smart for us... Read the comments in is_indexed()
1882  {
1883  std::string color_type;
1884  Matrix cmap;
1885  if (is_indexed (img))
1886  {
1887  color_type = "indexed";
1888  cmap =
1889  read_maps (const_cast<Magick::Image&> (img))(0).matrix_value ();
1890  }
1891  else
1892  {
1893  switch (img.type ())
1894  {
1895  case Magick::BilevelType:
1896  case Magick::GrayscaleType:
1897  case Magick::GrayscaleMatteType:
1898  color_type = "grayscale";
1899  break;
1900 
1901  case Magick::TrueColorType:
1902  case Magick::TrueColorMatteType:
1903  color_type = "truecolor";
1904  break;
1905 
1906  case Magick::PaletteType:
1907  case Magick::PaletteMatteType:
1908  // we should never get here or is_indexed needs to be fixed
1909  color_type = "indexed";
1910  break;
1911 
1912  case Magick::ColorSeparationType:
1913  case Magick::ColorSeparationMatteType:
1914  color_type = "CMYK";
1915  break;
1916 
1917  default:
1918  color_type = "undefined";
1919  }
1920  }
1921  info_frame.setfield ("ColorType", octave_value (color_type));
1922  info_frame.setfield ("Colormap", octave_value (cmap));
1923  }
1924 
1925  {
1926  // Not all images have chroma values. In such cases, they'll
1927  // be all zeros. So rather than send a matrix of zeros, we will
1928  // check for that, and send an empty vector instead.
1929  RowVector chromaticities (8);
1930  double* chroma_fvec = chromaticities.fortran_vec ();
1931  img.chromaWhitePoint (&chroma_fvec[0], &chroma_fvec[1]);
1932  img.chromaRedPrimary (&chroma_fvec[2], &chroma_fvec[3]);
1933  img.chromaGreenPrimary (&chroma_fvec[4], &chroma_fvec[5]);
1934  img.chromaBluePrimary (&chroma_fvec[6], &chroma_fvec[7]);
1935  if (chromaticities.nnz () == 0)
1936  chromaticities = RowVector (0);
1937  info_frame.setfield ("Chromaticities", octave_value (chromaticities));
1938  }
1939 
1940  info_frame.setfield ("Gamma", octave_value (img.gamma ()));
1941  info_frame.setfield ("XResolution", octave_value (img.xResolution ()));
1942  info_frame.setfield ("YResolution", octave_value (img.yResolution ()));
1943  info_frame.setfield ("DelayTime", octave_value (img.animationDelay ()));
1944  info_frame.setfield ("LoopCount",
1945  octave_value (img.animationIterations ()));
1946  info_frame.setfield ("Quality", octave_value (img.quality ()));
1947  info_frame.setfield ("Comment", octave_value (img.comment ()));
1948 
1949  info_frame.setfield ("Compression",
1950  magick_to_octave_value (img.compressType ()));
1951  info_frame.setfield ("Orientation",
1952  magick_to_octave_value (img.orientation ()));
1953  info_frame.setfield ("ResolutionUnit",
1954  magick_to_octave_value (img.resolutionUnits ()));
1955  info_frame.setfield ("ByteOrder",
1956  magick_to_octave_value (img.endian ()));
1957 
1958  // It is not possible to know if there's an Exif field so we just
1959  // check for the Exif Version value. If it does exists, then we
1960  // bother about looking for specific fields.
1961  {
1962  Magick::Image& cimg = const_cast<Magick::Image&> (img);
1963 
1964  // These will be in Exif tags but must appear as fields in the
1965  // base struct array, not as another struct in one of its fields.
1966  // This is likely because they belong to the Baseline TIFF specs
1967  // and may appear out of the Exif tag. So first we check if it
1968  // exists outside the Exif tag.
1969  // See Section 4.6.4, table 4, page 28 of Exif specs version 2.3
1970  // (CIPA DC- 008-Translation- 2010)
1971  static const char *base_exif_str_fields[] =
1972  {
1973  "DateTime",
1974  "ImageDescription",
1975  "Make",
1976  "Model",
1977  "Software",
1978  "Artist",
1979  "Copyright",
1980  0,
1981  };
1982  static const string_vector base_exif_str (base_exif_str_fields);
1983  static const octave_idx_type n_base_exif_str = base_exif_str.numel ();
1984  for (octave_idx_type field = 0; field < n_base_exif_str; field++)
1985  {
1986  info_frame.setfield (base_exif_str[field],
1987  octave_value (cimg.attribute (base_exif_str[field])));
1988  fill_exif (info_frame, cimg, base_exif_str[field]);
1989  }
1990 
1991  octave_scalar_map camera;
1992  octave_scalar_map gps;
1993  if (! cimg.attribute ("EXIF:ExifVersion").empty ())
1994  {
1995  // See Section 4.6.5, table 7 and 8, over pages page 42 to 43
1996  // of Exif specs version 2.3 (CIPA DC- 008-Translation- 2010)
1997 
1998  // Listed on the Exif specs as being of type ASCII.
1999  static const char *exif_str_fields[] =
2000  {
2001  "RelatedSoundFile",
2002  "DateTimeOriginal",
2003  "DateTimeDigitized",
2004  "SubSecTime",
2005  "DateTimeOriginal",
2006  "SubSecTimeOriginal",
2007  "SubSecTimeDigitized",
2008  "ImageUniqueID",
2009  "CameraOwnerName",
2010  "BodySerialNumber",
2011  "LensMake",
2012  "LensModel",
2013  "LensSerialNumber",
2014  "SpectralSensitivity",
2015  // These last two are of type undefined but most likely will
2016  // be strings. Even if they're not GM returns a string anyway.
2017  "UserComment",
2018  "MakerComment",
2019  0
2020  };
2021  static const string_vector exif_str (exif_str_fields);
2022  static const octave_idx_type n_exif_str = exif_str.numel ();
2023  for (octave_idx_type field = 0; field < n_exif_str; field++)
2024  fill_exif (camera, cimg, exif_str[field]);
2025 
2026  // Listed on the Exif specs as being of type SHORT or LONG.
2027  static const char *exif_int_fields[] =
2028  {
2029  "ColorSpace",
2030  "ExifImageWidth", // PixelXDimension (CPixelXDimension in Matlab)
2031  "ExifImageHeight", // PixelYDimension (CPixelYDimension in Matlab)
2032  "PhotographicSensitivity",
2033  "StandardOutputSensitivity",
2034  "RecommendedExposureIndex",
2035  "ISOSpeed",
2036  "ISOSpeedLatitudeyyy",
2037  "ISOSpeedLatitudezzz",
2038  "FocalPlaneResolutionUnit",
2039  "FocalLengthIn35mmFilm",
2040  // Listed as SHORT or LONG but with more than 1 count.
2041  "SubjectArea",
2042  "SubjectLocation",
2043  // While the following are an integer, their value have a meaning
2044  // that must be represented as a string for Matlab compatibility.
2045  // For example, a 3 on ExposureProgram, would return
2046  // "Aperture priority" as defined on the Exif specs.
2047  "ExposureProgram",
2048  "SensitivityType",
2049  "MeteringMode",
2050  "LightSource",
2051  "Flash",
2052  "SensingMethod",
2053  "FileSource",
2054  "CustomRendered",
2055  "ExposureMode",
2056  "WhiteBalance",
2057  "SceneCaptureType",
2058  "GainControl",
2059  "Contrast",
2060  "Saturation",
2061  "Sharpness",
2062  "SubjectDistanceRange",
2063  0
2064  };
2065  static const string_vector exif_int (exif_int_fields);
2066  static const octave_idx_type n_exif_int = exif_int.numel ();
2067  for (octave_idx_type field = 0; field < n_exif_int; field++)
2068  fill_exif_ints (camera, cimg, exif_int[field]);
2069 
2070  // Listed as RATIONAL or SRATIONAL
2071  static const char *exif_float_fields[] =
2072  {
2073  "Gamma",
2074  "CompressedBitsPerPixel",
2075  "ExposureTime",
2076  "FNumber",
2077  "ShutterSpeedValue", // SRATIONAL
2078  "ApertureValue",
2079  "BrightnessValue", // SRATIONAL
2080  "ExposureBiasValue", // SRATIONAL
2081  "MaxApertureValue",
2082  "SubjectDistance",
2083  "FocalLength",
2084  "FlashEnergy",
2085  "FocalPlaneXResolution",
2086  "FocalPlaneYResolution",
2087  "ExposureIndex",
2088  "DigitalZoomRatio",
2089  // Listed as RATIONAL or SRATIONAL with more than 1 count.
2090  "LensSpecification",
2091  0
2092  };
2093  static const string_vector exif_float (exif_float_fields);
2094  static const octave_idx_type n_exif_float = exif_float.numel ();
2095  for (octave_idx_type field = 0; field < n_exif_float; field++)
2096  fill_exif_floats (camera, cimg, exif_float[field]);
2097 
2098  // Inside a Exif field, it is possible that there is also a
2099  // GPS field. This is not the same as ExifVersion but seems
2100  // to be how we have to check for it.
2101  if (cimg.attribute ("EXIF:GPSInfo") != "unknown")
2102  {
2103  // The story here is the same as with Exif.
2104  // See Section 4.6.6, table 15 on page 68 of Exif specs
2105  // version 2.3 (CIPA DC- 008-Translation- 2010)
2106 
2107  static const char *gps_str_fields[] =
2108  {
2109  "GPSLatitudeRef",
2110  "GPSLongitudeRef",
2111  "GPSAltitudeRef",
2112  "GPSSatellites",
2113  "GPSStatus",
2114  "GPSMeasureMode",
2115  "GPSSpeedRef",
2116  "GPSTrackRef",
2117  "GPSImgDirectionRef",
2118  "GPSMapDatum",
2119  "GPSDestLatitudeRef",
2120  "GPSDestLongitudeRef",
2121  "GPSDestBearingRef",
2122  "GPSDestDistanceRef",
2123  "GPSDateStamp",
2124  0
2125  };
2126  static const string_vector gps_str (gps_str_fields);
2127  static const octave_idx_type n_gps_str = gps_str.numel ();
2128  for (octave_idx_type field = 0; field < n_gps_str; field++)
2129  fill_exif (gps, cimg, gps_str[field]);
2130 
2131  static const char *gps_int_fields[] =
2132  {
2133  "GPSDifferential",
2134  0
2135  };
2136  static const string_vector gps_int (gps_int_fields);
2137  static const octave_idx_type n_gps_int = gps_int.numel ();
2138  for (octave_idx_type field = 0; field < n_gps_int; field++)
2139  fill_exif_ints (gps, cimg, gps_int[field]);
2140 
2141  static const char *gps_float_fields[] =
2142  {
2143  "GPSAltitude",
2144  "GPSDOP",
2145  "GPSSpeed",
2146  "GPSTrack",
2147  "GPSImgDirection",
2148  "GPSDestBearing",
2149  "GPSDestDistance",
2150  "GPSHPositioningError",
2151  // Listed as RATIONAL or SRATIONAL with more than 1 count.
2152  "GPSLatitude",
2153  "GPSLongitude",
2154  "GPSTimeStamp",
2155  "GPSDestLatitude",
2156  "GPSDestLongitude",
2157  0
2158  };
2159  static const string_vector gps_float (gps_float_fields);
2160  static const octave_idx_type n_gps_float = gps_float.numel ();
2161  for (octave_idx_type field = 0; field < n_gps_float; field++)
2162  fill_exif_floats (gps, cimg, gps_float[field]);
2163 
2164  }
2165  }
2166  info_frame.setfield ("DigitalCamera", octave_value (camera));
2167  info_frame.setfield ("GPSInfo", octave_value (gps));
2168  }
2169 
2170  info.fast_elem_insert (frame, info_frame);
2171  }
2172 
2173  if (format == "GIF")
2174  {
2175  static std::map<octave_idx_type, std::string> disposal_methods
2176  = init_disposal_methods ();
2177  string_vector methods (nFrames);
2178  for (octave_idx_type frame = 0; frame < nFrames; frame++)
2179  methods[frame] = disposal_methods[imvec[frame].gifDisposeMethod ()];
2180  info.setfield ("DisposalMethod", Cell (methods));
2181  }
2182  else
2183  info.setfield ("DisposalMethod",
2184  Cell (dim_vector (nFrames, 1), octave_value ("")));
2185 
2186  retval = octave_value (info);
2187 #endif
2188  return retval;
2189 }
2190 
2191 /*
2192 ## No test needed for internal helper function.
2193 %!assert (1)
2194 */
2195 
2196 DEFUN_DLD (__magick_formats__, args, ,
2197  "-*- texinfo -*-\n\
2198 @deftypefn {Loadable Function} {} __magick_imformats__ (@var{formats})\n\
2199 Fill formats info with GraphicsMagick CoderInfo.\n\
2200 \n\
2201 @seealso{imfinfo, imformats, imread, imwrite}\n\
2202 @end deftypefn")
2203 {
2204  octave_value retval;
2205 #ifndef HAVE_MAGICK
2206  gripe_disabled_feature ("imformats", "Image IO");
2207 #else
2208  if (args.length () != 1 || ! args(0).is_map ())
2209  {
2210  print_usage ();
2211  return retval;
2212  }
2213  octave_map formats = args(0).map_value ();
2214 
2216  for (octave_idx_type idx = 0; idx < formats.numel (); idx++)
2217  {
2218  try
2219  {
2220  octave_scalar_map fmt = formats.checkelem (idx);
2221  Magick::CoderInfo coder (fmt.getfield ("coder").string_value ());
2222 
2223  fmt.setfield ("description", octave_value (coder.description ()));
2224  fmt.setfield ("multipage", coder.isMultiFrame () ? true : false);
2225  // default for read and write is a function handle. If we can't
2226  // read or write them, them set it to an empty value
2227  if (! coder.isReadable ())
2228  fmt.setfield ("read", Matrix ());
2229  if (! coder.isWritable ())
2230  fmt.setfield ("write", Matrix ());
2231  formats.fast_elem_insert (idx, fmt);
2232  }
2233  catch (Magick::Exception& e)
2234  {
2235  // Exception here are missing formats. So we remove the format
2236  // from the structure and reduce idx.
2237  formats.delete_elements (idx);
2238  idx--;
2239  }
2240  }
2241  retval = formats;
2242 #endif
2243  return retval;
2244 }
2245 
2246 /*
2247 ## No test needed for internal helper function.
2248 %!assert (1)
2249 */