/src/graphicsmagick/coders/avs.c
Line | Count | Source |
1 | | /* |
2 | | % Copyright (C) 2003-2026 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % Copyright 1991-1999 E. I. du Pont de Nemours and Company |
5 | | % |
6 | | % This program is covered by multiple licenses, which are described in |
7 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
8 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
9 | | % |
10 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
11 | | % % |
12 | | % % |
13 | | % % |
14 | | % AAA V V SSSSS % |
15 | | % A A V V SS % |
16 | | % AAAAA V V SSS % |
17 | | % A A V V SS % |
18 | | % A A V SSSSS % |
19 | | % % |
20 | | % % |
21 | | % Read/Write AVS X Image Format. % |
22 | | % % |
23 | | % % |
24 | | % Software Design % |
25 | | % John Cristy % |
26 | | % July 1992 % |
27 | | % % |
28 | | % % |
29 | | % % |
30 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
31 | | % |
32 | | % |
33 | | */ |
34 | | |
35 | | /* |
36 | | Include declarations. |
37 | | */ |
38 | | #include "magick/studio.h" |
39 | | #include "magick/blob.h" |
40 | | #include "magick/log.h" |
41 | | #include "magick/magick.h" |
42 | | #include "magick/monitor.h" |
43 | | #include "magick/pixel_cache.h" |
44 | | #include "magick/utility.h" |
45 | | #include "magick/static.h" |
46 | | |
47 | | /* |
48 | | Forward declarations. |
49 | | */ |
50 | | static unsigned int |
51 | | WriteAVSImage(const ImageInfo *,Image *); |
52 | | |
53 | | /* |
54 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
55 | | % % |
56 | | % % |
57 | | % % |
58 | | % R e a d A V S I m a g e % |
59 | | % % |
60 | | % % |
61 | | % % |
62 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
63 | | % |
64 | | % Method ReadAVSImage reads an AVS X image file and returns it. It |
65 | | % allocates the memory necessary for the new Image structure and returns a |
66 | | % pointer to the new image. Note that these files often have the extension |
67 | | % 'X' which conflicts with our X11 support coder. |
68 | | % |
69 | | % The format of the ReadAVSImage method is: |
70 | | % |
71 | | % Image *ReadAVSImage(const ImageInfo *image_info,ExceptionInfo *exception) |
72 | | % |
73 | | % A description of each parameter follows: |
74 | | % |
75 | | % o image: Method ReadAVSImage returns a pointer to the image after |
76 | | % reading. A null image is returned if there is a memory shortage or if |
77 | | % the image cannot be read. |
78 | | % |
79 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
80 | | % |
81 | | % o exception: return any errors or warnings in this structure. |
82 | | % |
83 | | % |
84 | | */ |
85 | 561 | #define AVS_WIDTH_LIMIT 65536UL /* Artificially limit width to 64K pixels */ |
86 | 539 | #define AVS_HEIGHT_LIMIT 65536UL /* Artificially limit height to 64K pixels */ |
87 | | #define ThrowAVSReaderException(code_,reason_,image_) \ |
88 | 404 | { \ |
89 | 404 | MagickFreeResourceLimitedMemory(unsigned char *,pixels); \ |
90 | 404 | ThrowReaderException(code_,reason_,image_); \ |
91 | 0 | } |
92 | | static Image *ReadAVSImage(const ImageInfo *image_info,ExceptionInfo *exception) |
93 | 570 | { |
94 | 570 | Image |
95 | 570 | *image; |
96 | | |
97 | 570 | long |
98 | 570 | y; |
99 | | |
100 | 570 | register long |
101 | 570 | x; |
102 | | |
103 | 570 | register PixelPacket |
104 | 570 | *q; |
105 | | |
106 | 570 | register unsigned char |
107 | 570 | *p; |
108 | | |
109 | 570 | unsigned char |
110 | 570 | *pixels = (unsigned char *) NULL; |
111 | | |
112 | 570 | unsigned int |
113 | 570 | status; |
114 | | |
115 | 570 | magick_uint32_t |
116 | 570 | height, |
117 | 570 | width; |
118 | | |
119 | | /* |
120 | | Open image file. |
121 | | */ |
122 | 570 | assert(image_info != (const ImageInfo *) NULL); |
123 | 570 | assert(image_info->signature == MagickSignature); |
124 | 570 | assert(exception != (ExceptionInfo *) NULL); |
125 | 570 | assert(exception->signature == MagickSignature); |
126 | 570 | image=AllocateImage(image_info); |
127 | 570 | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
128 | 570 | if (status == MagickFail) |
129 | 570 | ThrowReaderException(FileOpenError,UnableToOpenFile,image); |
130 | | /* |
131 | | Read AVS image. |
132 | | */ |
133 | 570 | width=ReadBlobMSBLong(image); |
134 | 570 | height=ReadBlobMSBLong(image); |
135 | 570 | if (EOFBlob(image)) |
136 | 561 | ThrowAVSReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
137 | | |
138 | 561 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
139 | 561 | "AVS dimensions %ux%u",(unsigned)width,(unsigned)height); |
140 | | |
141 | | /* |
142 | | Impose a maximum width and height limit in order to avoid |
143 | | incredibly huge allocations. |
144 | | */ |
145 | 561 | if ((width > AVS_WIDTH_LIMIT) || (height > AVS_HEIGHT_LIMIT)) |
146 | 512 | ThrowAVSReaderException(CoderError,ImageColumnOrRowSizeIsNotSupported,image); |
147 | | |
148 | 512 | do |
149 | 512 | { |
150 | | /* |
151 | | Convert AVS raster image to pixel packets. |
152 | | */ |
153 | 512 | size_t |
154 | 512 | row_bytes; |
155 | | |
156 | 512 | image->columns=width; |
157 | 512 | image->rows=height; |
158 | 512 | image->depth=8; |
159 | 512 | if (image_info->ping && (image_info->subrange != 0)) |
160 | 0 | if (image->scene >= (image_info->subimage+image_info->subrange-1)) |
161 | 0 | break; |
162 | 512 | if (CheckImagePixelLimits(image, exception) != MagickPass) |
163 | 470 | ThrowAVSReaderException(ResourceLimitError,ImagePixelLimitExceeded,image); |
164 | 470 | pixels=MagickAllocateResourceLimitedArray(unsigned char *,image->columns,4); |
165 | 470 | if (pixels == (unsigned char *) NULL) |
166 | 470 | ThrowAVSReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
167 | 470 | row_bytes=(size_t) 4*image->columns; |
168 | 108k | for (y=0; y < (long) image->rows; y++) |
169 | 107k | { |
170 | 107k | if (ReadBlob(image,row_bytes,pixels) != row_bytes) |
171 | 107k | ThrowAVSReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
172 | 107k | p=pixels; |
173 | 107k | q=SetImagePixels(image,0,y,image->columns,1); |
174 | 107k | if (q == (PixelPacket *) NULL) |
175 | 0 | { |
176 | 0 | status=MagickFail; |
177 | 0 | break; |
178 | 0 | } |
179 | 11.2M | for (x=0; x < (long) image->columns; x++) |
180 | 11.1M | { |
181 | 11.1M | q->opacity=(Quantum) (MaxRGB-ScaleCharToQuantum(*p++)); |
182 | 11.1M | q->red=ScaleCharToQuantum(*p++); |
183 | 11.1M | q->green=ScaleCharToQuantum(*p++); |
184 | 11.1M | q->blue=ScaleCharToQuantum(*p++); |
185 | 11.1M | image->matte|=(q->opacity != OpaqueOpacity); |
186 | 11.1M | q++; |
187 | 11.1M | } |
188 | 107k | if (!SyncImagePixels(image)) |
189 | 0 | { |
190 | 0 | status=MagickFail; |
191 | 0 | break; |
192 | 0 | } |
193 | 107k | if (image->previous == (Image *) NULL) |
194 | 107k | if (QuantumTick(y,image->rows)) |
195 | 16.1k | if (!MagickMonitorFormatted(y,image->rows,exception, |
196 | 16.1k | LoadImageText,image->filename, |
197 | 16.1k | image->columns,image->rows)) |
198 | 0 | { |
199 | 0 | status=MagickFail; |
200 | 0 | break; |
201 | 0 | } |
202 | 107k | } |
203 | 166 | MagickFreeResourceLimitedMemory(unsigned char *,pixels); |
204 | | |
205 | 166 | if (MagickFail == status) |
206 | 0 | break; |
207 | | |
208 | 166 | StopTimer(&image->timer); |
209 | | |
210 | | /* |
211 | | Proceed to next image. |
212 | | */ |
213 | 166 | if (image_info->subrange != 0) |
214 | 166 | if (image->scene >= (image_info->subimage+image_info->subrange-1)) |
215 | 166 | break; |
216 | 0 | width=ReadBlobMSBLong(image); |
217 | 0 | height=ReadBlobMSBLong(image); |
218 | 0 | if (!(EOFBlob(image)) && (width <= AVS_WIDTH_LIMIT) && |
219 | 0 | (height <= AVS_HEIGHT_LIMIT)) |
220 | 0 | { |
221 | | /* |
222 | | Allocate next image structure. |
223 | | */ |
224 | 0 | AllocateNextImage(image_info,image); |
225 | 0 | if (image->next == (Image *) NULL) |
226 | 0 | { |
227 | 0 | DestroyImageList(image); |
228 | 0 | return((Image *) NULL); |
229 | 0 | } |
230 | 0 | image=SyncNextImageInList(image); |
231 | 0 | status=MagickMonitorFormatted(TellBlob(image),GetBlobSize(image), |
232 | 0 | exception,LoadImagesText,image->filename); |
233 | 0 | if (status == MagickFail) |
234 | 0 | break; |
235 | 0 | } |
236 | 0 | } while (!(EOFBlob(image))); |
237 | 166 | while (image->previous != (Image *) NULL) |
238 | 0 | image=image->previous; |
239 | 166 | CloseBlob(image); |
240 | | |
241 | 166 | if (MagickFail == status) |
242 | 0 | { |
243 | 0 | DestroyImageList(image); |
244 | 0 | image=(Image *) NULL; |
245 | 0 | } |
246 | | |
247 | 166 | return(image); |
248 | 512 | } |
249 | | |
250 | | /* |
251 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
252 | | % % |
253 | | % % |
254 | | % % |
255 | | % R e g i s t e r A V S I m a g e % |
256 | | % % |
257 | | % % |
258 | | % % |
259 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
260 | | % |
261 | | % Method RegisterAVSImage adds attributes for the AVS image format to |
262 | | % the list of supported formats. The attributes include the image format |
263 | | % tag, a method to read and/or write the format, whether the format |
264 | | % supports the saving of more than one frame to the same file or blob, |
265 | | % whether the format supports native in-memory I/O, and a brief |
266 | | % description of the format. |
267 | | % |
268 | | % The format of the RegisterAVSImage method is: |
269 | | % |
270 | | % RegisterAVSImage(void) |
271 | | % |
272 | | */ |
273 | | ModuleExport void RegisterAVSImage(void) |
274 | 1 | { |
275 | 1 | MagickInfo |
276 | 1 | *entry; |
277 | | |
278 | 1 | entry=SetMagickInfo("AVS"); |
279 | 1 | entry->decoder=(DecoderHandler) ReadAVSImage; |
280 | 1 | entry->encoder=(EncoderHandler) WriteAVSImage; |
281 | 1 | entry->description="AVS X image"; |
282 | 1 | entry->module="AVS"; |
283 | 1 | entry->coder_class=UnstableCoderClass; |
284 | 1 | (void) RegisterMagickInfo(entry); |
285 | 1 | } |
286 | | |
287 | | /* |
288 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
289 | | % % |
290 | | % % |
291 | | % % |
292 | | % U n r e g i s t e r A V S I m a g e % |
293 | | % % |
294 | | % % |
295 | | % % |
296 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
297 | | % |
298 | | % Method UnregisterAVSImage removes format registrations made by the |
299 | | % AVS module from the list of supported formats. |
300 | | % |
301 | | % The format of the UnregisterAVSImage method is: |
302 | | % |
303 | | % UnregisterAVSImage(void) |
304 | | % |
305 | | */ |
306 | | ModuleExport void UnregisterAVSImage(void) |
307 | 0 | { |
308 | 0 | (void) UnregisterMagickInfo("AVS"); |
309 | 0 | } |
310 | | |
311 | | /* |
312 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
313 | | % % |
314 | | % % |
315 | | % % |
316 | | % W r i t e A V S I m a g e % |
317 | | % % |
318 | | % % |
319 | | % % |
320 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
321 | | % |
322 | | % Method WriteAVSImage writes an image to a file in AVS X image format. |
323 | | % |
324 | | % The format of the WriteAVSImage method is: |
325 | | % |
326 | | % unsigned int WriteAVSImage(const ImageInfo *image_info,Image *image) |
327 | | % |
328 | | % A description of each parameter follows. |
329 | | % |
330 | | % o status: Method WriteAVSImage return True if the image is written. |
331 | | % False is returned is there is a memory shortage or if the image file |
332 | | % fails to write. |
333 | | % |
334 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
335 | | % |
336 | | % o image: A pointer to an Image structure. |
337 | | % |
338 | | % |
339 | | */ |
340 | | static unsigned int WriteAVSImage(const ImageInfo *image_info,Image *image) |
341 | 166 | { |
342 | 166 | register const PixelPacket |
343 | 166 | *p; |
344 | | |
345 | 166 | register long |
346 | 166 | x, |
347 | 166 | y; |
348 | | |
349 | 166 | register unsigned char |
350 | 166 | *q; |
351 | | |
352 | 166 | unsigned char |
353 | 166 | *pixels; |
354 | | |
355 | 166 | unsigned int |
356 | 166 | status; |
357 | | |
358 | 166 | unsigned long |
359 | 166 | scene; |
360 | | |
361 | 166 | size_t |
362 | 166 | image_list_length; |
363 | | |
364 | | /* |
365 | | Open output image file. |
366 | | */ |
367 | 166 | assert(image_info != (const ImageInfo *) NULL); |
368 | 166 | assert(image_info->signature == MagickSignature); |
369 | 166 | assert(image != (Image *) NULL); |
370 | 166 | assert(image->signature == MagickSignature); |
371 | 166 | image_list_length=GetImageListLength(image); |
372 | 166 | status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); |
373 | 166 | if (status == False) |
374 | 166 | ThrowWriterException(FileOpenError,UnableToOpenFile,image); |
375 | 166 | scene=0; |
376 | 166 | do |
377 | 166 | { |
378 | | /* |
379 | | Write AVS header. |
380 | | */ |
381 | 166 | if (TransformColorspace(image,RGBColorspace) == MagickFail) |
382 | 166 | ThrowWriterException(CoderError,UnableToTransformColorspace,image); |
383 | | |
384 | 166 | (void) WriteBlobMSBLong(image,image->columns); |
385 | 166 | (void) WriteBlobMSBLong(image,image->rows); |
386 | | /* |
387 | | Allocate memory for pixels. |
388 | | */ |
389 | 166 | pixels=MagickAllocateResourceLimitedMemory(unsigned char *,image->columns*sizeof(PixelPacket)); |
390 | 166 | if (pixels == (unsigned char *) NULL) |
391 | 166 | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
392 | | /* |
393 | | Convert MIFF to AVS raster pixels. |
394 | | */ |
395 | 56.4k | for (y=0; y < (long) image->rows; y++) |
396 | 56.2k | { |
397 | 56.2k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
398 | 56.2k | if (p == (PixelPacket *) NULL) |
399 | 0 | break; |
400 | 56.2k | q=pixels; |
401 | 1.73M | for (x=0; x < (long) image->columns; x++) |
402 | 1.68M | { |
403 | 1.68M | *q++=ScaleQuantumToChar( |
404 | 1.68M | MaxRGB-(image->matte ? p->opacity : OpaqueOpacity)); |
405 | 1.68M | *q++=ScaleQuantumToChar(p->red); |
406 | 1.68M | *q++=ScaleQuantumToChar(p->green); |
407 | 1.68M | *q++=ScaleQuantumToChar(p->blue); |
408 | 1.68M | p++; |
409 | 1.68M | } |
410 | 56.2k | (void) WriteBlob(image,q-pixels,(char *) pixels); |
411 | 56.2k | if (image->previous == (Image *) NULL) |
412 | 56.2k | if (QuantumTick(y,image->rows)) |
413 | 10.6k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
414 | 10.6k | SaveImageText,image->filename, |
415 | 10.6k | image->columns,image->rows)) |
416 | 0 | break; |
417 | 56.2k | } |
418 | 166 | MagickFreeResourceLimitedMemory(unsigned char *,pixels); |
419 | 166 | if (image->next == (Image *) NULL) |
420 | 166 | break; |
421 | 0 | image=SyncNextImageInList(image); |
422 | 0 | status=MagickMonitorFormatted(scene++,image_list_length, |
423 | 0 | &image->exception,SaveImagesText, |
424 | 0 | image->filename); |
425 | 0 | if (status == False) |
426 | 0 | break; |
427 | 0 | } while (image_info->adjoin); |
428 | 166 | if (image_info->adjoin) |
429 | 166 | while (image->previous != (Image *) NULL) |
430 | 0 | image=image->previous; |
431 | 166 | status &= CloseBlob(image); |
432 | 166 | return(status); |
433 | 166 | } |