# HG changeset patch # User Carnë Draug # Date 1372959311 -3600 # Node ID 861516dcad195656da9f9bdca6a9214144797cfd # Parent 434a0b29ab12d5219d58d3fa5f9fa2ab1adcaeef New function imformats. * imformats.m: the new function. * __magick_read__.cc: created new internal function __magick__formats__ to be used by imformats(). * NEWS: add imformats to list of new functions. * image.txi: add reference to new function to the Octave manual. * module.mk: include new function in the build system. diff -r 434a0b29ab12 -r 861516dcad19 NEWS --- a/NEWS Fri Jul 05 07:12:18 2013 -0400 +++ b/NEWS Thu Jul 04 18:35:11 2013 +0100 @@ -200,6 +200,7 @@ debug_jit importdata struct2hdl doc_cache_create iscolormap tetramesh ellipj jit_enable waterfall + imformats ** Deprecated functions. diff -r 434a0b29ab12 -r 861516dcad19 doc/interpreter/image.txi --- a/doc/interpreter/image.txi Fri Jul 05 07:12:18 2013 -0400 +++ b/doc/interpreter/image.txi Thu Jul 04 18:35:11 2013 +0100 @@ -77,6 +77,8 @@ @DOCSTRING(imfinfo) +@DOCSTRING(imformats) + @node Displaying Images @section Displaying Images diff -r 434a0b29ab12 -r 861516dcad19 libinterp/dldfcn/__magick_read__.cc --- a/libinterp/dldfcn/__magick_read__.cc Fri Jul 05 07:12:18 2013 -0400 +++ b/libinterp/dldfcn/__magick_read__.cc Thu Jul 04 18:35:11 2013 +0100 @@ -1152,6 +1152,59 @@ #undef GET_PARAM +DEFUN_DLD (__magick_formats__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __magick_imformats__ (@var{formats})\n\ +Fill formats info with GraphicsMagick CoderInfo.\n\ +@end deftypefn") +{ + octave_value retval; +#ifndef HAVE_MAGICK + gripe_disabled_feature ("imformats", "Image IO"); +#else + if (args.length () != 1 || ! args (0).is_map ()) + { + print_usage (); + return retval; + } + octave_map formats = args(0).map_value (); + + maybe_initialize_magick (); + for (octave_idx_type idx = 0; idx < formats.numel (); idx++) + { + try + { + octave_scalar_map fmt = formats.checkelem (idx); + Magick::CoderInfo coder (fmt.getfield ("coder").string_value ()); + + fmt.setfield ("description", octave_value (coder.description ())); + fmt.setfield ("multipage", coder.isMultiFrame () ? true : false); + // default for read and write is a function handle. If we can't + // read or write them, them set it to an empty value + if (! coder.isReadable ()) + fmt.setfield ("read", Matrix ()); + if (! coder.isWritable ()) + fmt.setfield ("write", Matrix ()); + formats.fast_elem_insert (idx, fmt); + } + catch (Magick::Exception& e) + { + // Exception here are missing formats. So we remove the format + // from the structure and reduce idx. + formats.delete_elements (idx); + idx--; + } + } + retval = formats; +#endif + return retval; +} + +/* +## No test needed for internal helper function. +%!assert (1) +*/ + // Determine the file formats supported by GraphicsMagick. This is // called once at the beginning of imread or imwrite to determine // exactly which file formats are supported, so error messages can be diff -r 434a0b29ab12 -r 861516dcad19 scripts/image/imformats.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/image/imformats.m Thu Jul 04 18:35:11 2013 +0100 @@ -0,0 +1,276 @@ +## Copyright (C) 2013 Carnë Draug +## +## This file is part of Octave. +## +## Octave is free software; you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or (at +## your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . + +## -*- texinfo -*- +## @deftypefn {Function File} {} imformats () +## @deftypefnx {Function File} {@var{formats} =} imformats (@var{ext}) +## @deftypefnx {Function File} {@var{formats} =} imformats (@var{format}) +## @deftypefnx {Function File} {@var{formats} =} imformats ("add", @var{format}) +## @deftypefnx {Function File} {@var{formats} =} imformats ("remove", @var{ext}) +## @deftypefnx {Function File} {@var{formats} =} imformats ("update", @var{ext}, @var{format}) +## @deftypefnx {Function File} {@var{formats} =} imformats ("factory") +## Manage supported image formats. +## +## @var{formats} is a structure with information about each supported file +## format, or from a specific format @var{ext}, the value displayed on the +## field @code{ext}. It contains the following fields: +## +## @table @asis +## @item ext +## The name of the file format. This may match the file extension but Octave +## will automatically detect the file format. +## @item description +## A long description of the file format. +## @item isa +## A function handle to confirm if a file is of the specified format. +## @item write +## A function handle to write if a file is of the specified format. +## @item read +## A function handle to open files the specified format. +## @item info +## A function handle to obtain image information of the specified format. +## @item alpha +## Logical value if format supports alpha channel (transparency or matte). +## @item multipage +## Logical value if format supports multipage (multiple images per file). +## @end table +## +## It is possible to change the way Octave manages file formats with the options +## @code{"add"}, @code{"remove"}, and @code{"update"}, and supplying a +## structure @var{format} with the required fields. The option +## @code{"factory"} resets the configuration to the default. +## +## This can be used by Octave packages to extend the image reading capabilities +## Octave, through use of the PKG_ADD and PKG_DEL commands. +## +## @seealso{imfinfo, imread, imwrite} +## @end deftypefn + +## Author: Carnë Draug + +function varargout = imformats (arg1, arg2, arg3) + if (nargin > 3) + print_usage (); + endif + + persistent formats = default_formats (); + + if (nargin == 0 && nargout == 0) + error ("imformats: pretty print not yet implemented."); + elseif (nargin >= 1) + if (isstruct (arg1)) + arrayfun (@is_valid_format, arg1); + ## FIXME: what is the return value in this situation? + formats = arg1; + + elseif (ischar (arg1)) + switch (tolower (arg1)) + case "add", + if (! isstruct (arg2)) + error ("imformats: FORMAT to %s must be a structure.", arg1); + endif + arrayfun (@is_valid_format, arg2); + formats(end + numel (b)) = arg2; + varargin{1} = formats; + + case {"remove", "update"}, + if (! ischar (arg2)) + error ("imformats: EXT to %s must be a string.", arg1); + endif + ## FIXME: suppose a format with multiple extensions. If one of + ## them is requested to be removed, should we remove the + ## whole format, or just that extension from the format? + match = find_ext_idx (formats, arg2) + if (! any (match)) + error ("imformats: no EXT `%s' found.", arg2); + endif + if (strcmpi (arg1, "remove")) + formats(match) = []; + else + ## then it's update + if (! isstruct (arg3)) + error ("imformats: FORMAT to update must be a structure."); + endif + is_valid_format (arg3); + formats(match) = arg3; + endif + varargin{1} = formats; + + case "factory", + formats = default_formats (); + otherwise + ## then we look for a format with that extension. + match = find_ext_idx (formats, arg1) + ## For matlab compatibility, if we don't find any format we must + ## return an empty struct with NO fields. We can't use match as mask + if (any (match)) + varargin{1} = formats(match); + else + varargin{1} = struct (); + endif + endswitch + else + error ("imformats: first argument must be either a structure or string."); + endif + else + varargout{1} = formats; + endif +endfunction + +function formats = default_formats () + + ## The available formats are dependent on what the user has installed at + ## a given time, and how GraphicsMagick was built. Checking for + ## GraphicsMagick features when building Octave is not enough since it + ## delegates some of them to external programs which can be removed or + ## installed at any time. + ## The recommended method would be to use CoderInfoList() to get a list of + ## all available coders and try to write and read back a small test image. + ## But this will not work since some coders are readable or writable only. + ## It will still fail if we test only the ones marked as readable and + ## writable because some RW coders are not of image formats (NULL, 8BIM, + ## or EXIF for example). + ## So we'd need a blacklist (unacceptable because a `bad' coder may be + ## added later) or a whitelist. A whitelist means that even with a + ## super-fancy recent build of GraphicsMagick, some formats won't be listed + ## by imformats but in truth, we will still be able to read and write them + ## since imread() and imwrite() will give it a try anyway. + ## + ## For more info and comments from the GraphicsMagick main developer, see + ## http://sourceforge.net/mailarchive/forum.php?thread_name=alpine.GSO.2.01.1304301916050.2267%40freddy.simplesystems.org&forum_name=graphicsmagick-help + + persistent formats = struct ( "coder", {}, + "ext", {}, + "isa", {}, + "info", {}, + "read", {}, + "write", {}, + "alpha", {}, + "description", {}, + "multipage", {}); + + ## Image IO abilities won't change during the same Octave session, + ## there's no need to go and calculate it all over again if we are + ## requested to reset back to factory. + if (! isempty (formats)) + return + endif + + ## Building the formats info + ## + ## As mentioned above we start with a whitelist of coders. Since the + ## GraphicsMagick build may be missing some coders, we will remove those + ## from the list. Some info can be obtained directly from GraphicsMagick + ## through the CoderInfo object. However, some will need to be hardcoded. + ## + ## The association between file extensions and coders needs to be done + ## with a manually coded list (file extensions do not define the image + ## format and GraphicsMagick will not be fooled by changing the extension). + ## + ## We can get the read, write, description and multipage fields from + ## CoderInfo in C++. We should do the same for alpha (GraphicsMagick + ## calls it matte) but it's not available from CoderInfo. The only way to + ## check it is to create a sample image with each coder, then try to read + ## it back with GraphicsMagick and use the matte method on the Image class. + ## But making such test for each Octave session... meh! While technically + ## it may be possible that the same coder has different support for alpha + ## channel in different versions and builds, this doesn't seem to happen. + ## So we also hardcode those. In the future, maybe the CoderInfo class will + ## have a matte method like it does for multipage. + ## + ## Other notes: some formats have more than one coder that do the same. For + ## example, for jpeg images there is both the JPG and JPEG coders. However, + ## it seems that when reading images, GraphicsMagick only uses one of them + ## and that's the one we list (it's the one reported by imfinfo and that we + ## can use for isa). However, in some cases GraphicsMagick seems to rely + ## uniquely on the file extension ((JBIG and JBG at least. Create an image + ## with each of those coders, swap their extension and it will report the + ## other coder). We don't have such cases on the whitelist but if we did, we + ## would need two entries for such cases. + + ## each row: 1st => Coder, 2nd=> file extensions, 3rd=> alpha + coders = {"BMP", {"bmp"}, true; + "CUR", {"cur"}, false; + "GIF", {"gif"}, true; + "ICO", {"ico"}, true; + "JBG", {"jbg"}, false; + "JBIG", {"jbig"}, false; + "JP2", {"jp2", "jpx"}, true; + "JPEG", {"jpg", "jpeg"}, false; # there is also a JPG coder + "PBM", {"pbm"}, false; + "PCX", {"pcx"}, true; + "PGM", {"pgm"}, false; + "PGM", {"pgm"}, false; + "PNG", {"png"}, true; + ## PNM is a family of formats supporting portable bitmaps (PBM), + ## graymaps (PGM), and pixmaps (PPM). There is no file format + ## associated with pnm itself. If PNM is used as the output format + ## specifier, then GraphicsMagick automatically selects the most + ## appropriate format to represent the image. + "PNM", {"pnm"}, true; + "PPM", {"ppm"}, false; + "SUN", {"ras"}, true; # SUN Rasterfile + "TGA", {"tga", "tpic"}, true; + "TIFF", {"tif", "tiff"}, true; + "XBM", {"xbm"}, false; + "XPM", {"xpm"}, true; + "XWD", {"xwd"}, false; + }; + + for fidx = 1: rows(coders) + formats(fidx).coder = coders{fidx, 1}; + formats(fidx).ext = coders{fidx, 2}; + formats(fidx).alpha = coders{fidx, 3}; + ## default isa is to check if the format returned by imfinfo is the coder + formats(fidx).isa = @(x) isa_magick (coders{fidx,1}, x); + endfor + + ## the default info, read, and write functions + [formats.info ] = deal (@imfinfo); + [formats.read ] = deal (@imread); + [formats.write] = deal (@imread); + + ## fills rest of format information by checking with GraphicsMagick + formats = __magick_formats__ (formats); +endfunction + +function is_valid_format (format) + ## the minimal list of fields required in the structure. We don't + ## require multipage because it doesn't exist in matlab + min_fields = {"ext", "read", "isa", "write", "info", "alpha", "description"}; + fields_mask = cellfun (@(x) isfield (format, x), min_fields); + if (! all (fields_mask)) + error ("imformats: structure has missing field `%s'.", min_fields(! fields_mask){1}); + endif +endfunction + +function match = find_ext_idx (formats, ext) + ## FIXME: is matlab sensitive to file extensions? + ## XXX: what should we do if there's more than one hit? + ## Should this function prevent the addition of + ## duplicated extensions? + match = cellfun (@(x) any (strcmp (x, ext)), {formats.ext}); +endfunction + +function bool = isa_magick (coder, filename) + bool = false; + try + info = imfinfo (filename); + bool = strcmp (coder, info.Format); + end_try_catch +endfunction diff -r 434a0b29ab12 -r 861516dcad19 scripts/image/module.mk --- a/scripts/image/module.mk Fri Jul 05 07:12:18 2013 -0400 +++ b/scripts/image/module.mk Thu Jul 04 18:35:11 2013 +0100 @@ -25,6 +25,7 @@ image/image.m \ image/imagesc.m \ image/imfinfo.m \ + image/imformats.m \ image/imread.m \ image/imshow.m \ image/imwrite.m \