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