Mercurial > octave-antonio
annotate scripts/image/imformats.m @ 16916:59f575e504dc
Connect imwrite with imformats.
* private/core_imwrite.m: new function. Old code from imwrite() moved here
just like what happened to imfinfo(). See message on cset bfad37d33435
* imfinfo.m: reduced to minimum input check, until finding filename. Passes
all arguments to imageIO().
* imformats.m: change calls to imwrite() to the new core_imwrite().
author | Carnë Draug <carandraug@octave.org> |
---|---|
date | Sun, 07 Jul 2013 21:45:17 +0100 |
parents | d0558ee259ad |
children | bfd119642f6a |
rev | line source |
---|---|
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; | |
16907
04f4f067eb33
imformats: fix typos and silence some statements.
Carnë Draug <carandraug@octave.org>
parents:
16906
diff
changeset
|
89 varargout{1} = formats; |
16901 | 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? | |
16907
04f4f067eb33
imformats: fix typos and silence some statements.
Carnë Draug <carandraug@octave.org>
parents:
16906
diff
changeset
|
98 match = find_ext_idx (formats, arg2); |
16901 | 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 | |
16907
04f4f067eb33
imformats: fix typos and silence some statements.
Carnë Draug <carandraug@octave.org>
parents:
16906
diff
changeset
|
112 varargout{1} = formats; |
16901 | 113 |
114 case "factory", | |
115 formats = default_formats (); | |
116 otherwise | |
117 ## then we look for a format with that extension. | |
16907
04f4f067eb33
imformats: fix typos and silence some statements.
Carnë Draug <carandraug@octave.org>
parents:
16906
diff
changeset
|
118 match = find_ext_idx (formats, arg1); |
16901 | 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)) | |
16907
04f4f067eb33
imformats: fix typos and silence some statements.
Carnë Draug <carandraug@octave.org>
parents:
16906
diff
changeset
|
122 varargout{1} = formats(match); |
16901 | 123 else |
16907
04f4f067eb33
imformats: fix typos and silence some statements.
Carnë Draug <carandraug@octave.org>
parents:
16906
diff
changeset
|
124 varargout{1} = struct (); |
16901 | 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 | |
16906
bfad37d33435
Connect imfinfo with imformats.
Carnë Draug <carandraug@octave.org>
parents:
16901
diff
changeset
|
244 [formats.info ] = deal (@core_imfinfo); |
16913
d0558ee259ad
Connect imread with imformats.
Carnë Draug <carandraug@octave.org>
parents:
16907
diff
changeset
|
245 [formats.read ] = deal (@core_imread); |
16916
59f575e504dc
Connect imwrite with imformats.
Carnë Draug <carandraug@octave.org>
parents:
16913
diff
changeset
|
246 [formats.write] = deal (@core_imwrite); |
16901 | 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 | |
16906
bfad37d33435
Connect imfinfo with imformats.
Carnë Draug <carandraug@octave.org>
parents:
16901
diff
changeset
|
273 info = core_imfinfo (filename); |
16901 | 274 bool = strcmp (coder, info.Format); |
275 end_try_catch | |
276 endfunction |