changeset 17362:0e14b25c5f0f

Merge with main repository after solving conflicts in text renderers code.
author Andrej Lojdl <andrej.lojdl@gmail.com>
date Fri, 30 Aug 2013 12:21:22 +0200
parents 4ee5b344a4e3 (current diff) 80bf005cdf8e (diff)
children 8a930cffa978
files
diffstat 10 files changed, 498 insertions(+), 308 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/load-save.cc
+++ b/libinterp/corefcn/load-save.cc
@@ -1326,6 +1326,8 @@
     {
       for (int i = argv_idx; i < argc; i++)
         {
+          if (argv[i] == "")
+            continue;  // Skip empty vars for Matlab compatibility
           if (! save_vars (os, argv[i], fmt, save_as_floats))
             warning ("save: no such variable '%s'", argv[i].c_str ());
         }
--- a/libinterp/corefcn/oct-hist.cc
+++ b/libinterp/corefcn/oct-hist.cc
@@ -755,12 +755,13 @@
 @seealso{history_file, history_size, history_timestamp_format_string, history_save}\n\
 @end deftypefn")
 {
+  octave_value retval;
+
   std::string old_history_control = command_history::histcontrol ();
 
   std::string tmp = old_history_control;
 
-  octave_value retval = set_internal_variable (tmp, args, nargout,
-                                               "history_control");
+  retval = set_internal_variable (tmp, args, nargout, "history_control");
 
   if (tmp != old_history_control)
     command_history::process_histcontrol (tmp);
@@ -778,13 +779,15 @@
 @seealso{history_file, history_timestamp_format_string, history_save}\n\
 @end deftypefn")
 {
+  octave_value retval;
+
   int old_history_size = command_history::size ();
 
   int tmp = old_history_size;
 
-  octave_value retval = set_internal_variable (tmp, args, nargout,
-                                               "history_size", -1,
-                                               std::numeric_limits<int>::max ());
+  retval = set_internal_variable (tmp, args, nargout,
+                                  "history_size", -1,
+                                  std::numeric_limits<int>::max ());
 
   if (tmp != old_history_size)
     command_history::set_size (tmp);
@@ -803,12 +806,13 @@
 @seealso{history_size, history_save, history_timestamp_format_string}\n\
 @end deftypefn")
 {
+  octave_value retval;
+
   std::string old_history_file = command_history::file ();
 
   std::string tmp = old_history_file;
 
-  octave_value retval = set_internal_variable (tmp, args, nargout,
-                                               "history_file");
+  retval = set_internal_variable (tmp, args, nargout, "history_file");
 
   if (tmp != old_history_file)
     command_history::set_file (tmp);
@@ -853,12 +857,13 @@
 @seealso{history_control, history_file, history_size, history_timestamp_format_string}\n\
 @end deftypefn")
 {
+  octave_value retval;
+
   bool old_history_save = ! command_history::ignoring_entries ();
 
   bool tmp = old_history_save;
 
-  octave_value retval = set_internal_variable (tmp, args, nargout,
-                                               "history_save");
+  retval = set_internal_variable (tmp, args, nargout, "history_save");
 
   if (tmp != old_history_save)
     command_history::ignore_entries (! tmp);
--- a/libinterp/corefcn/oct-tex-symbols.in
+++ b/libinterp/corefcn/oct-tex-symbols.in
@@ -88,12 +88,12 @@
 rightarrow      0x2192  0xF0AE
 Rightarrow      0x21D2  0xF0DE
 downarrow       0x2193  0xF0AF
-circ            0x00B0  0xF0B0
+circ            0x2218  0xF0B0
 pm              0x00B1  0xF0B1
 geq             0x2265  0xF0B3
 propto          0x221D  0xF0B5
 partial         0x2202  0xF0B6
-bullet          0x2022  0xF0B7
+bullet          0x2219  0xF0B7
 div             0x00F7  0xF0B8
 neq             0x2260  0xF0B9
 aleph           0x2135  0xF0C0
@@ -108,3 +108,5 @@
 0               0x2205  0xF0C6
 mid             0x2223  0xF0BD
 copyright       0x00A9  0xF0E3
+
+deg             0x00B0  0xF0B0
--- a/libinterp/dldfcn/__init_fltk__.cc
+++ b/libinterp/dldfcn/__init_fltk__.cc
@@ -255,7 +255,7 @@
 };
 
 // Parameter controlling how fast we zoom when using the scrool wheel.
-static double wheel_zoom_speed = 0.05;
+static double Vwheel_zoom_speed = 0.05;
 // Parameter controlling the GUI mode.
 static enum { pan_zoom, rotate_zoom, none } gui_mode;
 
@@ -1392,7 +1392,8 @@
 
                   // Determine if we're zooming in or out.
                   const double factor =
-                    (Fl::event_dy () > 0) ? 1.0 + wheel_zoom_speed : 1.0 - wheel_zoom_speed;
+                    (Fl::event_dy () > 0) ? 1.0 + Vwheel_zoom_speed
+                                          : 1.0 - Vwheel_zoom_speed;
 
                   // Get the point we're zooming about.
                   double x1, y1;
@@ -2128,35 +2129,35 @@
   return retval;
 }
 
-// FIXME -- This function should be abstracted and made potentially
+// FIXME: This function should be abstracted and made potentially
 // available to all graphics toolkits.  This suggests putting it in
 // graphics.cc as is done for drawnow() and having the master
 // mouse_wheel_zoom function call fltk_mouse_wheel_zoom.  The same
 // should be done for gui_mode and fltk_gui_mode.  For now (2011.01.30),
 // just changing function names and docstrings.
 
-DEFUN_DLD (mouse_wheel_zoom, args, ,
+DEFUN_DLD (mouse_wheel_zoom, args, nargout,
   "-*- texinfo -*-\n\
-@deftypefn  {Built-in Function} {@var{speed} =} mouse_wheel_zoom ()\n\
-@deftypefnx {Built-in Function} {} mouse_wheel_zoom (@var{speed})\n\
+@deftypefn  {Loadable Function} {@var{val} =} mouse_wheel_zoom ()\n\
+@deftypefnx {Loadable Function} {@var{old_val} =} mouse_wheel_zoom (@var{new_val})\n\
+@deftypefnx {Loadable Function} {} mouse_wheel_zoom (@var{new_val}, \"local\")\n\
 Query or set the mouse wheel zoom factor.\n\
 \n\
+The zoom factor is a number in the range (0,1) which is the percentage of the\n\
+current axis limits that will be used when zooming.  For example, if the\n\
+current x-axis limits are [0, 50] and @code{mouse_wheel_zoom} is 0.4 (40%),\n\
+then a zoom operation will change the limits by 20.\n\
+\n\
+When called from inside a function with the @qcode{\"local\"} option, the\n\
+variable is changed locally for the function and any subroutines it calls.  \n\
+The original variable value is restored when exiting the function.\n\
+\n\
 This function is currently implemented only for the FLTK graphics toolkit.\n\
 @seealso{gui_mode}\n\
 @end deftypefn")
 {
 #ifdef HAVE_FLTK
-  octave_value retval = wheel_zoom_speed;
-
-  if (args.length () == 1)
-    {
-      if (args(0).is_real_scalar ())
-        wheel_zoom_speed = args(0).double_value ();
-      else
-        error ("mouse_wheel_zoom: SPEED must be a real scalar");
-    }
-
-  return retval;
+  return SET_INTERNAL_VARIABLE_WITH_LIMITS(wheel_zoom_speed, 0.0001, 0.9999);
 #else
   error ("mouse_wheel_zoom: not available without OpenGL and FLTK libraries");
   return octave_value ();
--- a/libinterp/dldfcn/__magick_read__.cc
+++ b/libinterp/dldfcn/__magick_read__.cc
@@ -44,6 +44,73 @@
 #include <Magick++.h>
 #include <clocale>
 
+// In theory, it should be enough to check the class:
+// Magick::ClassType
+// PseudoClass:
+// Image is composed of pixels which specify an index in a color palette.
+// DirectClass:
+// Image is composed of pixels which represent literal color values.
+//
+//  GraphicsMagick does not really distinguishes between indexed and
+//  normal images. After reading a file, it decides itself the optimal
+//  way to store the image in memory, independently of the how the
+//  image was stored in the file. That's what ClassType returns. While
+//  it seems to match the original file most of the times, this is
+//  not necessarily true all the times. See
+//    https://sourceforge.net/mailarchive/message.php?msg_id=31180507
+//  In addition to the ClassType, there is also ImageType which has a
+//  type for indexed images (PaletteType and PaletteMatteType). However,
+//  they also don't represent the original image. Not only does DirectClass
+//  can have a PaletteType, but also does a PseudoClass have non Palette
+//  types.
+//
+//        We can't do better without having format specific code which is
+//        what we are trying to avoid by using a library such as GM. We at
+//        least create workarounds for the most common problems.
+//
+// 1) A grayscale jpeg image can report being indexed even though the
+//    JPEG format has no support for indexed images. We can at least
+//    fix this one.
+static bool
+is_indexed (const Magick::Image& img)
+{
+  bool retval = false;
+
+  if (img.classType () == Magick::PseudoClass && img.magick () != "JPEG")
+    retval = true;
+
+  return retval;
+}
+
+//  The depth from depth() is not always correct for us but seems to be the
+//  best value we can get. For example, a grayscale png image with 1 bit
+//  per channel should return a depth of 1 but instead we get 8.
+//  We could check channelDepth() but then, which channel has the data
+//  is not straightforward. So we'd have to check all
+//  the channels and select the highest value. But then, I also
+//  have a 16bit TIFF whose depth returns 16 (correct), but all of the
+//  channels gives 8 (wrong). No idea why, maybe a bug in GM?
+//  Anyway, using depth() seems that only causes problems for binary
+//  images, and the problem with channelDepth() is not making set them
+//  all to 1. So we will guess that if all channels have depth of 1,
+//  then we must have a binary image.
+//  Note that we can't use AllChannels it doesn't work for this.
+//  Instead of checking all of the individual channels, we check one
+//  from RGB, CMYK, grayscale, and transparency.
+static octave_idx_type
+get_depth (Magick::Image& img)
+{
+  octave_idx_type depth = img.depth ();
+  if (depth != 1
+      && img.channelDepth (Magick::RedChannel)     == 1
+      && img.channelDepth (Magick::CyanChannel)    == 1
+      && img.channelDepth (Magick::OpacityChannel) == 1
+      && img.channelDepth (Magick::GrayChannel)    == 1)
+    depth = 1;
+
+  return depth;
+}
+
 // We need this in case one of the sides of the image being read has
 // width 1. In those cases, the type will come as scalar instead of range
 // since that's the behaviour of the colon operator (1:1:1 will be a scalar,
@@ -96,9 +163,30 @@
   return region;
 }
 
+static octave_value_list
+read_maps (Magick::Image& img)
+{
+  // can't call colorMapSize on const Magick::Image
+  const octave_idx_type mapsize = img.colorMapSize ();
+  Matrix cmap                   = Matrix (mapsize, 3); // colormap
+  Matrix amap                   = Matrix (mapsize, 3); // alpha map
+  for (octave_idx_type i = 0; i < mapsize; i++)
+    {
+      const Magick::ColorRGB c = img.colorMap (i);
+      cmap(i,0) = c.red   ();
+      cmap(i,1) = c.green ();
+      cmap(i,2) = c.blue  ();
+      amap(i,0) = c.alpha ();
+    }
+  octave_value_list maps;
+  maps(0) = cmap;
+  maps(1) = amap;
+  return maps;
+}
+
 template <class T>
 static octave_value_list
-read_indexed_images (std::vector<Magick::Image>& imvec,
+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)
@@ -151,57 +239,36 @@
     }
   retval(0) = octave_value (img);
 
-  // Do we need to get the colormap to interpret the image and alpha channel?
+//   Only bother reading the colormap if it was requested as output.
   if (nargout > 1)
     {
-      const octave_idx_type mapsize = imvec[def_elem].colorMapSize ();
-      Matrix cmap                   = Matrix (mapsize, 3);
-
       // In theory, it should be possible for each frame of an image to
       // have different colormaps but for Matlab compatibility, we only
-      // return the colormap of the first frame.
+      // return the colormap of the first frame.  To obtain the colormaps
+      // of different frames, one needs can either use imfinfo or a for
+      // loop around imread.
+      const octave_value_list maps =
+        read_maps (const_cast<Magick::Image&> (imvec[frameidx(def_elem)]));
 
-      // only get alpha channel if it exists and was requested as output
+      retval(1) = maps(0);
+
+      // only interpret alpha channel if it exists and was requested as output
       if (imvec[def_elem].matte () && nargout >= 3)
         {
-          Matrix amap = Matrix (mapsize, 1);
-          for (octave_idx_type i = 0; i < mapsize; i++)
-            {
-              const Magick::ColorRGB c = imvec[def_elem].colorMap (i);
-              cmap(i,0) = c.red   ();
-              cmap(i,1) = c.green ();
-              cmap(i,2) = c.blue  ();
-              amap(i,0) = c.alpha ();
-            }
+          const Matrix amap = maps(1).matrix_value ();
+          const double* amap_fvec = amap.fortran_vec ();
 
           NDArray alpha (dim_vector (nRows, nCols, 1, nFrames));
-          const octave_idx_type nPixels = alpha.numel ();
-
           double* alpha_fvec = alpha.fortran_vec ();
 
-          idx = 0;
+          // GraphicsMagick stores the alpha values inverted, i.e.,
+          // 1 for transparent and 0 for opaque so we fix that here.
+          const octave_idx_type nPixels = alpha.numel ();
           for (octave_idx_type pix = 0; pix < nPixels; pix++)
-            {
-              // GraphicsMagick stores the alpha values inverted, i.e.,
-              // 1 for transparent and 0 for opaque so we fix that here.
-              alpha_fvec[idx] = 1 - amap(img(idx), 0);
-              idx++;
-            }
+            alpha_fvec[pix] = 1 - amap_fvec[static_cast<int> (img_fvec[3])];
+
           retval(2) = alpha;
         }
-
-      else
-        {
-          for (octave_idx_type i = 0; i < mapsize; i++)
-            {
-              const Magick::ColorRGB c = imvec[def_elem].colorMap (i);
-              cmap(i,0) = c.red   ();
-              cmap(i,1) = c.green ();
-              cmap(i,2) = c.blue  ();
-            }
-        }
-
-      retval(1) = cmap;
     }
 
   return retval;
@@ -689,60 +756,8 @@
         }
     }
 
-  // FIXME: the depth here is not always correct for us but seems to be the best
-  //        value we can get. For example, a grayscale png image with 1 bit
-  //        per channel should return a depth of 1 but instead we get 8.
-  //        We could check channelDepth() but then, which channel has the data
-  //        is not straightforward. So we'd have to check all
-  //        the channels and select the highest value. But then, I also
-  //        have a 16bit TIFF whose depth returns 16 (correct), but all of the
-  //        channels gives 8 (wrong). No idea why, maybe a bug in GM?
-  //        Anyway, using depth() seems that only causes problems for binary
-  //        images, and the problem with channelDepth() is not making set them
-  //        all to 1. So we will guess that if all channels have depth of 1,
-  //        then we must have a binary image.
-  //        Note that we can't use AllChannels it doesn't work for this.
-  //        Instead of checking all of the individual channels, we check one
-  //        from RGB, CMYK, grayscale, and transparency.
-  octave_idx_type depth = imvec[frameidx(0)].depth ();
-  if (depth != 1
-      && imvec[frameidx(0)].channelDepth (Magick::RedChannel)     == 1
-      && imvec[frameidx(0)].channelDepth (Magick::CyanChannel)    == 1
-      && imvec[frameidx(0)].channelDepth (Magick::OpacityChannel) == 1
-      && imvec[frameidx(0)].channelDepth (Magick::GrayChannel)    == 1)
-    depth = 1;
-
-  // Magick::ClassType
-  // PseudoClass:
-  // Image is composed of pixels which specify an index in a color palette.
-  // DirectClass:
-  // Image is composed of pixels which represent literal color values.
-
-  Magick::ClassType klass = imvec[frameidx(0)].classType ();
-  // FIXME: GraphicsMagick does not really distinguishes between indexed and
-  //        normal images. After reading a file, it decides itself the optimal
-  //        way to store the image in memory, independently of the how the
-  //        image was stored in the file. That's what ClassType returns. While
-  //        it seems to match the original file most of the times, this is
-  //        not necessarily true all the times. See
-  //          https://sourceforge.net/mailarchive/message.php?msg_id=31180507
-  //        In addition to the ClassType, there is also ImageType which has a
-  //        type for indexed images (PaletteType and PaletteMatteType). However,
-  //        they also don't represent the original image. Interestingly, one
-  //        would at least guess that PseudoClass would include only the Palette
-  //        types but that does not happen.
-  //
-  //        We can't do better without having format specific code which is
-  //        what we are trying to avoid by using a library such as GM. We at
-  //        least create workarounds for the most common problems.
-
-  // 1) A grayscale jpeg image can report being indexed even though the
-  //    JPEG format has no support for indexed images. We can at least
-  //    fix this one.
-  if (klass == Magick::PseudoClass && imvec[0].magick () == "JPEG")
-    klass = Magick::DirectClass;
-
-  if (klass == Magick::PseudoClass)
+  const octave_idx_type depth = get_depth (imvec[frameidx(0)]);
+  if (is_indexed (imvec[frameidx(0)]))
     {
       if (depth <= 1)
         output = read_indexed_images<boolNDArray>   (imvec, frameidx,
@@ -1396,82 +1411,173 @@
 %!assert (1)
 */
 
-#ifdef HAVE_MAGICK
+// Gets the minimum information from images such as its size and format. Much
+// faster than using imfinfo, which slows down a lot since. Note than without
+// this, we need to read the image once for imfinfo to set defaults (which is
+// done in Octave language), and then again for the actual reading.
+DEFUN_DLD (__magick_ping__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __magick_ping__ (@var{fname}, @var{idx})\n\
+Ping image information with GraphicsMagick or ImageMagick.\n\
+\n\
+This is a private internal function not intended for direct use.\n\
+\n\
+@seealso{imfinfo}\n\
+@end deftypefn")
+{
+  octave_value retval;
+#ifndef HAVE_MAGICK
+  gripe_disabled_feature ("imfinfo", "Image IO");
+#else
+  maybe_initialize_magick ();
 
-template<class T>
-static octave_value
-magick_to_octave_value (const T magick)
-{
-  return octave_value (magick);
+  if (args.length () < 1 || ! args(0).is_string ())
+    {
+      print_usage ();
+      return retval;
+    }
+  const std::string filename = args(0).string_value ();
+  int idx;
+  if (args.length () > 1)
+    idx = args(1).int_value () -1;
+  else
+    idx = 0;
+
+  Magick::Image img;
+  img.subImage (idx);
+  img.subRange (1);
+  img.ping (filename);
+  static const char *fields[] = {"rows", "columns", "format", 0};
+  octave_scalar_map ping = octave_scalar_map (string_vector (fields));
+  ping.setfield ("rows",    octave_value (img.rows ()));
+  ping.setfield ("columns", octave_value (img.columns ()));
+  ping.setfield ("format",  octave_value (img.magick ()));
+  retval = octave_value (ping);
+#endif
+  return retval;
 }
 
+#ifdef HAVE_MAGICK
 static octave_value
-magick_to_octave_value (const Magick::EndianType magick)
+magick_to_octave_value (const Magick::CompressionType& magick)
 {
   switch (magick)
     {
-      case Magick::LSBEndian:
-        return octave_value ("little-endian");
-
-      case Magick::MSBEndian:
-        return octave_value ("big-endian");
-
+      case Magick::NoCompression:
+        return octave_value ("none");
+      case Magick::BZipCompression:
+        return octave_value ("bzip");
+      case Magick::FaxCompression:
+        return octave_value ("fax3");
+      case Magick::Group4Compression:
+        return octave_value ("fax4");
+      case Magick::JPEGCompression:
+        return octave_value ("jpeg");
+      case Magick::LZWCompression:
+        return octave_value ("lzw");
+      case Magick::RLECompression:
+        // This is named "rle" for the HDF, but the same thing is named
+        // "ccitt" and "PackBits" for binary and non-binary images in TIFF.
+        return octave_value ("rle");
+      case Magick::ZipCompression:
+        return octave_value ("deflate");
+      case Magick::LZMACompression:
+        return octave_value ("lzma");
+      case Magick::JPEG2000Compression:
+        return octave_value ("jpeg2000");
+      case Magick::JBIG1Compression:
+        return octave_value ("jbig1");
+      case Magick::JBIG2Compression:
+        return octave_value ("jbig2");
       default:
         return octave_value ("undefined");
     }
 }
 
 static octave_value
-magick_to_octave_value (const Magick::ResolutionType magick)
+magick_to_octave_value (const Magick::EndianType& magick)
 {
   switch (magick)
     {
-      case Magick::PixelsPerInchResolution:
-        return octave_value ("pixels per inch");
-
-      case Magick::PixelsPerCentimeterResolution:
-        return octave_value ("pixels per centimeter");
-
+      case Magick::LSBEndian:
+        return octave_value ("little-endian");
+      case Magick::MSBEndian:
+        return octave_value ("big-endian");
       default:
         return octave_value ("undefined");
     }
 }
 
 static octave_value
-magick_to_octave_value (const Magick::ImageType magick)
+magick_to_octave_value (const Magick::OrientationType& magick)
 {
   switch (magick)
     {
-      case Magick::BilevelType:
-      case Magick::GrayscaleType:
-      case Magick::GrayscaleMatteType:
-        return octave_value ("grayscale");
+      // Values come from the TIFF6 spec
+      case Magick::TopLeftOrientation:
+        return octave_value (1);
+      case Magick::TopRightOrientation:
+        return octave_value (2);
+      case Magick::BottomRightOrientation:
+        return octave_value (3);
+      case Magick::BottomLeftOrientation:
+        return octave_value (4);
+      case Magick::LeftTopOrientation:
+        return octave_value (5);
+      case Magick::RightTopOrientation:
+        return octave_value (6);
+      case Magick::RightBottomOrientation:
+        return octave_value (7);
+      case Magick::LeftBottomOrientation:
+        return octave_value (8);
+      default:
+        return octave_value (1);
+    }
+}
 
-      case Magick::PaletteType:
-      case Magick::PaletteMatteType:
-        return octave_value ("indexed");
-
-      case Magick::TrueColorType:
-      case Magick::TrueColorMatteType:
-      case Magick::ColorSeparationType:
-        return octave_value ("truecolor");
-
+static octave_value
+magick_to_octave_value (const Magick::ResolutionType& magick)
+{
+  switch (magick)
+    {
+      case Magick::PixelsPerInchResolution:
+        return octave_value ("Inch");
+      case Magick::PixelsPerCentimeterResolution:
+        return octave_value ("Centimeter");
       default:
         return octave_value ("undefined");
     }
 }
 
-// We put this in a try-block because GraphicsMagick will throw
-// exceptions if a parameter isn't present in the current image.
-#define GET_PARAM(NAME, OUTNAME) \
-  try \
-    { \
-      info.contents (OUTNAME)(frame,0) = magick_to_octave_value (im.NAME ()); \
-    } \
-  catch (Magick::Warning& w) \
-    { \
+// We return a map so this can be used both in imwrite and imfinfo.
+static std::map<octave_idx_type, std::string>
+disposal_methods ()
+{
+  //  GIF Specifications:
+  //
+  // Disposal Method - Indicates the way in which the graphic is to
+  //                    be treated after being displayed.
+  //
+  //  0 -   No disposal specified. The decoder is
+  //        not required to take any action.
+  //  1 -   Do not dispose. The graphic is to be left
+  //        in place.
+  //  2 -   Restore to background color. The area used by the
+  //        graphic must be restored to the background color.
+  //  3 -   Restore to previous. The decoder is required to
+  //        restore the area overwritten by the graphic with
+  //        what was there prior to rendering the graphic.
+  //  4-7 - To be defined.
+  static std::map<octave_idx_type, std::string> methods;
+  if (methods.empty ())
+    {
+      methods[0] = "doNotSpecify";
+      methods[1] = "leaveInPlace";
+      methods[2] = "restoreBG";
+      methods[3] = "restorePrevious";
     }
-
+  return methods;
+}
 #endif
 
 DEFUN_DLD (__magick_finfo__, args, ,
@@ -1490,7 +1596,6 @@
 #ifndef HAVE_MAGICK
   gripe_disabled_feature ("imfinfo", "Image IO");
 #else
-
   maybe_initialize_magick ();
 
   if (args.length () < 1 || ! args(0).is_string ())
@@ -1498,117 +1603,184 @@
       print_usage ();
       return retval;
     }
-
   const std::string filename = args(0).string_value ();
 
-  try
+  std::vector<Magick::Image> imvec;
+  read_file (filename, imvec);
+  if (error_state)
+    return retval;
+
+  // Matlab has different list of fields for each file format. We don't.
+  static const char *fields[] =
     {
-      // Read the file.
-      std::vector<Magick::Image> imvec;
-      Magick::readImages (&imvec, args(0).string_value ());
-      int nframes = imvec.size ();
-
-      // Create the right size for the output.
+      // These are fields that must always appear for Matlab.
+      "Filename",
+      "FileModDate",
+      "FileSize",
+      "Format",
+      "FormatVersion",
+      "Width",
+      "Height",
+      "BitDepth",
+      "ColorType",
 
-      static const char *fields[] =
-        {
-          "Filename",
-          "FileModDate",
-          "FileSize",
-          "Height",
-          "Width",
-          "BitDepth",
-          "Format",
-          "LongFormat",
-          "XResolution",
-          "YResolution",
-          "TotalColors",
-          "TileName",
-          "AnimationDelay",
-          "AnimationIterations",
-          "ByteOrder",
-          "Gamma",
-          "Matte",
-          "ModulusDepth",
-          "Quality",
-          "QuantizeColors",
-          "ResolutionUnits",
-          "ColorType",
-          "View",
-          0
-        };
-
-      octave_map info (dim_vector (nframes, 1), string_vector (fields));
-
-      file_stat fs (filename);
-
-      std::string filetime;
-
-      if (fs)
-        {
-          octave_localtime mtime = fs.mtime ();
-
-          filetime = mtime.strftime ("%e-%b-%Y %H:%M:%S");
-        }
-      else
-        {
-          std::string msg = fs.error ();
+      // These are format specific or not existent in Matlab. The most
+      // annoying thing is that Matlab may have different names for the
+      // same thing, in different formats.
+      "DelayTime",
+      "DisposalMethod",
+      "LoopCount",
+      "ByteOrder",
+      "Gamma",
+      "Chromaticities",
+      "Comment",
+      "Quality",
+      "Compression",        // same as CompressionType
+      "Colormap",           // same as ColorTable (in PNG)
+      "Orientation",
+      "ResolutionUnit",
+      "XResolution",
+      "YResolution",
+      0
+    };
 
-          error ("imfinfo: error reading '%s': %s",
-                 filename.c_str (), msg.c_str ());
-
-          return retval;
-        }
+  // Notes for the future: GM allows to get many attributes, and even has
+  // attribute() to obtain arbitrary ones, that may be set in only some
+  // cases. The following is a list of some methods and into what possible
+  // Matlab value they may be converted.
+  //
+  //  colorSpace()      -> PhotometricInterpretation
+  //  backgroundColor() -> BackgroundColor
+  //  interlaceType()   -> Interlaced, InterlaceType, and PlanarConfiguration
+  //  label()           -> Title
 
-      // For each frame in the image (some images contain multiple
-      // layers, each to be treated like a separate image).
-      for (int frame = 0; frame < nframes; frame++)
-        {
-          Magick::Image im = imvec[frame];
-
-          // Add file name and timestamp.
-          info.contents ("Filename")(frame,0) = filename;
-          info.contents ("FileModDate")(frame,0) = filetime;
+  // Create the right size for the output.
+  const octave_idx_type nFrames = imvec.size ();
+  octave_map info (dim_vector (nFrames, 1), string_vector (fields));
 
-          // Annoying CamelCase naming is for Matlab compatibility.
-          GET_PARAM (fileSize, "FileSize")
-          GET_PARAM (rows, "Height")
-          GET_PARAM (columns, "Width")
-          GET_PARAM (depth, "BitDepth")
-          GET_PARAM (magick, "Format")
-          GET_PARAM (format, "LongFormat")
-          GET_PARAM (xResolution, "XResolution")
-          GET_PARAM (yResolution, "YResolution")
-          GET_PARAM (totalColors, "TotalColors")
-          GET_PARAM (tileName, "TileName")
-          GET_PARAM (animationDelay, "AnimationDelay")
-          GET_PARAM (animationIterations, "AnimationIterations")
-          GET_PARAM (endian, "ByteOrder")
-          GET_PARAM (gamma, "Gamma")
-          GET_PARAM (matte, "Matte")
-          GET_PARAM (modulusDepth, "ModulusDepth")
-          GET_PARAM (quality, "Quality")
-          GET_PARAM (quantizeColors, "QuantizeColors")
-          GET_PARAM (resolutionUnits, "ResolutionUnits")
-          GET_PARAM (type, "ColorType")
-          GET_PARAM (view, "View")
-        }
+  const std::string format (imvec[0].magick ());
+  // For each frame in the image (some images contain multiple
+  // layers, each to be treated like a separate image). So we create
+  // octave_scalar_map and insert them in the octave_map during the
+  // loop.  Since some fields will never change value, we set the
+  // template
+  octave_scalar_map template_info = (string_vector (fields));
+
+  template_info.setfield ("Format", octave_value (format));
+  // We can't actually get FormatVersion but even Matlab sometimes can't.
+  template_info.setfield ("FormatVersion", octave_value (""));
 
-      retval = octave_value (info);
-    }
-  catch (Magick::Warning& w)
+  const file_stat fs (filename);
+  if (fs)
     {
-      warning ("Magick++ warning: %s", w.what ());
+      const octave_localtime mtime (fs.mtime ());
+      const std::string filetime = mtime.strftime ("%e-%b-%Y %H:%M:%S");
+      template_info.setfield ("Filename",    octave_value (filename));
+      template_info.setfield ("FileModDate", octave_value (filetime));
+      template_info.setfield ("FileSize",    octave_value (fs.size ()));
     }
-  catch (Magick::ErrorCoder& e)
+  else
     {
-      warning ("Magick++ coder error: %s", e.what ());
-    }
-  catch (Magick::Exception& e)
-    {
-      error ("Magick++ exception: %s", e.what ());
+      error ("imfinfo: error reading '%s': %s",
+             filename.c_str (), fs.error ().c_str ());
       return retval;
     }
+
+  std::map<octave_idx_type, std::string> gif_methods = disposal_methods ();
+
+  for (octave_idx_type frame = 0; frame < nFrames; frame++)
+    {
+      octave_scalar_map info_frame (template_info);
+      const Magick::Image img = imvec[frame];
+
+      info_frame.setfield ("Width",  octave_value (img.columns ()));
+      info_frame.setfield ("Height", octave_value (img.rows ()));
+      info_frame.setfield ("BitDepth",
+        octave_value (get_depth (const_cast<Magick::Image&> (img))));
+
+      // Stuff related to colormap, image class and type
+      // Because GM is too smart for us... Read the comments in is_indexed()
+      {
+        std::string color_type;
+        Matrix cmap;
+        if (is_indexed (img))
+          {
+            color_type = "indexed";
+            cmap = read_maps (const_cast<Magick::Image&> (img))(0).matrix_value ();
+          }
+        else
+          {
+            switch (img.type ())
+              {
+                case Magick::BilevelType:
+                case Magick::GrayscaleType:
+                case Magick::GrayscaleMatteType:
+                  color_type = "grayscale";
+                  break;
+
+                case Magick::TrueColorType:
+                case Magick::TrueColorMatteType:
+                  color_type = "truecolor";
+                  break;
+
+                case Magick::PaletteType:
+                case Magick::PaletteMatteType:
+                  // we should never get here or is_indexed needs to be fixed
+                  color_type = "indexed";
+                  break;
+
+                case Magick::ColorSeparationType:
+                case Magick::ColorSeparationMatteType:
+                  color_type = "CMYK";
+                  break;
+
+                default:
+                  color_type = "undefined";
+              }
+          }
+        info_frame.setfield ("ColorType",           octave_value (color_type));
+        info_frame.setfield ("Colormap",            octave_value (cmap));
+      }
+
+      info_frame.setfield ("Gamma",     octave_value (img.gamma ()));
+      {
+        // Not all images have chroma values. In such cases, they'll
+        // be all zeros. SO rather than send a matrix of zeros, we will
+        // check for that, and send an empty vector instead.
+        RowVector chromaticities (8);
+        double* chroma_fvec = chromaticities.fortran_vec ();
+        img.chromaWhitePoint    (&chroma_fvec[0], &chroma_fvec[1]);
+        img.chromaRedPrimary    (&chroma_fvec[2], &chroma_fvec[3]);
+        img.chromaGreenPrimary  (&chroma_fvec[4], &chroma_fvec[5]);
+        img.chromaBluePrimary   (&chroma_fvec[6], &chroma_fvec[7]);
+        if (chromaticities.nnz () == 0)
+          chromaticities = RowVector (0);
+        info_frame.setfield ("Chromaticities", octave_value (chromaticities));
+      }
+
+      info_frame.setfield ("XResolution",   octave_value (img.xResolution ()));
+      info_frame.setfield ("YResolution",   octave_value (img.yResolution ()));
+      info_frame.setfield ("DelayTime",     octave_value (img.animationDelay ()));
+      info_frame.setfield ("LoopCount",     octave_value (img.animationIterations ()));
+      info_frame.setfield ("Quality",       octave_value (img.quality ()));
+      info_frame.setfield ("Comment",       octave_value (img.comment ()));
+
+      info_frame.setfield ("DisposalMethod",
+        octave_value (format == "GIF"?
+                      gif_methods[img.gifDisposeMethod ()] : ""));
+
+      info_frame.setfield ("Compression",
+        magick_to_octave_value (img.compressType ()));
+      info_frame.setfield ("Orientation",
+        magick_to_octave_value (img.orientation ()));
+      info_frame.setfield ("ResolutionUnit",
+        magick_to_octave_value (img.resolutionUnits ()));
+      info_frame.setfield ("ByteOrder",
+        magick_to_octave_value (img.endian ()));
+
+      info.fast_elem_insert (frame, info_frame);
+    }
+  retval = octave_value (info);
 #endif
   return retval;
 }
@@ -1618,8 +1790,6 @@
 %!assert (1)
 */
 
-#undef GET_PARAM
-
 DEFUN_DLD (__magick_formats__, args, ,
   "-*- texinfo -*-\n\
 @deftypefn {Loadable Function} {} __magick_imformats__ (@var{formats})\n\
--- a/scripts/image/imfinfo.m
+++ b/scripts/image/imfinfo.m
@@ -33,11 +33,14 @@
 ## @item Filename
 ## The full name of the image file.
 ##
+## @item FileModDate
+## Date of last modification to the file.
+##
 ## @item FileSize
 ## Number of bytes of the image on disk
 ##
-## @item FileModDate
-## Date of last modification to the file.
+## @item Format
+## Image format (e.g., @qcode{"jpeg"}).
 ##
 ## @item Height
 ## Image height in pixels.
@@ -48,11 +51,9 @@
 ## @item BitDepth
 ## Number of bits per channel per pixel.
 ##
-## @item Format
-## Image format (e.g., @qcode{"jpeg"}).
-##
-## @item LongFormat
-## Long form image format description.
+## @item ColorType
+## Image type.  Value is @qcode{"grayscale"}, @qcode{"indexed"},
+## @qcode{"truecolor"}, @qcode{"CMYK"}, or @qcode{"undefined"}.
 ##
 ## @item XResolution
 ## X resolution of the image.
@@ -60,19 +61,16 @@
 ## @item YResolution
 ## Y resolution of the image.
 ##
-## @item TotalColors
-## Number of unique colors in the image.
+## @item ResolutionUnit
+## Units of image resolution.  Value is @qcode{"Inch"},
+## @qcode{"Centimeter"}, or @qcode{"undefined"}.
 ##
-## @item TileName
-## Tile name.
-##
-## @item AnimationDelay
+## @item DelayTime
 ## Time in 1/100ths of a second (0 to 65535) which must expire before displaying
 ## the next image in an animated sequence.
 ##
-## @item AnimationIterations
-## Number of iterations to loop an animation (e.g., Netscape loop extension)
-## for.
+## @item LoopCount
+## Number of iterations to loop an animation.
 ##
 ## @item ByteOrder
 ## Endian option for formats that support it.  Value is @qcode{"little-endian"},
@@ -82,29 +80,35 @@
 ## Gamma level of the image.  The same color image displayed on two different
 ## workstations may look different due to differences in the display monitor.
 ##
-## @item Matte
-## @code{true} if the image has transparency.
+## @item Quality
+## JPEG/MIFF/PNG compression level.  Value is an integer in the range [0 100].
 ##
-## @item ModulusDepth
-## Image modulus depth (minimum number of bits required to support
-## red/green/blue components without loss of accuracy).
+## @item DisposalMethod
+## Only valid for GIF images, control how successive frames are rendered (how
+## the preceding frame is disposed of) when creating a GIF animation.  Values
+## can be @qcode{"doNotSpecify"}, @qcode{"leaveInPlace"}, @qcode{"restoreBG"},
+## or @qcode{"restorePrevious"}.  For non-GIF files, value is an empty string.
 ##
-## @item Quality
-## JPEG/MIFF/PNG compression level.
+## @item Chromaticities
+## Value is a 1x8 Matrix with the x,y chromaticity values for white, red,
+## green, and blue points, in that order.
 ##
-## @item QuantizeColors
-## Preferred number of colors in the image.
-##
-## @item ResolutionUnits
-## Units of image resolution.  Value is @qcode{"pixels per inch"},
-## @qcode{"pixels per centimeter"}, or @qcode{"undefined"}.
+## @item Comment
+## Image comment.
 ##
-## @item ColorType
-## Image type.  Value is @qcode{"grayscale"}, @qcode{"indexed"},
-## @qcode{"truecolor"}, or @qcode{"undefined"}.
+## @item Compression
+## Compression type.  Value can be @qcode{"none"}, @qcode{"bzip"},
+## @qcode{"fax3"}, @qcode{"fax4"}, @qcode{"jpeg"}, @qcode{"lzw"},
+## @qcode{"rle"}, @qcode{"deflate"}, @qcode{"lzma"}, @qcode{"jpeg2000"},
+## @qcode{"jbig2"}, @qcode{"jbig2"}, or @qcode{"undefined"}.
 ##
-## @item View
-## FlashPix viewing parameters.
+## @item Colormap
+## Colormap for each image.
+##
+## @item Orientation
+## The orientation of the image with respect to the rows and columns.  Value
+## is an integer between 1 and 8 as defined in the TIFF 6 specifications, and
+## for @sc{Matlab} compatibility.
 ## @end table
 ##
 ## @seealso{imread, imwrite, imshow, imformats}
--- a/scripts/image/imformats.m
+++ b/scripts/image/imformats.m
@@ -277,7 +277,7 @@
 function bool = isa_magick (coder, filename)
   bool = false;
   try
-    info = __imfinfo__ (filename);
+    info = __magick_ping__ (filename, 1);
     bool = strcmp (coder, info.Format);
   end_try_catch
 endfunction
--- a/scripts/image/imread.m
+++ b/scripts/image/imread.m
@@ -39,12 +39,17 @@
 ## @nospell{MxNx3} matrix.  Gray-level and black-and-white images are
 ## of size @nospell{MxN}.  Multipage images will have an additional 4th
 ## dimension.
+##
 ## The bit depth of the image determines the
-## class of the output: @qcode{"uint8"} or @qcode{"uint16"} for gray
-## and color, and @qcode{"logical"} for black and white.
+## class of the output: @qcode{"uint8"}, @qcode{"uint16"} or @qcode{"single"}
+## for gray and color, and @qcode{"logical"} for black and white.
 ## Note that indexed images always return the indexes for a colormap,
 ## independent if @var{map} is a requested output.  To obtain the actual
-## RGB image, use @code{ind2rgb}.
+## RGB image, use @code{ind2rgb}.  When more than one indexed image is being
+## read, @var{map} is obtained from the first.  In some rare cases this
+## may be incorrect and @code{imfinfo] can be used to obtain the colormap of
+## each image.
+##
 ## See the Octave manual for more information in representing images.
 ##
 ## Some file formats, such as TIFF and GIF, are able to store multiple
--- a/scripts/image/private/__imread__.m
+++ b/scripts/image/private/__imread__.m
@@ -92,10 +92,10 @@
 
   try
     ## Use information from the first image to be read to set defaults.
-    info = imfinfo (fn)(options.index(1));
+    info = __magick_ping__ (fn, options.index(1));
 
     ## Set default for options.
-    options.region = {1:1:info.Height 1:1:info.Width};
+    options.region = {1:1:info.rows 1:1:info.columns};
 
     for idx = offset:2:(numel (varargin) - offset + 1)
       switch (tolower (varargin{idx}))
@@ -120,9 +120,9 @@
                                       floor (options.region{reg_idx}(2)): ...
                                       floor (options.region{reg_idx}(3));
           endfor
-          if (options.region{1}(end) > info.Height)
+          if (options.region{1}(end) > info.rows)
             error ("imread: end ROWS for PixelRegions option is larger than image height");
-          elseif (options.region{2}(end) > info.Width)
+          elseif (options.region{2}(end) > info.columns)
             error ("imread: end COLS for PixelRegions option is larger than image width");
           endif
 
--- a/scripts/plot/private/__patch__.m
+++ b/scripts/plot/private/__patch__.m
@@ -29,6 +29,7 @@
 
 function [h, failed] = __patch__ (p, varargin)
 
+  keyboard
   h = NaN;
   failed = false;
 
@@ -148,7 +149,7 @@
             args{7} = "facecolor";
             args{8} = "interp";
             args{9} = "cdata";
-            agrs{10} = c;
+            args{10} = c;
           else
             error ("patch: color value not valid");
           endif