16901
|
1 ## Copyright (C) 2013 Carnë Draug |
|
2 ## |
|
3 ## This file is part of Octave. |
|
4 ## |
|
5 ## Octave is free software; you can redistribute it and/or modify it |
|
6 ## under the terms of the GNU General Public License as published by |
|
7 ## the Free Software Foundation; either version 3 of the License, or (at |
|
8 ## your option) any later version. |
|
9 ## |
|
10 ## Octave is distributed in the hope that it will be useful, but |
|
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 ## General Public License for more details. |
|
14 ## |
|
15 ## You should have received a copy of the GNU General Public License |
|
16 ## along with Octave; see the file COPYING. If not, see |
|
17 ## <http://www.gnu.org/licenses/>. |
|
18 |
|
19 ## -*- texinfo -*- |
|
20 ## @deftypefn {Function File} {} imformats () |
|
21 ## @deftypefnx {Function File} {@var{formats} =} imformats (@var{ext}) |
|
22 ## @deftypefnx {Function File} {@var{formats} =} imformats (@var{format}) |
|
23 ## @deftypefnx {Function File} {@var{formats} =} imformats ("add", @var{format}) |
|
24 ## @deftypefnx {Function File} {@var{formats} =} imformats ("remove", @var{ext}) |
|
25 ## @deftypefnx {Function File} {@var{formats} =} imformats ("update", @var{ext}, @var{format}) |
|
26 ## @deftypefnx {Function File} {@var{formats} =} imformats ("factory") |
|
27 ## Manage supported image formats. |
|
28 ## |
|
29 ## @var{formats} is a structure with information about each supported file |
|
30 ## format, or from a specific format @var{ext}, the value displayed on the |
|
31 ## field @code{ext}. It contains the following fields: |
|
32 ## |
|
33 ## @table @asis |
|
34 ## @item ext |
|
35 ## The name of the file format. This may match the file extension but Octave |
|
36 ## will automatically detect the file format. |
|
37 ## @item description |
|
38 ## A long description of the file format. |
|
39 ## @item isa |
|
40 ## A function handle to confirm if a file is of the specified format. |
|
41 ## @item write |
|
42 ## A function handle to write if a file is of the specified format. |
|
43 ## @item read |
|
44 ## A function handle to open files the specified format. |
|
45 ## @item info |
|
46 ## A function handle to obtain image information of the specified format. |
|
47 ## @item alpha |
|
48 ## Logical value if format supports alpha channel (transparency or matte). |
|
49 ## @item multipage |
|
50 ## Logical value if format supports multipage (multiple images per file). |
|
51 ## @end table |
|
52 ## |
|
53 ## It is possible to change the way Octave manages file formats with the options |
|
54 ## @code{"add"}, @code{"remove"}, and @code{"update"}, and supplying a |
|
55 ## structure @var{format} with the required fields. The option |
|
56 ## @code{"factory"} resets the configuration to the default. |
|
57 ## |
|
58 ## This can be used by Octave packages to extend the image reading capabilities |
|
59 ## Octave, through use of the PKG_ADD and PKG_DEL commands. |
|
60 ## |
|
61 ## @seealso{imfinfo, imread, imwrite} |
|
62 ## @end deftypefn |
|
63 |
|
64 ## Author: Carnë Draug <carandraug@octave.org> |
|
65 |
|
66 function varargout = imformats (arg1, arg2, arg3) |
|
67 if (nargin > 3) |
|
68 print_usage (); |
|
69 endif |
|
70 |
|
71 persistent formats = default_formats (); |
|
72 |
|
73 if (nargin == 0 && nargout == 0) |
|
74 error ("imformats: pretty print not yet implemented."); |
|
75 elseif (nargin >= 1) |
|
76 if (isstruct (arg1)) |
|
77 arrayfun (@is_valid_format, arg1); |
|
78 ## FIXME: what is the return value in this situation? |
|
79 formats = arg1; |
|
80 |
|
81 elseif (ischar (arg1)) |
|
82 switch (tolower (arg1)) |
|
83 case "add", |
|
84 if (! isstruct (arg2)) |
|
85 error ("imformats: FORMAT to %s must be a structure.", arg1); |
|
86 endif |
|
87 arrayfun (@is_valid_format, arg2); |
|
88 formats(end + numel (b)) = arg2; |
|
89 varargin{1} = formats; |
|
90 |
|
91 case {"remove", "update"}, |
|
92 if (! ischar (arg2)) |
|
93 error ("imformats: EXT to %s must be a string.", arg1); |
|
94 endif |
|
95 ## FIXME: suppose a format with multiple extensions. If one of |
|
96 ## them is requested to be removed, should we remove the |
|
97 ## whole format, or just that extension from the format? |
|
98 match = find_ext_idx (formats, arg2) |
|
99 if (! any (match)) |
|
100 error ("imformats: no EXT `%s' found.", arg2); |
|
101 endif |
|
102 if (strcmpi (arg1, "remove")) |
|
103 formats(match) = []; |
|
104 else |
|
105 ## then it's update |
|
106 if (! isstruct (arg3)) |
|
107 error ("imformats: FORMAT to update must be a structure."); |
|
108 endif |
|
109 is_valid_format (arg3); |
|
110 formats(match) = arg3; |
|
111 endif |
|
112 varargin{1} = formats; |
|
113 |
|
114 case "factory", |
|
115 formats = default_formats (); |
|
116 otherwise |
|
117 ## then we look for a format with that extension. |
|
118 match = find_ext_idx (formats, arg1) |
|
119 ## For matlab compatibility, if we don't find any format we must |
|
120 ## return an empty struct with NO fields. We can't use match as mask |
|
121 if (any (match)) |
|
122 varargin{1} = formats(match); |
|
123 else |
|
124 varargin{1} = struct (); |
|
125 endif |
|
126 endswitch |
|
127 else |
|
128 error ("imformats: first argument must be either a structure or string."); |
|
129 endif |
|
130 else |
|
131 varargout{1} = formats; |
|
132 endif |
|
133 endfunction |
|
134 |
|
135 function formats = default_formats () |
|
136 |
|
137 ## The available formats are dependent on what the user has installed at |
|
138 ## a given time, and how GraphicsMagick was built. Checking for |
|
139 ## GraphicsMagick features when building Octave is not enough since it |
|
140 ## delegates some of them to external programs which can be removed or |
|
141 ## installed at any time. |
|
142 ## The recommended method would be to use CoderInfoList() to get a list of |
|
143 ## all available coders and try to write and read back a small test image. |
|
144 ## But this will not work since some coders are readable or writable only. |
|
145 ## It will still fail if we test only the ones marked as readable and |
|
146 ## writable because some RW coders are not of image formats (NULL, 8BIM, |
|
147 ## or EXIF for example). |
|
148 ## So we'd need a blacklist (unacceptable because a `bad' coder may be |
|
149 ## added later) or a whitelist. A whitelist means that even with a |
|
150 ## super-fancy recent build of GraphicsMagick, some formats won't be listed |
|
151 ## by imformats but in truth, we will still be able to read and write them |
|
152 ## since imread() and imwrite() will give it a try anyway. |
|
153 ## |
|
154 ## For more info and comments from the GraphicsMagick main developer, see |
|
155 ## http://sourceforge.net/mailarchive/forum.php?thread_name=alpine.GSO.2.01.1304301916050.2267%40freddy.simplesystems.org&forum_name=graphicsmagick-help |
|
156 |
|
157 persistent formats = struct ( "coder", {}, |
|
158 "ext", {}, |
|
159 "isa", {}, |
|
160 "info", {}, |
|
161 "read", {}, |
|
162 "write", {}, |
|
163 "alpha", {}, |
|
164 "description", {}, |
|
165 "multipage", {}); |
|
166 |
|
167 ## Image IO abilities won't change during the same Octave session, |
|
168 ## there's no need to go and calculate it all over again if we are |
|
169 ## requested to reset back to factory. |
|
170 if (! isempty (formats)) |
|
171 return |
|
172 endif |
|
173 |
|
174 ## Building the formats info |
|
175 ## |
|
176 ## As mentioned above we start with a whitelist of coders. Since the |
|
177 ## GraphicsMagick build may be missing some coders, we will remove those |
|
178 ## from the list. Some info can be obtained directly from GraphicsMagick |
|
179 ## through the CoderInfo object. However, some will need to be hardcoded. |
|
180 ## |
|
181 ## The association between file extensions and coders needs to be done |
|
182 ## with a manually coded list (file extensions do not define the image |
|
183 ## format and GraphicsMagick will not be fooled by changing the extension). |
|
184 ## |
|
185 ## We can get the read, write, description and multipage fields from |
|
186 ## CoderInfo in C++. We should do the same for alpha (GraphicsMagick |
|
187 ## calls it matte) but it's not available from CoderInfo. The only way to |
|
188 ## check it is to create a sample image with each coder, then try to read |
|
189 ## it back with GraphicsMagick and use the matte method on the Image class. |
|
190 ## But making such test for each Octave session... meh! While technically |
|
191 ## it may be possible that the same coder has different support for alpha |
|
192 ## channel in different versions and builds, this doesn't seem to happen. |
|
193 ## So we also hardcode those. In the future, maybe the CoderInfo class will |
|
194 ## have a matte method like it does for multipage. |
|
195 ## |
|
196 ## Other notes: some formats have more than one coder that do the same. For |
|
197 ## example, for jpeg images there is both the JPG and JPEG coders. However, |
|
198 ## it seems that when reading images, GraphicsMagick only uses one of them |
|
199 ## and that's the one we list (it's the one reported by imfinfo and that we |
|
200 ## can use for isa). However, in some cases GraphicsMagick seems to rely |
|
201 ## uniquely on the file extension ((JBIG and JBG at least. Create an image |
|
202 ## with each of those coders, swap their extension and it will report the |
|
203 ## other coder). We don't have such cases on the whitelist but if we did, we |
|
204 ## would need two entries for such cases. |
|
205 |
|
206 ## each row: 1st => Coder, 2nd=> file extensions, 3rd=> alpha |
|
207 coders = {"BMP", {"bmp"}, true; |
|
208 "CUR", {"cur"}, false; |
|
209 "GIF", {"gif"}, true; |
|
210 "ICO", {"ico"}, true; |
|
211 "JBG", {"jbg"}, false; |
|
212 "JBIG", {"jbig"}, false; |
|
213 "JP2", {"jp2", "jpx"}, true; |
|
214 "JPEG", {"jpg", "jpeg"}, false; # there is also a JPG coder |
|
215 "PBM", {"pbm"}, false; |
|
216 "PCX", {"pcx"}, true; |
|
217 "PGM", {"pgm"}, false; |
|
218 "PGM", {"pgm"}, false; |
|
219 "PNG", {"png"}, true; |
|
220 ## PNM is a family of formats supporting portable bitmaps (PBM), |
|
221 ## graymaps (PGM), and pixmaps (PPM). There is no file format |
|
222 ## associated with pnm itself. If PNM is used as the output format |
|
223 ## specifier, then GraphicsMagick automatically selects the most |
|
224 ## appropriate format to represent the image. |
|
225 "PNM", {"pnm"}, true; |
|
226 "PPM", {"ppm"}, false; |
|
227 "SUN", {"ras"}, true; # SUN Rasterfile |
|
228 "TGA", {"tga", "tpic"}, true; |
|
229 "TIFF", {"tif", "tiff"}, true; |
|
230 "XBM", {"xbm"}, false; |
|
231 "XPM", {"xpm"}, true; |
|
232 "XWD", {"xwd"}, false; |
|
233 }; |
|
234 |
|
235 for fidx = 1: rows(coders) |
|
236 formats(fidx).coder = coders{fidx, 1}; |
|
237 formats(fidx).ext = coders{fidx, 2}; |
|
238 formats(fidx).alpha = coders{fidx, 3}; |
|
239 ## default isa is to check if the format returned by imfinfo is the coder |
|
240 formats(fidx).isa = @(x) isa_magick (coders{fidx,1}, x); |
|
241 endfor |
|
242 |
|
243 ## the default info, read, and write functions |
|
244 [formats.info ] = deal (@imfinfo); |
|
245 [formats.read ] = deal (@imread); |
|
246 [formats.write] = deal (@imread); |
|
247 |
|
248 ## fills rest of format information by checking with GraphicsMagick |
|
249 formats = __magick_formats__ (formats); |
|
250 endfunction |
|
251 |
|
252 function is_valid_format (format) |
|
253 ## the minimal list of fields required in the structure. We don't |
|
254 ## require multipage because it doesn't exist in matlab |
|
255 min_fields = {"ext", "read", "isa", "write", "info", "alpha", "description"}; |
|
256 fields_mask = cellfun (@(x) isfield (format, x), min_fields); |
|
257 if (! all (fields_mask)) |
|
258 error ("imformats: structure has missing field `%s'.", min_fields(! fields_mask){1}); |
|
259 endif |
|
260 endfunction |
|
261 |
|
262 function match = find_ext_idx (formats, ext) |
|
263 ## FIXME: is matlab sensitive to file extensions? |
|
264 ## XXX: what should we do if there's more than one hit? |
|
265 ## Should this function prevent the addition of |
|
266 ## duplicated extensions? |
|
267 match = cellfun (@(x) any (strcmp (x, ext)), {formats.ext}); |
|
268 endfunction |
|
269 |
|
270 function bool = isa_magick (coder, filename) |
|
271 bool = false; |
|
272 try |
|
273 info = imfinfo (filename); |
|
274 bool = strcmp (coder, info.Format); |
|
275 end_try_catch |
|
276 endfunction |