/src/imagemagick/coders/pcx.c
Line | Count | Source |
1 | | /* |
2 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3 | | % % |
4 | | % % |
5 | | % % |
6 | | % PPPP CCCC X X % |
7 | | % P P C X X % |
8 | | % PPPP C X % |
9 | | % P C X X % |
10 | | % P CCCC X X % |
11 | | % % |
12 | | % % |
13 | | % Read/Write ZSoft IBM PC Paintbrush Image Format % |
14 | | % % |
15 | | % Software Design % |
16 | | % Cristy % |
17 | | % July 1992 % |
18 | | % % |
19 | | % % |
20 | | % Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization % |
21 | | % dedicated to making software imaging solutions freely available. % |
22 | | % % |
23 | | % You may not use this file except in compliance with the License. You may % |
24 | | % obtain a copy of the License at % |
25 | | % % |
26 | | % https://imagemagick.org/license/ % |
27 | | % % |
28 | | % Unless required by applicable law or agreed to in writing, software % |
29 | | % distributed under the License is distributed on an "AS IS" BASIS, % |
30 | | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % |
31 | | % See the License for the specific language governing permissions and % |
32 | | % limitations under the License. % |
33 | | % % |
34 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
35 | | % |
36 | | % |
37 | | */ |
38 | | |
39 | | /* |
40 | | Include declarations. |
41 | | */ |
42 | | #include "MagickCore/studio.h" |
43 | | #include "MagickCore/attribute.h" |
44 | | #include "MagickCore/blob.h" |
45 | | #include "MagickCore/blob-private.h" |
46 | | #include "MagickCore/cache.h" |
47 | | #include "MagickCore/color.h" |
48 | | #include "MagickCore/color-private.h" |
49 | | #include "MagickCore/colormap.h" |
50 | | #include "MagickCore/colorspace.h" |
51 | | #include "MagickCore/colorspace-private.h" |
52 | | #include "MagickCore/exception.h" |
53 | | #include "MagickCore/exception-private.h" |
54 | | #include "MagickCore/image.h" |
55 | | #include "MagickCore/image-private.h" |
56 | | #include "MagickCore/list.h" |
57 | | #include "MagickCore/magick.h" |
58 | | #include "MagickCore/memory_.h" |
59 | | #include "MagickCore/memory-private.h" |
60 | | #include "MagickCore/monitor.h" |
61 | | #include "MagickCore/monitor-private.h" |
62 | | #include "MagickCore/pixel-accessor.h" |
63 | | #include "MagickCore/quantum-private.h" |
64 | | #include "MagickCore/static.h" |
65 | | #include "MagickCore/string_.h" |
66 | | #include "MagickCore/module.h" |
67 | | |
68 | | /* |
69 | | Typedef declarations. |
70 | | */ |
71 | | typedef struct _PCXInfo |
72 | | { |
73 | | unsigned char |
74 | | identifier, |
75 | | version, |
76 | | encoding, |
77 | | bits_per_pixel; |
78 | | |
79 | | unsigned short |
80 | | left, |
81 | | top, |
82 | | right, |
83 | | bottom, |
84 | | horizontal_resolution, |
85 | | vertical_resolution; |
86 | | |
87 | | unsigned char |
88 | | reserved, |
89 | | planes; |
90 | | |
91 | | unsigned short |
92 | | bytes_per_line, |
93 | | palette_info, |
94 | | horizontal_screensize, |
95 | | vertical_screensize; |
96 | | |
97 | | unsigned char |
98 | | colormap_signature; |
99 | | } PCXInfo; |
100 | | |
101 | | /* |
102 | | Forward declarations. |
103 | | */ |
104 | | static MagickBooleanType |
105 | | WritePCXImage(const ImageInfo *,Image *,ExceptionInfo *); |
106 | | |
107 | | /* |
108 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
109 | | % % |
110 | | % % |
111 | | % % |
112 | | % I s D C X % |
113 | | % % |
114 | | % % |
115 | | % % |
116 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
117 | | % |
118 | | % IsDCX() returns MagickTrue if the image format type, identified by the |
119 | | % magick string, is DCX. |
120 | | % |
121 | | % The format of the IsDCX method is: |
122 | | % |
123 | | % MagickBooleanType IsDCX(const unsigned char *magick,const size_t length) |
124 | | % |
125 | | % A description of each parameter follows: |
126 | | % |
127 | | % o magick: compare image format pattern against these bytes. |
128 | | % |
129 | | % o length: Specifies the length of the magick string. |
130 | | % |
131 | | */ |
132 | | static MagickBooleanType IsDCX(const unsigned char *magick,const size_t length) |
133 | 0 | { |
134 | 0 | if (length < 4) |
135 | 0 | return(MagickFalse); |
136 | 0 | if (memcmp(magick,"\261\150\336\72",4) == 0) |
137 | 0 | return(MagickTrue); |
138 | 0 | return(MagickFalse); |
139 | 0 | } |
140 | | |
141 | | /* |
142 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
143 | | % % |
144 | | % % |
145 | | % % |
146 | | % I s P C X % |
147 | | % % |
148 | | % % |
149 | | % % |
150 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
151 | | % |
152 | | % IsPCX() returns MagickTrue if the image format type, identified by the |
153 | | % magick string, is PCX. |
154 | | % |
155 | | % The format of the IsPCX method is: |
156 | | % |
157 | | % MagickBooleanType IsPCX(const unsigned char *magick,const size_t length) |
158 | | % |
159 | | % A description of each parameter follows: |
160 | | % |
161 | | % o magick: compare image format pattern against these bytes. |
162 | | % |
163 | | % o length: Specifies the length of the magick string. |
164 | | % |
165 | | */ |
166 | | static MagickBooleanType IsPCX(const unsigned char *magick,const size_t length) |
167 | 0 | { |
168 | 0 | if (length < 2) |
169 | 0 | return(MagickFalse); |
170 | 0 | if (memcmp(magick,"\012\002",2) == 0) |
171 | 0 | return(MagickTrue); |
172 | 0 | if (memcmp(magick,"\012\005",2) == 0) |
173 | 0 | return(MagickTrue); |
174 | 0 | return(MagickFalse); |
175 | 0 | } |
176 | | |
177 | | /* |
178 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
179 | | % % |
180 | | % % |
181 | | % % |
182 | | % R e a d P C X I m a g e % |
183 | | % % |
184 | | % % |
185 | | % % |
186 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
187 | | % |
188 | | % ReadPCXImage() reads a ZSoft IBM PC Paintbrush file and returns it. |
189 | | % It allocates the memory necessary for the new Image structure and returns |
190 | | % a pointer to the new image. |
191 | | % |
192 | | % The format of the ReadPCXImage method is: |
193 | | % |
194 | | % Image *ReadPCXImage(const ImageInfo *image_info,ExceptionInfo *exception) |
195 | | % |
196 | | % A description of each parameter follows: |
197 | | % |
198 | | % o image_info: the image info. |
199 | | % |
200 | | % o exception: return any errors or warnings in this structure. |
201 | | % |
202 | | */ |
203 | | static Image *ReadPCXImage(const ImageInfo *image_info,ExceptionInfo *exception) |
204 | 1.25k | { |
205 | 3.61k | #define MaxNumberScenes 1024 |
206 | 1.25k | #define ThrowPCXException(severity,tag) \ |
207 | 649 | { \ |
208 | 649 | if (scanline != (unsigned char *) NULL) \ |
209 | 649 | scanline=(unsigned char *) RelinquishMagickMemory(scanline); \ |
210 | 649 | if (pixel_info != (MemoryInfo *) NULL) \ |
211 | 649 | pixel_info=RelinquishVirtualMemory(pixel_info); \ |
212 | 649 | if (page_table != (MagickOffsetType *) NULL) \ |
213 | 649 | page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table); \ |
214 | 649 | ThrowReaderException(severity,tag); \ |
215 | 0 | } |
216 | | |
217 | 1.25k | Image |
218 | 1.25k | *image; |
219 | | |
220 | 1.25k | int |
221 | 1.25k | bits, |
222 | 1.25k | id, |
223 | 1.25k | mask; |
224 | | |
225 | 1.25k | MagickBooleanType |
226 | 1.25k | status; |
227 | | |
228 | 1.25k | MagickOffsetType |
229 | 1.25k | offset, |
230 | 1.25k | *page_table; |
231 | | |
232 | 1.25k | MemoryInfo |
233 | 1.25k | *pixel_info; |
234 | | |
235 | 1.25k | PCXInfo |
236 | 1.25k | pcx_info; |
237 | | |
238 | 1.25k | Quantum |
239 | 1.25k | *q; |
240 | | |
241 | 1.25k | size_t |
242 | 1.25k | one, |
243 | 1.25k | pcx_packets; |
244 | | |
245 | 1.25k | ssize_t |
246 | 1.25k | count, |
247 | 1.25k | i, |
248 | 1.25k | x, |
249 | 1.25k | y; |
250 | | |
251 | 1.25k | unsigned char |
252 | 1.25k | *p, |
253 | 1.25k | packet, |
254 | 1.25k | pcx_colormap[768], |
255 | 1.25k | *pixels, |
256 | 1.25k | *r, |
257 | 1.25k | *scanline; |
258 | | |
259 | | /* |
260 | | Open image file. |
261 | | */ |
262 | 1.25k | assert(image_info != (const ImageInfo *) NULL); |
263 | 1.25k | assert(image_info->signature == MagickCoreSignature); |
264 | 1.25k | assert(exception != (ExceptionInfo *) NULL); |
265 | 1.25k | assert(exception->signature == MagickCoreSignature); |
266 | 1.25k | if (IsEventLogging() != MagickFalse) |
267 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", |
268 | 0 | image_info->filename); |
269 | 1.25k | image=AcquireImage(image_info,exception); |
270 | 1.25k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
271 | 1.25k | if (status == MagickFalse) |
272 | 0 | { |
273 | 0 | image=DestroyImageList(image); |
274 | 0 | return((Image *) NULL); |
275 | 0 | } |
276 | | /* |
277 | | Determine if this a PCX file. |
278 | | */ |
279 | 1.25k | page_table=(MagickOffsetType *) NULL; |
280 | 1.25k | scanline=(unsigned char *) NULL; |
281 | 1.25k | pixel_info=(MemoryInfo *) NULL; |
282 | 1.25k | if (LocaleCompare(image_info->magick,"DCX") == 0) |
283 | 49 | { |
284 | 49 | size_t |
285 | 49 | magic; |
286 | | |
287 | | /* |
288 | | Read the DCX page table. |
289 | | */ |
290 | 49 | magic=ReadBlobLSBLong(image); |
291 | 49 | if (magic != 987654321) |
292 | 46 | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
293 | 46 | page_table=(MagickOffsetType *) AcquireQuantumMemory(MaxNumberScenes, |
294 | 46 | sizeof(*page_table)); |
295 | 46 | if (page_table == (MagickOffsetType *) NULL) |
296 | 46 | ThrowPCXException(ResourceLimitError,"MemoryAllocationFailed"); |
297 | 1.83k | for (id=0; id < MaxNumberScenes; id++) |
298 | 1.82k | { |
299 | 1.82k | page_table[id]=(MagickOffsetType) ReadBlobLSBLong(image); |
300 | 1.82k | if (page_table[id] == 0) |
301 | 45 | break; |
302 | 1.82k | } |
303 | 46 | } |
304 | 1.25k | if (page_table != (MagickOffsetType *) NULL) |
305 | 46 | { |
306 | 46 | offset=SeekBlob(image,(MagickOffsetType) page_table[0],SEEK_SET); |
307 | 46 | if (offset < 0) |
308 | 46 | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
309 | 46 | } |
310 | 1.25k | count=ReadBlob(image,1,&pcx_info.identifier); |
311 | 1.25k | for (id=1; id < MaxNumberScenes; id++) |
312 | 1.25k | { |
313 | 1.25k | int |
314 | 1.25k | bits_per_pixel; |
315 | | |
316 | | /* |
317 | | Verify PCX identifier. |
318 | | */ |
319 | 1.25k | pcx_info.version=(unsigned char) ReadBlobByte(image); |
320 | 1.25k | if ((count != 1) || (pcx_info.identifier != 0x0a)) |
321 | 1.20k | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
322 | 1.20k | pcx_info.encoding=(unsigned char) ReadBlobByte(image); |
323 | 1.20k | bits_per_pixel=ReadBlobByte(image); |
324 | 1.20k | if (bits_per_pixel == -1) |
325 | 1.19k | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
326 | 1.19k | pcx_info.bits_per_pixel=(unsigned char) bits_per_pixel; |
327 | 1.19k | pcx_info.left=ReadBlobLSBShort(image); |
328 | 1.19k | pcx_info.top=ReadBlobLSBShort(image); |
329 | 1.19k | pcx_info.right=ReadBlobLSBShort(image); |
330 | 1.19k | pcx_info.bottom=ReadBlobLSBShort(image); |
331 | 1.19k | pcx_info.horizontal_resolution=ReadBlobLSBShort(image); |
332 | 1.19k | pcx_info.vertical_resolution=ReadBlobLSBShort(image); |
333 | 1.19k | if (EOFBlob(image) != MagickFalse) |
334 | 1.17k | ThrowPCXException(CorruptImageError,"UnexpectedEndOfFile"); |
335 | | /* |
336 | | Read PCX raster colormap. |
337 | | */ |
338 | 1.17k | if ((pcx_info.right < pcx_info.left) || (pcx_info.bottom < pcx_info.top) || |
339 | 1.16k | ((pcx_info.bits_per_pixel != 1) && (pcx_info.bits_per_pixel != 2) && |
340 | 518 | (pcx_info.bits_per_pixel != 4) && (pcx_info.bits_per_pixel != 8))) |
341 | 1.14k | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
342 | 1.14k | image->columns=(size_t) (pcx_info.right-pcx_info.left)+1UL; |
343 | 1.14k | image->rows=(size_t) (pcx_info.bottom-pcx_info.top)+1UL; |
344 | 1.14k | image->depth=pcx_info.bits_per_pixel; |
345 | 1.14k | image->units=PixelsPerInchResolution; |
346 | 1.14k | image->resolution.x=(double) pcx_info.horizontal_resolution; |
347 | 1.14k | image->resolution.y=(double) pcx_info.vertical_resolution; |
348 | 1.14k | image->colors=16; |
349 | 1.14k | if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0)) |
350 | 0 | if (image->scene >= (image_info->scene+image_info->number_scenes-1)) |
351 | 0 | break; |
352 | 1.14k | if ((MagickSizeType) (image->columns*image->rows/255) > GetBlobSize(image)) |
353 | 1.09k | ThrowPCXException(CorruptImageError,"InsufficientImageDataInFile"); |
354 | 1.09k | status=SetImageExtent(image,image->columns,image->rows,exception); |
355 | 1.09k | if (status == MagickFalse) |
356 | 1.04k | ThrowPCXException(exception->severity,exception->reason); |
357 | 1.04k | (void) SetImageBackgroundColor(image,exception); |
358 | 1.04k | (void) memset(pcx_colormap,0,sizeof(pcx_colormap)); |
359 | 1.04k | count=ReadBlob(image,3*image->colors,pcx_colormap); |
360 | 1.04k | if (count != (ssize_t) (3*image->colors)) |
361 | 929 | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
362 | 929 | pcx_info.reserved=(unsigned char) ReadBlobByte(image); |
363 | 929 | pcx_info.planes=(unsigned char) ReadBlobByte(image); |
364 | 929 | if (pcx_info.planes == 0) |
365 | 922 | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
366 | 922 | if (pcx_info.planes > 6) |
367 | 895 | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
368 | 895 | if ((pcx_info.bits_per_pixel*pcx_info.planes) >= 64) |
369 | 895 | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
370 | 895 | one=1; |
371 | 895 | if ((pcx_info.bits_per_pixel != 8) || (pcx_info.planes == 1)) |
372 | 741 | if ((pcx_info.version == 3) || (pcx_info.version == 5) || |
373 | 465 | ((pcx_info.bits_per_pixel*pcx_info.planes) == 1)) |
374 | 416 | image->colors=(size_t) MagickMin(one << (1UL* |
375 | 895 | (pcx_info.bits_per_pixel*pcx_info.planes)),256UL); |
376 | 895 | if (AcquireImageColormap(image,image->colors,exception) == MagickFalse) |
377 | 895 | ThrowPCXException(ResourceLimitError,"MemoryAllocationFailed"); |
378 | 895 | if ((pcx_info.bits_per_pixel >= 8) && (pcx_info.planes != 1)) |
379 | 154 | image->storage_class=DirectClass; |
380 | 895 | p=pcx_colormap; |
381 | 35.6k | for (i=0; i < (ssize_t) image->colors; i++) |
382 | 34.7k | { |
383 | 34.7k | image->colormap[i].red=ScaleCharToQuantum(*p++); |
384 | 34.7k | image->colormap[i].green=ScaleCharToQuantum(*p++); |
385 | 34.7k | image->colormap[i].blue=ScaleCharToQuantum(*p++); |
386 | 34.7k | } |
387 | 895 | pcx_info.bytes_per_line=ReadBlobLSBShort(image); |
388 | 895 | pcx_info.palette_info=ReadBlobLSBShort(image); |
389 | 895 | pcx_info.horizontal_screensize=ReadBlobLSBShort(image); |
390 | 895 | pcx_info.vertical_screensize=ReadBlobLSBShort(image); |
391 | 49.2k | for (i=0; i < 54; i++) |
392 | 48.3k | (void) ReadBlobByte(image); |
393 | | /* |
394 | | Read image data. |
395 | | */ |
396 | 895 | if (HeapOverflowSanityCheckGetSize(image->rows,(size_t) pcx_info.bytes_per_line,&pcx_packets) != MagickFalse) |
397 | 866 | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
398 | 866 | if (HeapOverflowSanityCheckGetSize(pcx_packets,(size_t) pcx_info.planes,&pcx_packets) != MagickFalse) |
399 | 866 | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
400 | 866 | if ((size_t) (pcx_info.bits_per_pixel*pcx_info.planes*image->columns) > (pcx_packets*8U)) |
401 | 844 | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
402 | 844 | if ((MagickSizeType) (pcx_packets/32+128) > GetBlobSize(image)) |
403 | 772 | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
404 | 772 | { |
405 | 772 | size_t bytes_per_line = image->columns*pcx_info.bits_per_pixel; |
406 | 772 | if (bytes_per_line != 0) |
407 | 772 | bytes_per_line+=7U; |
408 | 772 | if (bytes_per_line != 0) |
409 | 772 | bytes_per_line/=8U; |
410 | 772 | if ((bytes_per_line == 0) || (pcx_info.bytes_per_line < bytes_per_line)) |
411 | 749 | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
412 | 749 | } |
413 | 749 | scanline=(unsigned char *) AcquireQuantumMemory(MagickMax(image->columns, |
414 | 749 | pcx_info.bytes_per_line),MagickMax(pcx_info.planes,8)*sizeof(*scanline)); |
415 | 749 | pixel_info=AcquireVirtualMemory(pcx_packets,2*sizeof(*pixels)); |
416 | 749 | if ((scanline == (unsigned char *) NULL) || |
417 | 749 | (pixel_info == (MemoryInfo *) NULL)) |
418 | 0 | { |
419 | 0 | if (scanline != (unsigned char *) NULL) |
420 | 0 | scanline=(unsigned char *) RelinquishMagickMemory(scanline); |
421 | 0 | if (pixel_info != (MemoryInfo *) NULL) |
422 | 0 | pixel_info=RelinquishVirtualMemory(pixel_info); |
423 | 0 | ThrowPCXException(ResourceLimitError,"MemoryAllocationFailed"); |
424 | 0 | } |
425 | 749 | (void) memset(scanline,0,(size_t) MagickMax(image->columns, |
426 | 749 | pcx_info.bytes_per_line)*MagickMax(pcx_info.planes,8)*sizeof(*scanline)); |
427 | 749 | pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info); |
428 | 749 | (void) memset(pixels,0,(size_t) pcx_packets*(2*sizeof(*pixels))); |
429 | | /* |
430 | | Uncompress image data. |
431 | | */ |
432 | 749 | p=pixels; |
433 | 749 | if (pcx_info.encoding == 0) |
434 | 22.3k | while (pcx_packets != 0) |
435 | 22.3k | { |
436 | 22.3k | packet=(unsigned char) ReadBlobByte(image); |
437 | 22.3k | if (EOFBlob(image) != MagickFalse) |
438 | 22.3k | ThrowPCXException(CorruptImageError,"UnexpectedEndOfFile"); |
439 | 22.3k | *p++=packet; |
440 | 22.3k | pcx_packets--; |
441 | 22.3k | } |
442 | 680 | else |
443 | 166k | while (pcx_packets != 0) |
444 | 165k | { |
445 | 165k | packet=(unsigned char) ReadBlobByte(image); |
446 | 165k | if (EOFBlob(image) != MagickFalse) |
447 | 165k | ThrowPCXException(CorruptImageError,"UnexpectedEndOfFile"); |
448 | 165k | if ((packet & 0xc0) != 0xc0) |
449 | 68.9k | { |
450 | 68.9k | *p++=packet; |
451 | 68.9k | pcx_packets--; |
452 | 68.9k | continue; |
453 | 68.9k | } |
454 | 96.6k | count=(ssize_t) (packet & 0x3f); |
455 | 96.6k | packet=(unsigned char) ReadBlobByte(image); |
456 | 96.6k | if (EOFBlob(image) != MagickFalse) |
457 | 96.6k | ThrowPCXException(CorruptImageError,"UnexpectedEndOfFile"); |
458 | 5.75M | for ( ; count != 0; count--) |
459 | 5.65M | { |
460 | 5.65M | *p++=packet; |
461 | 5.65M | pcx_packets--; |
462 | 5.65M | if (pcx_packets == 0) |
463 | 524 | break; |
464 | 5.65M | } |
465 | 96.6k | } |
466 | 606 | if (image->storage_class == DirectClass) |
467 | 120 | image->alpha_trait=pcx_info.planes > 3 ? BlendPixelTrait : |
468 | 120 | UndefinedPixelTrait; |
469 | | /* |
470 | | Convert PCX raster image to pixel packets. |
471 | | */ |
472 | 54.2k | for (y=0; y < (ssize_t) image->rows; y++) |
473 | 53.6k | { |
474 | 53.6k | p=pixels+(y*pcx_info.bytes_per_line*pcx_info.planes); |
475 | 53.6k | q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
476 | 53.6k | if (q == (Quantum *) NULL) |
477 | 0 | break; |
478 | 53.6k | r=scanline; |
479 | 53.6k | if (image->storage_class == DirectClass) |
480 | 26.4k | for (i=0; i < pcx_info.planes; i++) |
481 | 18.6k | { |
482 | 18.6k | r=scanline+i; |
483 | 1.22M | for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++) |
484 | 1.20M | { |
485 | 1.20M | switch (i) |
486 | 1.20M | { |
487 | 600k | case 0: |
488 | 600k | { |
489 | 600k | *r=(*p++); |
490 | 600k | break; |
491 | 0 | } |
492 | 600k | case 1: |
493 | 600k | { |
494 | 600k | *r=(*p++); |
495 | 600k | break; |
496 | 0 | } |
497 | 3.90k | case 2: |
498 | 3.90k | { |
499 | 3.90k | *r=(*p++); |
500 | 3.90k | break; |
501 | 0 | } |
502 | 1.92k | case 3: |
503 | 2.96k | default: |
504 | 2.96k | { |
505 | 2.96k | *r=(*p++); |
506 | 2.96k | break; |
507 | 1.92k | } |
508 | 1.20M | } |
509 | 1.20M | r+=(ptrdiff_t) pcx_info.planes; |
510 | 1.20M | } |
511 | 18.6k | } |
512 | 45.8k | else |
513 | 45.8k | if (pcx_info.planes > 1) |
514 | 5.58k | { |
515 | 2.00M | for (x=0; x < (ssize_t) image->columns; x++) |
516 | 1.99M | *r++=0; |
517 | 23.3k | for (i=0; i < pcx_info.planes; i++) |
518 | 17.8k | { |
519 | 17.8k | r=scanline; |
520 | 2.75M | for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++) |
521 | 2.73M | { |
522 | 2.73M | bits=(*p++); |
523 | 24.5M | for (mask=0x80; mask != 0; mask>>=1) |
524 | 21.8M | { |
525 | 21.8M | if (bits & mask) |
526 | 18.5M | *r|=1 << i; |
527 | 21.8M | r++; |
528 | 21.8M | } |
529 | 2.73M | } |
530 | 17.8k | } |
531 | 5.58k | } |
532 | 40.2k | else |
533 | 40.2k | switch (pcx_info.bits_per_pixel) |
534 | 40.2k | { |
535 | 8.82k | case 1: |
536 | 8.82k | { |
537 | 8.82k | ssize_t |
538 | 8.82k | bit; |
539 | | |
540 | 604k | for (x=0; x < ((ssize_t) image->columns-7); x+=8) |
541 | 595k | { |
542 | 5.35M | for (bit=7; bit >= 0; bit--) |
543 | 4.76M | *r++=(unsigned char) ((*p) & (0x01 << bit) ? 0x00 : 0x01); |
544 | 595k | p++; |
545 | 595k | } |
546 | 8.82k | if ((image->columns % 8) != 0) |
547 | 4.08k | { |
548 | 24.8k | for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--) |
549 | 20.7k | *r++=(unsigned char) ((*p) & (0x01 << bit) ? 0x00 : 0x01); |
550 | 4.08k | p++; |
551 | 4.08k | } |
552 | 8.82k | break; |
553 | 0 | } |
554 | 7.09k | case 2: |
555 | 7.09k | { |
556 | 51.1k | for (x=0; x < ((ssize_t) image->columns-3); x+=4) |
557 | 44.0k | { |
558 | 44.0k | *r++=(*p >> 6) & 0x3; |
559 | 44.0k | *r++=(*p >> 4) & 0x3; |
560 | 44.0k | *r++=(*p >> 2) & 0x3; |
561 | 44.0k | *r++=(*p) & 0x3; |
562 | 44.0k | p++; |
563 | 44.0k | } |
564 | 7.09k | if ((image->columns % 4) != 0) |
565 | 5.89k | { |
566 | 17.1k | for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--) |
567 | 11.2k | *r++=(unsigned char) ((*p >> (i*2)) & 0x03); |
568 | 5.89k | p++; |
569 | 5.89k | } |
570 | 7.09k | break; |
571 | 0 | } |
572 | 18.7k | case 4: |
573 | 18.7k | { |
574 | 39.3k | for (x=0; x < ((ssize_t) image->columns-1); x+=2) |
575 | 20.6k | { |
576 | 20.6k | *r++=(*p >> 4) & 0xf; |
577 | 20.6k | *r++=(*p) & 0xf; |
578 | 20.6k | p++; |
579 | 20.6k | } |
580 | 18.7k | if ((image->columns % 2) != 0) |
581 | 1.57k | *r++=(*p++ >> 4) & 0xf; |
582 | 18.7k | break; |
583 | 0 | } |
584 | 5.64k | case 8: |
585 | 5.64k | { |
586 | 5.64k | (void) memcpy(r,p,image->columns); |
587 | 5.64k | break; |
588 | 0 | } |
589 | 0 | default: |
590 | 0 | break; |
591 | 40.2k | } |
592 | | /* |
593 | | Transfer image scanline. |
594 | | */ |
595 | 53.6k | r=scanline; |
596 | 7.44M | for (x=0; x < (ssize_t) image->columns; x++) |
597 | 7.39M | { |
598 | 7.39M | if (image->storage_class == PseudoClass) |
599 | 7.02M | SetPixelIndex(image,*r++,q); |
600 | 374k | else |
601 | 374k | { |
602 | 374k | SetPixelRed(image,ScaleCharToQuantum(*r++),q); |
603 | 374k | SetPixelGreen(image,ScaleCharToQuantum(*r++),q); |
604 | 374k | SetPixelBlue(image,ScaleCharToQuantum(*r++),q); |
605 | 374k | if (image->alpha_trait != UndefinedPixelTrait) |
606 | 1.26k | SetPixelAlpha(image,ScaleCharToQuantum(*r++),q); |
607 | 374k | } |
608 | 7.39M | q+=(ptrdiff_t) GetPixelChannels(image); |
609 | 7.39M | } |
610 | 53.6k | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
611 | 0 | break; |
612 | 53.6k | if (image->previous == (Image *) NULL) |
613 | 53.6k | { |
614 | 53.6k | status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, |
615 | 53.6k | image->rows); |
616 | 53.6k | if (status == MagickFalse) |
617 | 0 | break; |
618 | 53.6k | } |
619 | 53.6k | } |
620 | 606 | if (image->storage_class == PseudoClass) |
621 | 486 | { |
622 | 486 | if ((pcx_info.version == 5) || |
623 | 378 | ((pcx_info.bits_per_pixel*pcx_info.planes) == 1)) |
624 | 214 | { |
625 | | /* |
626 | | Initialize image colormap. |
627 | | */ |
628 | 214 | if (image->colors > 256) |
629 | 214 | ThrowPCXException(CorruptImageError,"ColormapExceeds256Colors"); |
630 | 214 | if ((pcx_info.bits_per_pixel*pcx_info.planes) == 1) |
631 | 124 | { |
632 | | /* |
633 | | Monochrome colormap. |
634 | | */ |
635 | 124 | image->colormap[0].red=(Quantum) 0; |
636 | 124 | image->colormap[0].green=(Quantum) 0; |
637 | 124 | image->colormap[0].blue=(Quantum) 0; |
638 | 124 | image->colormap[1].red=QuantumRange; |
639 | 124 | image->colormap[1].green=QuantumRange; |
640 | 124 | image->colormap[1].blue=QuantumRange; |
641 | 124 | } |
642 | 90 | else |
643 | 90 | if (image->colors > 16) |
644 | 45 | { |
645 | | /* |
646 | | 256 color images have their color map at the end of the file. |
647 | | */ |
648 | 45 | pcx_info.colormap_signature=(unsigned char) ReadBlobByte(image); |
649 | 45 | count=ReadBlob(image,3*image->colors,pcx_colormap); |
650 | 45 | p=pcx_colormap; |
651 | 8.20k | for (i=0; i < (ssize_t) image->colors; i++) |
652 | 8.16k | { |
653 | 8.16k | image->colormap[i].red=ScaleCharToQuantum(*p++); |
654 | 8.16k | image->colormap[i].green=ScaleCharToQuantum(*p++); |
655 | 8.16k | image->colormap[i].blue=ScaleCharToQuantum(*p++); |
656 | 8.16k | } |
657 | 45 | } |
658 | 214 | } |
659 | 486 | (void) SyncImage(image,exception); |
660 | 486 | } |
661 | 606 | scanline=(unsigned char *) RelinquishMagickMemory(scanline); |
662 | 606 | pixel_info=RelinquishVirtualMemory(pixel_info); |
663 | 606 | if (EOFBlob(image) != MagickFalse) |
664 | 34 | { |
665 | 34 | ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile", |
666 | 34 | image->filename); |
667 | 34 | break; |
668 | 34 | } |
669 | | /* |
670 | | Proceed to next image. |
671 | | */ |
672 | 572 | if (image_info->number_scenes != 0) |
673 | 2 | if (image->scene >= (image_info->scene+image_info->number_scenes-1)) |
674 | 2 | break; |
675 | 570 | if (page_table == (MagickOffsetType *) NULL) |
676 | 570 | break; |
677 | 0 | if (page_table[id] == 0) |
678 | 0 | break; |
679 | 0 | offset=SeekBlob(image,(MagickOffsetType) page_table[id],SEEK_SET); |
680 | 0 | if (offset < 0) |
681 | 0 | ThrowPCXException(CorruptImageError,"ImproperImageHeader"); |
682 | 0 | count=ReadBlob(image,1,&pcx_info.identifier); |
683 | 0 | if ((count != 0) && (pcx_info.identifier == 0x0a)) |
684 | 0 | { |
685 | | /* |
686 | | Allocate next image structure. |
687 | | */ |
688 | 0 | AcquireNextImage(image_info,image,exception); |
689 | 0 | if (GetNextImageInList(image) == (Image *) NULL) |
690 | 0 | { |
691 | 0 | status=MagickFalse; |
692 | 0 | break; |
693 | 0 | } |
694 | 0 | image=SyncNextImageInList(image); |
695 | 0 | status=SetImageProgress(image,LoadImagesTag,TellBlob(image), |
696 | 0 | GetBlobSize(image)); |
697 | 0 | if (status == MagickFalse) |
698 | 0 | break; |
699 | 0 | } |
700 | 0 | } |
701 | 606 | if (page_table != (MagickOffsetType *) NULL) |
702 | 0 | page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table); |
703 | 606 | if (CloseBlob(image) == MagickFalse) |
704 | 0 | status=MagickFalse; |
705 | 606 | if (status == MagickFalse) |
706 | 0 | return(DestroyImageList(image)); |
707 | 606 | return(GetFirstImageInList(image)); |
708 | 606 | } |
709 | | |
710 | | /* |
711 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
712 | | % % |
713 | | % % |
714 | | % % |
715 | | % R e g i s t e r P C X I m a g e % |
716 | | % % |
717 | | % % |
718 | | % % |
719 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
720 | | % |
721 | | % RegisterPCXImage() adds attributes for the PCX image format to |
722 | | % the list of supported formats. The attributes include the image format |
723 | | % tag, a method to read and/or write the format, whether the format |
724 | | % supports the saving of more than one frame to the same file or blob, |
725 | | % whether the format supports native in-memory I/O, and a brief |
726 | | % description of the format. |
727 | | % |
728 | | % The format of the RegisterPCXImage method is: |
729 | | % |
730 | | % size_t RegisterPCXImage(void) |
731 | | % |
732 | | */ |
733 | | ModuleExport size_t RegisterPCXImage(void) |
734 | 10 | { |
735 | 10 | MagickInfo |
736 | 10 | *entry; |
737 | | |
738 | 10 | entry=AcquireMagickInfo("PCX","DCX","ZSoft IBM PC multi-page Paintbrush"); |
739 | 10 | entry->decoder=(DecodeImageHandler *) ReadPCXImage; |
740 | 10 | entry->encoder=(EncodeImageHandler *) WritePCXImage; |
741 | 10 | entry->flags|=CoderDecoderSeekableStreamFlag; |
742 | 10 | entry->flags|=CoderEncoderSeekableStreamFlag; |
743 | 10 | entry->magick=(IsImageFormatHandler *) IsDCX; |
744 | 10 | (void) RegisterMagickInfo(entry); |
745 | 10 | entry=AcquireMagickInfo("PCX","PCX","ZSoft IBM PC Paintbrush"); |
746 | 10 | entry->decoder=(DecodeImageHandler *) ReadPCXImage; |
747 | 10 | entry->encoder=(EncodeImageHandler *) WritePCXImage; |
748 | 10 | entry->magick=(IsImageFormatHandler *) IsPCX; |
749 | 10 | entry->flags^=CoderAdjoinFlag; |
750 | 10 | entry->flags|=CoderDecoderSeekableStreamFlag; |
751 | 10 | entry->flags|=CoderEncoderSeekableStreamFlag; |
752 | 10 | (void) RegisterMagickInfo(entry); |
753 | 10 | return(MagickImageCoderSignature); |
754 | 10 | } |
755 | | |
756 | | /* |
757 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
758 | | % % |
759 | | % % |
760 | | % % |
761 | | % U n r e g i s t e r P C X I m a g e % |
762 | | % % |
763 | | % % |
764 | | % % |
765 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
766 | | % |
767 | | % UnregisterPCXImage() removes format registrations made by the |
768 | | % PCX module from the list of supported formats. |
769 | | % |
770 | | % The format of the UnregisterPCXImage method is: |
771 | | % |
772 | | % UnregisterPCXImage(void) |
773 | | % |
774 | | */ |
775 | | ModuleExport void UnregisterPCXImage(void) |
776 | 0 | { |
777 | 0 | (void) UnregisterMagickInfo("DCX"); |
778 | 0 | (void) UnregisterMagickInfo("PCX"); |
779 | 0 | } |
780 | | |
781 | | /* |
782 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
783 | | % % |
784 | | % % |
785 | | % % |
786 | | % W r i t e P C X I m a g e % |
787 | | % % |
788 | | % % |
789 | | % % |
790 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
791 | | % |
792 | | % WritePCXImage() writes an image in the ZSoft IBM PC Paintbrush file |
793 | | % format. |
794 | | % |
795 | | % The format of the WritePCXImage method is: |
796 | | % |
797 | | % MagickBooleanType WritePCXImage(const ImageInfo *image_info, |
798 | | % Image *image,ExceptionInfo *exception) |
799 | | % |
800 | | % A description of each parameter follows. |
801 | | % |
802 | | % o image_info: the image info. |
803 | | % |
804 | | % o image: The image. |
805 | | % |
806 | | % o exception: return any errors or warnings in this structure. |
807 | | % |
808 | | */ |
809 | | |
810 | | static MagickBooleanType PCXWritePixels(PCXInfo *pcx_info, |
811 | | const unsigned char *pixels,Image *image) |
812 | 50.7k | { |
813 | 50.7k | const unsigned char |
814 | 50.7k | *q; |
815 | | |
816 | 50.7k | ssize_t |
817 | 50.7k | count, |
818 | 50.7k | i, |
819 | 50.7k | x; |
820 | | |
821 | 50.7k | unsigned char |
822 | 50.7k | packet, |
823 | 50.7k | previous; |
824 | | |
825 | 50.7k | q=pixels; |
826 | 198k | for (i=0; i < (ssize_t) pcx_info->planes; i++) |
827 | 147k | { |
828 | 147k | if (pcx_info->encoding == 0) |
829 | 0 | { |
830 | 0 | for (x=0; x < (ssize_t) pcx_info->bytes_per_line; x++) |
831 | 0 | (void) WriteBlobByte(image,(unsigned char) (*q++)); |
832 | 0 | } |
833 | 147k | else |
834 | 147k | { |
835 | 147k | previous=(*q++); |
836 | 147k | count=1; |
837 | 3.68M | for (x=0; x < (ssize_t) (pcx_info->bytes_per_line-1); x++) |
838 | 3.54M | { |
839 | 3.54M | packet=(*q++); |
840 | 3.54M | if ((packet == previous) && (count < 63)) |
841 | 2.30M | { |
842 | 2.30M | count++; |
843 | 2.30M | continue; |
844 | 2.30M | } |
845 | 1.23M | if ((count > 1) || ((previous & 0xc0) == 0xc0)) |
846 | 726k | { |
847 | 726k | count|=0xc0; |
848 | 726k | (void) WriteBlobByte(image,(unsigned char) count); |
849 | 726k | } |
850 | 1.23M | (void) WriteBlobByte(image,previous); |
851 | 1.23M | previous=packet; |
852 | 1.23M | count=1; |
853 | 1.23M | } |
854 | 147k | if ((count > 1) || ((previous & 0xc0) == 0xc0)) |
855 | 114k | { |
856 | 114k | count|=0xc0; |
857 | 114k | (void) WriteBlobByte(image,(unsigned char) count); |
858 | 114k | } |
859 | 147k | (void) WriteBlobByte(image,previous); |
860 | 147k | } |
861 | 147k | } |
862 | 50.7k | return (MagickTrue); |
863 | 50.7k | } |
864 | | |
865 | | static MagickBooleanType WritePCXImage(const ImageInfo *image_info,Image *image, |
866 | | ExceptionInfo *exception) |
867 | 482 | { |
868 | 482 | const Quantum |
869 | 482 | *p; |
870 | | |
871 | 482 | MagickBooleanType |
872 | 482 | status; |
873 | | |
874 | 482 | MagickOffsetType |
875 | 482 | offset, |
876 | 482 | *page_table, |
877 | 482 | scene; |
878 | | |
879 | 482 | MemoryInfo |
880 | 482 | *pixel_info; |
881 | | |
882 | 482 | PCXInfo |
883 | 482 | pcx_info; |
884 | | |
885 | 482 | size_t |
886 | 482 | number_scenes, |
887 | 482 | length; |
888 | | |
889 | 482 | ssize_t |
890 | 482 | i, |
891 | 482 | x, |
892 | 482 | y; |
893 | | |
894 | 482 | unsigned char |
895 | 482 | *pcx_colormap, |
896 | 482 | *pixels, |
897 | 482 | *q; |
898 | | |
899 | | /* |
900 | | Open output image file. |
901 | | */ |
902 | 482 | assert(image_info != (const ImageInfo *) NULL); |
903 | 482 | assert(image_info->signature == MagickCoreSignature); |
904 | 482 | assert(image != (Image *) NULL); |
905 | 482 | assert(image->signature == MagickCoreSignature); |
906 | 482 | assert(exception != (ExceptionInfo *) NULL); |
907 | 482 | assert(exception->signature == MagickCoreSignature); |
908 | 482 | if (IsEventLogging() != MagickFalse) |
909 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
910 | 482 | status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); |
911 | 482 | if (status == MagickFalse) |
912 | 0 | return(status); |
913 | 482 | number_scenes=GetImageListLength(image); |
914 | 482 | if (number_scenes > MaxNumberScenes) |
915 | 482 | ThrowWriterException(ResourceLimitError,"ListLengthExceedsLimit"); |
916 | 482 | page_table=(MagickOffsetType *) NULL; |
917 | 482 | if ((LocaleCompare(image_info->magick,"DCX") == 0) || |
918 | 482 | ((GetNextImageInList(image) != (Image *) NULL) && |
919 | 0 | (image_info->adjoin != MagickFalse))) |
920 | 0 | { |
921 | | /* |
922 | | Write the DCX page table. |
923 | | */ |
924 | 0 | (void) WriteBlobLSBLong(image,0x3ADE68B1L); |
925 | 0 | page_table=(MagickOffsetType *) AcquireQuantumMemory(MaxNumberScenes+1, |
926 | 0 | sizeof(*page_table)); |
927 | 0 | if (page_table == (MagickOffsetType *) NULL) |
928 | 0 | ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); |
929 | 0 | for (scene=0; scene < MaxNumberScenes; scene++) |
930 | 0 | (void) WriteBlobLSBLong(image,0x00000000L); |
931 | 0 | } |
932 | 482 | scene=0; |
933 | 482 | do |
934 | 482 | { |
935 | 482 | if (page_table != (MagickOffsetType *) NULL) |
936 | 0 | page_table[scene]=TellBlob(image); |
937 | | /* |
938 | | Initialize PCX raster file header. |
939 | | */ |
940 | 482 | pcx_info.identifier=0x0a; |
941 | 482 | pcx_info.version=5; |
942 | 482 | pcx_info.encoding=image_info->compression == NoCompression ? 0 : 1; |
943 | 482 | pcx_info.bits_per_pixel=8; |
944 | 482 | if ((image->storage_class == PseudoClass) && |
945 | 370 | (SetImageMonochrome(image,exception) != MagickFalse || image->colors <= 16)) |
946 | 311 | pcx_info.bits_per_pixel=1; |
947 | 171 | else if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
948 | 0 | (void) TransformImageColorspace(image,sRGBColorspace,exception); |
949 | 482 | pcx_info.left=0; |
950 | 482 | pcx_info.top=0; |
951 | 482 | pcx_info.right=(unsigned short) (image->columns-1); |
952 | 482 | pcx_info.bottom=(unsigned short) (image->rows-1); |
953 | 482 | switch (image->units) |
954 | 482 | { |
955 | 0 | case UndefinedResolution: |
956 | 482 | case PixelsPerInchResolution: |
957 | 482 | default: |
958 | 482 | { |
959 | 482 | pcx_info.horizontal_resolution=CastDoubleToUShort(image->resolution.x); |
960 | 482 | pcx_info.vertical_resolution=CastDoubleToUShort(image->resolution.y); |
961 | 482 | break; |
962 | 482 | } |
963 | 0 | case PixelsPerCentimeterResolution: |
964 | 0 | { |
965 | 0 | pcx_info.horizontal_resolution=CastDoubleToUShort(2.54*image->resolution.x+0.5); |
966 | 0 | pcx_info.vertical_resolution=CastDoubleToUShort(2.54*image->resolution.y+0.5); |
967 | 0 | break; |
968 | 482 | } |
969 | 482 | } |
970 | 482 | pcx_info.reserved=0; |
971 | 482 | pcx_info.planes=1; |
972 | 482 | if ((image->storage_class == PseudoClass) && (image->colors <= 16) && |
973 | 300 | (image->type != BilevelType)) |
974 | 170 | pcx_info.planes=4; |
975 | 312 | else if ((image->storage_class == DirectClass) || (image->colors > 256)) |
976 | 112 | { |
977 | 112 | pcx_info.planes=3; |
978 | 112 | if (image->alpha_trait != UndefinedPixelTrait) |
979 | 38 | pcx_info.planes++; |
980 | 112 | } |
981 | 482 | length=(((size_t) image->columns*pcx_info.bits_per_pixel+7)/8); |
982 | 482 | if ((image->columns > 65535UL) || (image->rows > 65535UL) || |
983 | 482 | (length > 65535UL)) |
984 | 0 | { |
985 | 0 | if (page_table != (MagickOffsetType *) NULL) |
986 | 0 | page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table); |
987 | 0 | ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit"); |
988 | 0 | } |
989 | 482 | pcx_info.bytes_per_line=(unsigned short) length; |
990 | 482 | pcx_info.palette_info=1; |
991 | 482 | pcx_info.colormap_signature=0x0c; |
992 | | /* |
993 | | Write PCX header. |
994 | | */ |
995 | 482 | (void) WriteBlobByte(image,pcx_info.identifier); |
996 | 482 | (void) WriteBlobByte(image,pcx_info.version); |
997 | 482 | (void) WriteBlobByte(image,pcx_info.encoding); |
998 | 482 | (void) WriteBlobByte(image,pcx_info.bits_per_pixel); |
999 | 482 | (void) WriteBlobLSBShort(image,pcx_info.left); |
1000 | 482 | (void) WriteBlobLSBShort(image,pcx_info.top); |
1001 | 482 | (void) WriteBlobLSBShort(image,pcx_info.right); |
1002 | 482 | (void) WriteBlobLSBShort(image,pcx_info.bottom); |
1003 | 482 | (void) WriteBlobLSBShort(image,pcx_info.horizontal_resolution); |
1004 | 482 | (void) WriteBlobLSBShort(image,pcx_info.vertical_resolution); |
1005 | | /* |
1006 | | Dump colormap to file. |
1007 | | */ |
1008 | 482 | pcx_colormap=(unsigned char *) AcquireQuantumMemory(256UL, |
1009 | 482 | 3*sizeof(*pcx_colormap)); |
1010 | 482 | if (pcx_colormap == (unsigned char *) NULL) |
1011 | 0 | { |
1012 | 0 | if (page_table != (MagickOffsetType *) NULL) |
1013 | 0 | page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table); |
1014 | 0 | ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); |
1015 | 0 | } |
1016 | 482 | (void) memset(pcx_colormap,0,3*256*sizeof(*pcx_colormap)); |
1017 | 482 | q=pcx_colormap; |
1018 | 482 | if ((image->storage_class == PseudoClass) && (image->colors <= 256)) |
1019 | 15.6k | for (i=0; i < (ssize_t) image->colors; i++) |
1020 | 15.2k | { |
1021 | 15.2k | *q++=ScaleQuantumToChar((Quantum) image->colormap[i].red); |
1022 | 15.2k | *q++=ScaleQuantumToChar((Quantum) image->colormap[i].green); |
1023 | 15.2k | *q++=ScaleQuantumToChar((Quantum) image->colormap[i].blue); |
1024 | 15.2k | } |
1025 | 482 | (void) WriteBlob(image,3*16,(const unsigned char *) pcx_colormap); |
1026 | 482 | (void) WriteBlobByte(image,pcx_info.reserved); |
1027 | 482 | (void) WriteBlobByte(image,pcx_info.planes); |
1028 | 482 | (void) WriteBlobLSBShort(image,pcx_info.bytes_per_line); |
1029 | 482 | (void) WriteBlobLSBShort(image,pcx_info.palette_info); |
1030 | 28.4k | for (i=0; i < 58; i++) |
1031 | 27.9k | (void) WriteBlobByte(image,'\0'); |
1032 | 482 | length=(size_t) pcx_info.bytes_per_line; |
1033 | 482 | pixel_info=AcquireVirtualMemory(length,pcx_info.planes*sizeof(*pixels)); |
1034 | 482 | if (pixel_info == (MemoryInfo *) NULL) |
1035 | 0 | { |
1036 | 0 | pcx_colormap=(unsigned char *) RelinquishMagickMemory(pcx_colormap); |
1037 | 0 | if (page_table != (MagickOffsetType *) NULL) |
1038 | 0 | page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table); |
1039 | 0 | ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); |
1040 | 0 | } |
1041 | 482 | pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info); |
1042 | 482 | q=pixels; |
1043 | 482 | if ((image->storage_class == DirectClass) || (image->colors > 256)) |
1044 | 112 | { |
1045 | | /* |
1046 | | Convert DirectClass image to PCX raster pixels. |
1047 | | */ |
1048 | 7.82k | for (y=0; y < (ssize_t) image->rows; y++) |
1049 | 7.71k | { |
1050 | 7.71k | q=pixels; |
1051 | 31.6k | for (i=0; i < pcx_info.planes; i++) |
1052 | 23.9k | { |
1053 | 23.9k | p=GetVirtualPixels(image,0,y,image->columns,1,exception); |
1054 | 23.9k | if (p == (const Quantum *) NULL) |
1055 | 0 | break; |
1056 | 23.9k | switch ((int) i) |
1057 | 23.9k | { |
1058 | 7.71k | case 0: |
1059 | 7.71k | { |
1060 | 381k | for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++) |
1061 | 373k | { |
1062 | 373k | *q++=ScaleQuantumToChar(GetPixelRed(image,p)); |
1063 | 373k | p+=(ptrdiff_t) GetPixelChannels(image); |
1064 | 373k | } |
1065 | 7.71k | break; |
1066 | 0 | } |
1067 | 7.71k | case 1: |
1068 | 7.71k | { |
1069 | 381k | for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++) |
1070 | 373k | { |
1071 | 373k | *q++=ScaleQuantumToChar(GetPixelGreen(image,p)); |
1072 | 373k | p+=(ptrdiff_t) GetPixelChannels(image); |
1073 | 373k | } |
1074 | 7.71k | break; |
1075 | 0 | } |
1076 | 7.71k | case 2: |
1077 | 7.71k | { |
1078 | 381k | for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++) |
1079 | 373k | { |
1080 | 373k | *q++=ScaleQuantumToChar(GetPixelBlue(image,p)); |
1081 | 373k | p+=(ptrdiff_t) GetPixelChannels(image); |
1082 | 373k | } |
1083 | 7.71k | break; |
1084 | 0 | } |
1085 | 773 | case 3: |
1086 | 773 | default: |
1087 | 773 | { |
1088 | 2.03k | for (x=(ssize_t) pcx_info.bytes_per_line; x != 0; x--) |
1089 | 1.25k | { |
1090 | 1.25k | *q++=ScaleQuantumToChar((Quantum) (GetPixelAlpha(image,p))); |
1091 | 1.25k | p+=(ptrdiff_t) GetPixelChannels(image); |
1092 | 1.25k | } |
1093 | 773 | break; |
1094 | 773 | } |
1095 | 23.9k | } |
1096 | 23.9k | } |
1097 | 7.71k | if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse) |
1098 | 0 | break; |
1099 | 7.71k | if (image->previous == (Image *) NULL) |
1100 | 7.71k | { |
1101 | 7.71k | status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, |
1102 | 7.71k | image->rows); |
1103 | 7.71k | if (status == MagickFalse) |
1104 | 0 | break; |
1105 | 7.71k | } |
1106 | 7.71k | } |
1107 | 112 | } |
1108 | 370 | else |
1109 | 370 | { |
1110 | 370 | if (pcx_info.bits_per_pixel > 1) |
1111 | 6.94k | for (y=0; y < (ssize_t) image->rows; y++) |
1112 | 6.88k | { |
1113 | 6.88k | p=GetVirtualPixels(image,0,y,image->columns,1,exception); |
1114 | 6.88k | if (p == (const Quantum *) NULL) |
1115 | 0 | break; |
1116 | 6.88k | q=pixels; |
1117 | 1.57M | for (x=0; x < (ssize_t) image->columns; x++) |
1118 | 1.56M | { |
1119 | 1.56M | *q++=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); |
1120 | 1.56M | p+=(ptrdiff_t) GetPixelChannels(image); |
1121 | 1.56M | } |
1122 | 6.88k | if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse) |
1123 | 0 | break; |
1124 | 6.88k | if (image->previous == (Image *) NULL) |
1125 | 6.88k | { |
1126 | 6.88k | status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) |
1127 | 6.88k | y,image->rows); |
1128 | 6.88k | if (status == MagickFalse) |
1129 | 0 | break; |
1130 | 6.88k | } |
1131 | 6.88k | } |
1132 | 311 | else if (pcx_info.planes == 4) |
1133 | 170 | { |
1134 | 170 | const Quantum |
1135 | 170 | *r; |
1136 | | |
1137 | 170 | unsigned char |
1138 | 170 | bit, |
1139 | 170 | byte; |
1140 | | |
1141 | 27.1k | for (y=0; y < (ssize_t) image->rows; y++) |
1142 | 26.9k | { |
1143 | 26.9k | p=GetVirtualPixels(image,0,y,image->columns,1,exception); |
1144 | 26.9k | if (p == (const Quantum *) NULL) |
1145 | 0 | break; |
1146 | 134k | for (i=0; i < (ssize_t) pcx_info.planes; i++) |
1147 | 107k | { |
1148 | 107k | r=p; |
1149 | 107k | byte=0; |
1150 | 107k | bit=0; |
1151 | 107k | q=pixels+(i*(ssize_t) pcx_info.bytes_per_line); |
1152 | 2.71M | for (x=0; x < (ssize_t) image->columns; x++) |
1153 | 2.60M | { |
1154 | 2.60M | bit<<=1; |
1155 | 2.60M | if (((ssize_t) GetPixelIndex(image,r) & ((ssize_t) 1 << i)) != 0) |
1156 | 1.26M | bit|=0x01; |
1157 | 2.60M | byte++; |
1158 | 2.60M | if (byte == 8) |
1159 | 293k | { |
1160 | 293k | *q++=bit; |
1161 | 293k | byte=0; |
1162 | 293k | bit=0; |
1163 | 293k | } |
1164 | 2.60M | r+=(ptrdiff_t) GetPixelChannels(image); |
1165 | 2.60M | } |
1166 | 107k | if (byte != 0) |
1167 | 105k | *q++=bit << (8-byte); |
1168 | 107k | } |
1169 | 26.9k | if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse) |
1170 | 0 | break; |
1171 | 26.9k | if (image->previous == (Image *) NULL) |
1172 | 26.9k | { |
1173 | 26.9k | status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, |
1174 | 26.9k | image->rows); |
1175 | 26.9k | if (status == MagickFalse) |
1176 | 0 | break; |
1177 | 26.9k | } |
1178 | 26.9k | } |
1179 | 170 | } |
1180 | 141 | else |
1181 | 141 | { |
1182 | 141 | unsigned char |
1183 | 141 | bit, |
1184 | 141 | byte; |
1185 | | |
1186 | | /* |
1187 | | Convert PseudoClass image to a PCX monochrome image. |
1188 | | */ |
1189 | 9.34k | for (y=0; y < (ssize_t) image->rows; y++) |
1190 | 9.20k | { |
1191 | 9.20k | p=GetVirtualPixels(image,0,y,image->columns,1,exception); |
1192 | 9.20k | if (p == (const Quantum *) NULL) |
1193 | 0 | break; |
1194 | 9.20k | bit=0; |
1195 | 9.20k | byte=0; |
1196 | 9.20k | q=pixels; |
1197 | 4.79M | for (x=0; x < (ssize_t) image->columns; x++) |
1198 | 4.78M | { |
1199 | 4.78M | byte<<=1; |
1200 | 4.78M | if (GetPixelLuma(image,p) < ((double) QuantumRange/2.0)) |
1201 | 4.66M | byte|=0x01; |
1202 | 4.78M | bit++; |
1203 | 4.78M | if (bit == 8) |
1204 | 595k | { |
1205 | 595k | *q++=byte; |
1206 | 595k | bit=0; |
1207 | 595k | byte=0; |
1208 | 595k | } |
1209 | 4.78M | p+=(ptrdiff_t) GetPixelChannels(image); |
1210 | 4.78M | } |
1211 | 9.20k | if (bit != 0) |
1212 | 4.61k | *q++=byte << (8-bit); |
1213 | 9.20k | if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse) |
1214 | 0 | break; |
1215 | 9.20k | if (image->previous == (Image *) NULL) |
1216 | 9.20k | { |
1217 | 9.20k | status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) |
1218 | 9.20k | y,image->rows); |
1219 | 9.20k | if (status == MagickFalse) |
1220 | 0 | break; |
1221 | 9.20k | } |
1222 | 9.20k | } |
1223 | 141 | } |
1224 | 370 | (void) WriteBlobByte(image,pcx_info.colormap_signature); |
1225 | 370 | (void) WriteBlob(image,3*256,pcx_colormap); |
1226 | 370 | } |
1227 | 482 | pixel_info=RelinquishVirtualMemory(pixel_info); |
1228 | 482 | pcx_colormap=(unsigned char *) RelinquishMagickMemory(pcx_colormap); |
1229 | 482 | if (page_table == (MagickOffsetType *) NULL) |
1230 | 482 | break; |
1231 | 0 | if (GetNextImageInList(image) == (Image *) NULL) |
1232 | 0 | break; |
1233 | 0 | image=SyncNextImageInList(image); |
1234 | 0 | status=SetImageProgress(image,SaveImagesTag,scene++,number_scenes); |
1235 | 0 | if (status == MagickFalse) |
1236 | 0 | break; |
1237 | 0 | } while (image_info->adjoin != MagickFalse); |
1238 | 482 | if (page_table != (MagickOffsetType *) NULL) |
1239 | 0 | { |
1240 | | /* |
1241 | | Write the DCX page table. |
1242 | | */ |
1243 | 0 | page_table[scene+1]=0; |
1244 | 0 | offset=SeekBlob(image,0L,SEEK_SET); |
1245 | 0 | if (offset < 0) |
1246 | 0 | { |
1247 | 0 | page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table); |
1248 | 0 | ThrowWriterException(CorruptImageError,"ImproperImageHeader"); |
1249 | 0 | } |
1250 | 0 | (void) WriteBlobLSBLong(image,0x3ADE68B1L); |
1251 | 0 | for (i=0; i <= (ssize_t) scene; i++) |
1252 | 0 | (void) WriteBlobLSBLong(image,(unsigned int) page_table[i]); |
1253 | 0 | page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table); |
1254 | 0 | } |
1255 | 482 | if (status == MagickFalse) |
1256 | 0 | { |
1257 | 0 | char |
1258 | 0 | *message; |
1259 | |
|
1260 | 0 | message=GetExceptionMessage(errno); |
1261 | 0 | (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError, |
1262 | 0 | "UnableToWriteFile","`%s': %s",image->filename,message); |
1263 | 0 | message=DestroyString(message); |
1264 | 0 | } |
1265 | 482 | if (CloseBlob(image) == MagickFalse) |
1266 | 0 | status=MagickFalse; |
1267 | 482 | return(status); |
1268 | 482 | } |