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