blob: 0ff656ac4e3b2f3f29df796484b3fd0a713a41bb [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP CCCC DDDD %
7% P P C D D %
8% PPPP C D D %
9% P C D D %
10% P CCCC DDDD %
11% %
12% %
13% Read/Write Photo CD Image Format %
14% %
15% Software Design %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
Cristy7ce65e72015-12-12 18:03:16 -050020% Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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% http://www.imagemagick.org/script/license.php %
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*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/property.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/client.h"
48#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000049#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000050#include "MagickCore/constitute.h"
51#include "MagickCore/decorate.h"
cristyc53413d2011-11-17 13:04:26 +000052#include "MagickCore/distort.h"
cristy4c08aed2011-07-01 19:47:50 +000053#include "MagickCore/exception.h"
54#include "MagickCore/exception-private.h"
55#include "MagickCore/gem.h"
56#include "MagickCore/geometry.h"
57#include "MagickCore/image.h"
58#include "MagickCore/image-private.h"
59#include "MagickCore/list.h"
60#include "MagickCore/magick.h"
61#include "MagickCore/memory_.h"
62#include "MagickCore/monitor.h"
63#include "MagickCore/monitor-private.h"
64#include "MagickCore/montage.h"
65#include "MagickCore/pixel-accessor.h"
66#include "MagickCore/resize.h"
cristy4c08aed2011-07-01 19:47:50 +000067#include "MagickCore/quantum-private.h"
68#include "MagickCore/static.h"
69#include "MagickCore/string_.h"
70#include "MagickCore/module.h"
71#include "MagickCore/transform.h"
72#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000073
74/*
75 Forward declarations.
76*/
77static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +000078 WritePCDImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +000079
80/*
81%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82% %
83% %
84% %
85% D e c o d e I m a g e %
86% %
87% %
88% %
89%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90%
91% DecodeImage recovers the Huffman encoded luminance and chrominance
92% deltas.
93%
94% The format of the DecodeImage method is:
95%
96% MagickBooleanType DecodeImage(Image *image,unsigned char *luma,
97% unsigned char *chroma1,unsigned char *chroma2)
98%
99% A description of each parameter follows:
100%
101% o image: the address of a structure of type Image.
102%
103% o luma: the address of a character buffer that contains the
104% luminance information.
105%
106% o chroma1: the address of a character buffer that contains the
107% chrominance information.
108%
109% o chroma2: the address of a character buffer that contains the
110% chrominance information.
111%
112*/
113static MagickBooleanType DecodeImage(Image *image,unsigned char *luma,
cristy018f07f2011-09-04 21:15:19 +0000114 unsigned char *chroma1,unsigned char *chroma2,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000115{
cristyc31f22e2011-10-29 01:06:32 +0000116#define IsSync(sum) ((sum & 0xffffff00UL) == 0xfffffe00UL)
cristy3ed852e2009-09-05 21:47:34 +0000117#define PCDGetBits(n) \
118{ \
119 sum=(sum << n) & 0xffffffff; \
120 bits-=n; \
121 while (bits <= 24) \
122 { \
123 if (p >= (buffer+0x800)) \
124 { \
125 count=ReadBlob(image,0x800,buffer); \
126 p=buffer; \
127 } \
128 sum|=((unsigned int) (*p) << (24-bits)); \
129 bits+=8; \
130 p++; \
131 } \
cristy3ed852e2009-09-05 21:47:34 +0000132}
133
134 typedef struct PCDTable
135 {
136 unsigned int
137 length,
138 sequence;
139
140 MagickStatusType
141 mask;
142
143 unsigned char
144 key;
145 } PCDTable;
146
cristy3ed852e2009-09-05 21:47:34 +0000147 PCDTable
148 *pcd_table[3];
149
cristybb503372010-05-27 20:51:26 +0000150 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000151 i,
152 j;
153
154 register PCDTable
155 *r;
156
157 register unsigned char
158 *p,
159 *q;
160
161 size_t
cristy3ed852e2009-09-05 21:47:34 +0000162 bits,
cristyaff6d802011-04-26 01:46:31 +0000163 length,
cristy3ed852e2009-09-05 21:47:34 +0000164 plane,
165 pcd_length[3],
166 row,
167 sum;
168
cristyaff6d802011-04-26 01:46:31 +0000169 ssize_t
170 count,
171 quantum;
172
173 unsigned char
174 *buffer;
175
cristy3ed852e2009-09-05 21:47:34 +0000176 /*
177 Initialize Huffman tables.
178 */
179 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000180 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000181 if (image->debug != MagickFalse)
182 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
183 assert(luma != (unsigned char *) NULL);
184 assert(chroma1 != (unsigned char *) NULL);
185 assert(chroma2 != (unsigned char *) NULL);
186 buffer=(unsigned char *) AcquireQuantumMemory(0x800,sizeof(*buffer));
187 if (buffer == (unsigned char *) NULL)
188 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
189 image->filename);
190 sum=0;
191 bits=32;
192 p=buffer+0x800;
cristy54ebd8d2014-01-14 01:14:23 +0000193 for (i=0; i < 3; i++)
cristye47080a2014-01-14 23:53:40 +0000194 {
cristy54ebd8d2014-01-14 01:14:23 +0000195 pcd_table[i]=(PCDTable *) NULL;
cristyf676ac52014-01-15 00:08:09 +0000196 pcd_length[i]=0;
cristye47080a2014-01-14 23:53:40 +0000197 }
cristy3ed852e2009-09-05 21:47:34 +0000198 for (i=0; i < (image->columns > 1536 ? 3 : 1); i++)
199 {
200 PCDGetBits(8);
201 length=(sum & 0xff)+1;
202 pcd_table[i]=(PCDTable *) AcquireQuantumMemory(length,
203 sizeof(*pcd_table[i]));
204 if (pcd_table[i] == (PCDTable *) NULL)
205 {
206 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
207 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
208 image->filename);
209 }
210 r=pcd_table[i];
cristybb503372010-05-27 20:51:26 +0000211 for (j=0; j < (ssize_t) length; j++)
cristy3ed852e2009-09-05 21:47:34 +0000212 {
213 PCDGetBits(8);
214 r->length=(unsigned int) (sum & 0xff)+1;
215 if (r->length > 16)
216 {
217 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
218 return(MagickFalse);
219 }
220 PCDGetBits(16);
221 r->sequence=(unsigned int) (sum & 0xffff) << 16;
222 PCDGetBits(8);
223 r->key=(unsigned char) (sum & 0xff);
224 r->mask=(~((1U << (32-r->length))-1));
225 r++;
226 }
cristybb503372010-05-27 20:51:26 +0000227 pcd_length[i]=(size_t) length;
cristy3ed852e2009-09-05 21:47:34 +0000228 }
229 /*
230 Search for Sync byte.
231 */
232 for (i=0; i < 1; i++)
233 PCDGetBits(16);
234 for (i=0; i < 1; i++)
235 PCDGetBits(16);
236 while ((sum & 0x00fff000UL) != 0x00fff000UL)
237 PCDGetBits(8);
cristyc31f22e2011-10-29 01:06:32 +0000238 while (IsSync(sum) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000239 PCDGetBits(1);
240 /*
241 Recover the Huffman encoded luminance and chrominance deltas.
242 */
243 count=0;
244 length=0;
245 plane=0;
246 row=0;
247 q=luma;
248 for ( ; ; )
249 {
cristyc31f22e2011-10-29 01:06:32 +0000250 if (IsSync(sum) != 0)
cristy3ed852e2009-09-05 21:47:34 +0000251 {
252 /*
253 Determine plane and row number.
254 */
255 PCDGetBits(16);
256 row=((sum >> 9) & 0x1fff);
257 if (row == image->rows)
258 break;
259 PCDGetBits(8);
260 plane=sum >> 30;
261 PCDGetBits(16);
262 switch (plane)
263 {
264 case 0:
265 {
266 q=luma+row*image->columns;
267 count=(ssize_t) image->columns;
268 break;
269 }
270 case 2:
271 {
272 q=chroma1+(row >> 1)*image->columns;
273 count=(ssize_t) (image->columns >> 1);
274 plane--;
275 break;
276 }
277 case 3:
278 {
279 q=chroma2+(row >> 1)*image->columns;
280 count=(ssize_t) (image->columns >> 1);
281 plane--;
282 break;
283 }
284 default:
285 {
286 ThrowBinaryException(CorruptImageError,"CorruptImage",
287 image->filename);
288 }
289 }
290 length=pcd_length[plane];
291 continue;
292 }
293 /*
294 Decode luminance or chrominance deltas.
295 */
296 r=pcd_table[plane];
cristybb503372010-05-27 20:51:26 +0000297 for (i=0; ((i < (ssize_t) length) && ((sum & r->mask) != r->sequence)); i++)
cristy3ed852e2009-09-05 21:47:34 +0000298 r++;
cristy5d418802009-11-27 19:06:12 +0000299 if ((row > image->rows) || (r == (PCDTable *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000300 {
cristyc82a27b2011-10-21 01:07:16 +0000301 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +0000302 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
303 while ((sum & 0x00fff000) != 0x00fff000)
304 PCDGetBits(8);
cristyc31f22e2011-10-29 01:06:32 +0000305 while (IsSync(sum) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000306 PCDGetBits(1);
307 continue;
308 }
309 if (r->key < 128)
cristybb503372010-05-27 20:51:26 +0000310 quantum=(ssize_t) (*q)+r->key;
cristy3ed852e2009-09-05 21:47:34 +0000311 else
cristybb503372010-05-27 20:51:26 +0000312 quantum=(ssize_t) (*q)+r->key-256;
cristy3ed852e2009-09-05 21:47:34 +0000313 *q=(unsigned char) ((quantum < 0) ? 0 : (quantum > 255) ? 255 : quantum);
314 q++;
315 PCDGetBits(r->length);
316 count--;
317 }
318 /*
319 Relinquish resources.
320 */
321 for (i=0; i < (image->columns > 1536 ? 3 : 1); i++)
322 pcd_table[i]=(PCDTable *) RelinquishMagickMemory(pcd_table[i]);
323 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
324 return(MagickTrue);
325}
326
327/*
328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
329% %
330% %
331% %
332% I s P C D %
333% %
334% %
335% %
336%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
337%
338% IsPCD() returns MagickTrue if the image format type, identified by the
339% magick string, is PCD.
340%
341% The format of the IsPCD method is:
342%
343% MagickBooleanType IsPCD(const unsigned char *magick,const size_t length)
344%
345% A description of each parameter follows:
346%
347% o magick: compare image format pattern against these bytes.
348%
349% o length: Specifies the length of the magick string.
350%
351*/
352static MagickBooleanType IsPCD(const unsigned char *magick,const size_t length)
353{
354 if (length < 2052)
355 return(MagickFalse);
356 if (LocaleNCompare((const char *) magick+2048,"PCD_",4) == 0)
357 return(MagickTrue);
358 return(MagickFalse);
359}
360
361/*
362%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
363% %
364% %
365% %
366% R e a d P C D I m a g e %
367% %
368% %
369% %
370%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
371%
372% ReadPCDImage() reads a Photo CD image file and returns it. It
373% allocates the memory necessary for the new Image structure and returns a
374% pointer to the new image. Much of the PCD decoder was derived from
375% the program hpcdtoppm(1) by Hadmut Danisch.
376%
377% The format of the ReadPCDImage method is:
378%
379% image=ReadPCDImage(image_info)
380%
381% A description of each parameter follows:
382%
383% o image_info: the image info.
384%
385% o exception: return any errors or warnings in this structure.
386%
387*/
cristy3ed852e2009-09-05 21:47:34 +0000388static Image *OverviewImage(const ImageInfo *image_info,Image *image,
389 ExceptionInfo *exception)
390{
391 Image
392 *montage_image;
393
394 MontageInfo
395 *montage_info;
396
397 register Image
398 *p;
399
400 /*
401 Create the PCD Overview image.
402 */
403 for (p=image; p != (Image *) NULL; p=p->next)
404 {
405 (void) DeleteImageProperty(p,"label");
cristyd15e6592011-10-15 00:13:06 +0000406 (void) SetImageProperty(p,"label",DefaultTileLabel,exception);
cristy3ed852e2009-09-05 21:47:34 +0000407 }
408 montage_info=CloneMontageInfo(image_info,(MontageInfo *) NULL);
409 (void) CopyMagickString(montage_info->filename,image_info->filename,
cristy151b66d2015-04-15 10:50:31 +0000410 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000411 montage_image=MontageImageList(image_info,montage_info,image,exception);
412 montage_info=DestroyMontageInfo(montage_info);
413 if (montage_image == (Image *) NULL)
414 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
415 image=DestroyImage(image);
416 return(montage_image);
417}
418
cristybb503372010-05-27 20:51:26 +0000419static void Upsample(const size_t width,const size_t height,
420 const size_t scaled_width,unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +0000421{
cristybb503372010-05-27 20:51:26 +0000422 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000423 x,
424 y;
425
426 register unsigned char
427 *p,
428 *q,
429 *r;
430
431 /*
432 Create a new image that is a integral size greater than an existing one.
433 */
434 assert(pixels != (unsigned char *) NULL);
cristybb503372010-05-27 20:51:26 +0000435 for (y=0; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000436 {
437 p=pixels+(height-1-y)*scaled_width+(width-1);
438 q=pixels+((height-1-y) << 1)*scaled_width+((width-1) << 1);
439 *q=(*p);
440 *(q+1)=(*(p));
cristybb503372010-05-27 20:51:26 +0000441 for (x=1; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000442 {
443 p--;
444 q-=2;
445 *q=(*p);
cristyaff6d802011-04-26 01:46:31 +0000446 *(q+1)=(unsigned char) ((((size_t) *p)+((size_t) *(p+1))+1) >> 1);
cristy3ed852e2009-09-05 21:47:34 +0000447 }
448 }
cristybb503372010-05-27 20:51:26 +0000449 for (y=0; y < (ssize_t) (height-1); y++)
cristy3ed852e2009-09-05 21:47:34 +0000450 {
cristybb503372010-05-27 20:51:26 +0000451 p=pixels+((size_t) y << 1)*scaled_width;
cristy3ed852e2009-09-05 21:47:34 +0000452 q=p+scaled_width;
453 r=q+scaled_width;
cristybb503372010-05-27 20:51:26 +0000454 for (x=0; x < (ssize_t) (width-1); x++)
cristy3ed852e2009-09-05 21:47:34 +0000455 {
cristybb503372010-05-27 20:51:26 +0000456 *q=(unsigned char) ((((size_t) *p)+((size_t) *r)+1) >> 1);
457 *(q+1)=(unsigned char) ((((size_t) *p)+((size_t) *(p+2))+
458 ((size_t) *r)+((size_t) *(r+2))+2) >> 2);
cristy3ed852e2009-09-05 21:47:34 +0000459 q+=2;
460 p+=2;
461 r+=2;
462 }
cristyaff6d802011-04-26 01:46:31 +0000463 *q++=(unsigned char) ((((size_t) *p++)+((size_t) *r++)+1) >> 1);
464 *q++=(unsigned char) ((((size_t) *p++)+((size_t) *r++)+1) >> 1);
cristy3ed852e2009-09-05 21:47:34 +0000465 }
466 p=pixels+(2*height-2)*scaled_width;
467 q=pixels+(2*height-1)*scaled_width;
468 (void) CopyMagickMemory(q,p,(size_t) (2*width));
469}
470
471static Image *ReadPCDImage(const ImageInfo *image_info,ExceptionInfo *exception)
472{
473 Image
474 *image;
475
cristy3ed852e2009-09-05 21:47:34 +0000476 MagickBooleanType
477 status;
478
479 MagickOffsetType
480 offset;
481
482 MagickSizeType
483 number_pixels;
484
cristybb503372010-05-27 20:51:26 +0000485 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000486 i,
487 y;
488
cristy4c08aed2011-07-01 19:47:50 +0000489 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000490 *q;
491
492 register unsigned char
493 *c1,
494 *c2,
495 *yy;
496
cristyaff6d802011-04-26 01:46:31 +0000497 size_t
498 height,
499 number_images,
500 rotate,
501 scene,
502 width;
503
cristy3ed852e2009-09-05 21:47:34 +0000504 ssize_t
cristyaff6d802011-04-26 01:46:31 +0000505 count,
506 x;
cristy3ed852e2009-09-05 21:47:34 +0000507
508 unsigned char
509 *chroma1,
510 *chroma2,
511 *header,
512 *luma;
513
514 unsigned int
515 overview;
516
cristy3ed852e2009-09-05 21:47:34 +0000517 /*
518 Open image file.
519 */
520 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000521 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000522 if (image_info->debug != MagickFalse)
523 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
524 image_info->filename);
525 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000526 assert(exception->signature == MagickCoreSignature);
cristy9950d572011-10-01 18:22:35 +0000527 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000528 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
529 if (status == MagickFalse)
530 {
531 image=DestroyImageList(image);
532 return((Image *) NULL);
533 }
534 /*
535 Determine if this a PCD file.
536 */
537 header=(unsigned char *) AcquireQuantumMemory(0x800,3UL*sizeof(*header));
538 if (header == (unsigned char *) NULL)
539 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
540 count=ReadBlob(image,3*0x800,header);
541 overview=LocaleNCompare((char *) header,"PCD_OPA",7) == 0;
cristy771c8842015-01-09 12:13:22 +0000542 if ((count != (3*0x800)) ||
cristyc31f22e2011-10-29 01:06:32 +0000543 ((LocaleNCompare((char *) header+0x800,"PCD",3) != 0) && (overview ==0)))
cristy3ed852e2009-09-05 21:47:34 +0000544 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
545 rotate=header[0x0e02] & 0x03;
546 number_images=(header[10] << 8) | header[11];
cristy2867b872014-05-17 11:53:50 +0000547 if (number_images > 65535)
548 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +0000549 header=(unsigned char *) RelinquishMagickMemory(header);
550 /*
551 Determine resolution by scene specification.
552 */
553 if ((image->columns == 0) || (image->rows == 0))
554 scene=3;
555 else
556 {
557 width=192;
558 height=128;
559 for (scene=1; scene < 6; scene++)
560 {
561 if ((width >= image->columns) && (height >= image->rows))
562 break;
563 width<<=1;
564 height<<=1;
565 }
566 }
567 if (image_info->number_scenes != 0)
cristybb503372010-05-27 20:51:26 +0000568 scene=(size_t) MagickMin(image_info->scene,6);
cristyc31f22e2011-10-29 01:06:32 +0000569 if (overview != 0)
cristy3ed852e2009-09-05 21:47:34 +0000570 scene=1;
571 /*
572 Initialize image structure.
573 */
574 width=192;
575 height=128;
cristybb503372010-05-27 20:51:26 +0000576 for (i=1; i < (ssize_t) MagickMin(scene,3); i++)
cristy3ed852e2009-09-05 21:47:34 +0000577 {
578 width<<=1;
579 height<<=1;
580 }
581 image->columns=width;
582 image->rows=height;
583 image->depth=8;
cristybb503372010-05-27 20:51:26 +0000584 for ( ; i < (ssize_t) scene; i++)
cristy3ed852e2009-09-05 21:47:34 +0000585 {
586 image->columns<<=1;
587 image->rows<<=1;
588 }
cristyacabb842014-12-14 23:36:33 +0000589 status=SetImageExtent(image,image->columns,image->rows,exception);
590 if (status == MagickFalse)
591 return(DestroyImageList(image));
cristy3ed852e2009-09-05 21:47:34 +0000592 /*
cristy5d418802009-11-27 19:06:12 +0000593 Allocate luma and chroma memory.
cristy3ed852e2009-09-05 21:47:34 +0000594 */
595 number_pixels=(MagickSizeType) image->columns*image->rows;
596 if (number_pixels != (size_t) number_pixels)
597 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
598 chroma1=(unsigned char *) AcquireQuantumMemory(image->columns+1UL,image->rows*
cristyc31f22e2011-10-29 01:06:32 +0000599 10*sizeof(*chroma1));
cristy3ed852e2009-09-05 21:47:34 +0000600 chroma2=(unsigned char *) AcquireQuantumMemory(image->columns+1UL,image->rows*
cristyc31f22e2011-10-29 01:06:32 +0000601 10*sizeof(*chroma2));
cristy3ed852e2009-09-05 21:47:34 +0000602 luma=(unsigned char *) AcquireQuantumMemory(image->columns+1UL,image->rows*
cristyc31f22e2011-10-29 01:06:32 +0000603 10*sizeof(*luma));
cristy3ed852e2009-09-05 21:47:34 +0000604 if ((chroma1 == (unsigned char *) NULL) ||
605 (chroma2 == (unsigned char *) NULL) || (luma == (unsigned char *) NULL))
606 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
607 /*
608 Advance to image data.
609 */
610 offset=93;
cristyc31f22e2011-10-29 01:06:32 +0000611 if (overview != 0)
cristy3ed852e2009-09-05 21:47:34 +0000612 offset=2;
613 else
614 if (scene == 2)
615 offset=20;
616 else
617 if (scene <= 1)
618 offset=1;
cristybb503372010-05-27 20:51:26 +0000619 for (i=0; i < (ssize_t) (offset*0x800); i++)
cristy3ed852e2009-09-05 21:47:34 +0000620 (void) ReadBlobByte(image);
cristyc31f22e2011-10-29 01:06:32 +0000621 if (overview != 0)
cristy3ed852e2009-09-05 21:47:34 +0000622 {
623 Image
624 *overview_image;
625
626 MagickProgressMonitor
627 progress_monitor;
628
cristybb503372010-05-27 20:51:26 +0000629 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000630 j;
631
632 /*
633 Read thumbnails from overview image.
634 */
cristybb503372010-05-27 20:51:26 +0000635 for (j=1; j <= (ssize_t) number_images; j++)
cristy3ed852e2009-09-05 21:47:34 +0000636 {
637 progress_monitor=SetImageProgressMonitor(image,
638 (MagickProgressMonitor) NULL,image->client_data);
cristy151b66d2015-04-15 10:50:31 +0000639 (void) FormatLocaleString(image->filename,MagickPathExtent,
cristyf2faecf2010-05-28 19:19:36 +0000640 "images/img%04ld.pcd",(long) j);
cristy151b66d2015-04-15 10:50:31 +0000641 (void) FormatLocaleString(image->magick_filename,MagickPathExtent,
cristyf2faecf2010-05-28 19:19:36 +0000642 "images/img%04ld.pcd",(long) j);
cristybb503372010-05-27 20:51:26 +0000643 image->scene=(size_t) j;
cristy3ed852e2009-09-05 21:47:34 +0000644 image->columns=width;
645 image->rows=height;
646 image->depth=8;
647 yy=luma;
648 c1=chroma1;
649 c2=chroma2;
cristybb503372010-05-27 20:51:26 +0000650 for (y=0; y < (ssize_t) height; y+=2)
cristy3ed852e2009-09-05 21:47:34 +0000651 {
652 count=ReadBlob(image,width,yy);
653 yy+=image->columns;
654 count=ReadBlob(image,width,yy);
655 yy+=image->columns;
656 count=ReadBlob(image,width >> 1,c1);
657 c1+=image->columns;
658 count=ReadBlob(image,width >> 1,c2);
659 c2+=image->columns;
660 }
661 Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
662 Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
663 /*
664 Transfer luminance and chrominance channels.
665 */
666 yy=luma;
667 c1=chroma1;
668 c2=chroma2;
cristybb503372010-05-27 20:51:26 +0000669 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000670 {
671 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000672 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000673 break;
cristybb503372010-05-27 20:51:26 +0000674 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000675 {
cristy4c08aed2011-07-01 19:47:50 +0000676 SetPixelRed(image,ScaleCharToQuantum(*yy++),q);
677 SetPixelGreen(image,ScaleCharToQuantum(*c1++),q);
678 SetPixelBlue(image,ScaleCharToQuantum(*c2++),q);
cristyed231572011-07-14 02:18:59 +0000679 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000680 }
681 if (SyncAuthenticPixels(image,exception) == MagickFalse)
682 break;
683 }
684 image->colorspace=YCCColorspace;
685 if (LocaleCompare(image_info->magick,"PCDS") == 0)
cristye2c4f182012-05-12 14:11:53 +0000686 SetImageColorspace(image,sRGBColorspace,exception);
cristybb503372010-05-27 20:51:26 +0000687 if (j < (ssize_t) number_images)
cristy3ed852e2009-09-05 21:47:34 +0000688 {
689 /*
690 Allocate next image structure.
691 */
cristy9950d572011-10-01 18:22:35 +0000692 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000693 if (GetNextImageInList(image) == (Image *) NULL)
694 {
695 image=DestroyImageList(image);
696 return((Image *) NULL);
697 }
698 image=SyncNextImageInList(image);
699 }
700 (void) SetImageProgressMonitor(image,progress_monitor,
701 image->client_data);
702 if (image->previous == (Image *) NULL)
703 {
704 status=SetImageProgress(image,LoadImageTag,j-1,number_images);
705 if (status == MagickFalse)
706 break;
707 }
708 }
709 chroma2=(unsigned char *) RelinquishMagickMemory(chroma2);
710 chroma1=(unsigned char *) RelinquishMagickMemory(chroma1);
711 luma=(unsigned char *) RelinquishMagickMemory(luma);
712 image=GetFirstImageInList(image);
713 overview_image=OverviewImage(image_info,image,exception);
714 return(overview_image);
715 }
716 /*
717 Read interleaved image.
718 */
719 yy=luma;
720 c1=chroma1;
721 c2=chroma2;
cristybb503372010-05-27 20:51:26 +0000722 for (y=0; y < (ssize_t) height; y+=2)
cristy3ed852e2009-09-05 21:47:34 +0000723 {
724 count=ReadBlob(image,width,yy);
725 yy+=image->columns;
726 count=ReadBlob(image,width,yy);
727 yy+=image->columns;
728 count=ReadBlob(image,width >> 1,c1);
729 c1+=image->columns;
730 count=ReadBlob(image,width >> 1,c2);
731 c2+=image->columns;
732 }
733 if (scene >= 4)
734 {
735 /*
736 Recover luminance deltas for 1536x1024 image.
737 */
738 Upsample(768,512,image->columns,luma);
739 Upsample(384,256,image->columns,chroma1);
740 Upsample(384,256,image->columns,chroma2);
741 image->rows=1024;
742 for (i=0; i < (4*0x800); i++)
743 (void) ReadBlobByte(image);
cristy018f07f2011-09-04 21:15:19 +0000744 status=DecodeImage(image,luma,chroma1,chroma2,exception);
cristy3ed852e2009-09-05 21:47:34 +0000745 if ((scene >= 5) && status)
746 {
747 /*
748 Recover luminance deltas for 3072x2048 image.
749 */
750 Upsample(1536,1024,image->columns,luma);
751 Upsample(768,512,image->columns,chroma1);
752 Upsample(768,512,image->columns,chroma2);
753 image->rows=2048;
754 offset=TellBlob(image)/0x800+12;
755 offset=SeekBlob(image,offset*0x800,SEEK_SET);
cristy018f07f2011-09-04 21:15:19 +0000756 status=DecodeImage(image,luma,chroma1,chroma2,exception);
cristy3ed852e2009-09-05 21:47:34 +0000757 if ((scene >= 6) && (status != MagickFalse))
758 {
759 /*
760 Recover luminance deltas for 6144x4096 image (vaporware).
761 */
762 Upsample(3072,2048,image->columns,luma);
763 Upsample(1536,1024,image->columns,chroma1);
764 Upsample(1536,1024,image->columns,chroma2);
765 image->rows=4096;
766 }
767 }
768 }
769 Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
770 Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
771 /*
772 Transfer luminance and chrominance channels.
773 */
774 yy=luma;
775 c1=chroma1;
776 c2=chroma2;
cristybb503372010-05-27 20:51:26 +0000777 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000778 {
779 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000780 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000781 break;
cristybb503372010-05-27 20:51:26 +0000782 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000783 {
cristy4c08aed2011-07-01 19:47:50 +0000784 SetPixelRed(image,ScaleCharToQuantum(*yy++),q);
785 SetPixelGreen(image,ScaleCharToQuantum(*c1++),q);
786 SetPixelBlue(image,ScaleCharToQuantum(*c2++),q);
cristyed231572011-07-14 02:18:59 +0000787 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000788 }
789 if (SyncAuthenticPixels(image,exception) == MagickFalse)
790 break;
791 if (image->previous == (Image *) NULL)
792 {
cristycee97112010-05-28 00:44:52 +0000793 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
794 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000795 if (status == MagickFalse)
796 break;
797 }
798 }
799 chroma2=(unsigned char *) RelinquishMagickMemory(chroma2);
800 chroma1=(unsigned char *) RelinquishMagickMemory(chroma1);
801 luma=(unsigned char *) RelinquishMagickMemory(luma);
802 if (EOFBlob(image) != MagickFalse)
803 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
804 image->filename);
805 (void) CloseBlob(image);
806 if (image_info->ping == MagickFalse)
807 if ((rotate == 1) || (rotate == 3))
808 {
809 double
810 degrees;
811
812 Image
813 *rotate_image;
814
815 /*
816 Rotate image.
817 */
818 degrees=rotate == 1 ? -90.0 : 90.0;
819 rotate_image=RotateImage(image,degrees,exception);
820 if (rotate_image != (Image *) NULL)
821 {
822 image=DestroyImage(image);
823 image=rotate_image;
824 }
825 }
826 /*
827 Set CCIR 709 primaries with a D65 white point.
828 */
829 image->chromaticity.red_primary.x=0.6400f;
830 image->chromaticity.red_primary.y=0.3300f;
831 image->chromaticity.green_primary.x=0.3000f;
832 image->chromaticity.green_primary.y=0.6000f;
833 image->chromaticity.blue_primary.x=0.1500f;
834 image->chromaticity.blue_primary.y=0.0600f;
835 image->chromaticity.white_point.x=0.3127f;
836 image->chromaticity.white_point.y=0.3290f;
837 image->gamma=1.000f/2.200f;
838 image->colorspace=YCCColorspace;
839 if (LocaleCompare(image_info->magick,"PCDS") == 0)
cristye2c4f182012-05-12 14:11:53 +0000840 SetImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000841 return(GetFirstImageInList(image));
842}
843
844/*
845%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
846% %
847% %
848% %
849% R e g i s t e r P C D I m a g e %
850% %
851% %
852% %
853%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
854%
855% RegisterPCDImage() adds attributes for the PCD image format to
856% the list of supported formats. The attributes include the image format
857% tag, a method to read and/or write the format, whether the format
858% supports the saving of more than one frame to the same file or blob,
859% whether the format supports native in-memory I/O, and a brief
860% description of the format.
861%
862% The format of the RegisterPCDImage method is:
863%
cristybb503372010-05-27 20:51:26 +0000864% size_t RegisterPCDImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000865%
866*/
cristybb503372010-05-27 20:51:26 +0000867ModuleExport size_t RegisterPCDImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000868{
869 MagickInfo
870 *entry;
871
dirk06b627a2015-04-06 18:59:17 +0000872 entry=AcquireMagickInfo("PCD","PCD","Photo CD");
cristy3ed852e2009-09-05 21:47:34 +0000873 entry->decoder=(DecodeImageHandler *) ReadPCDImage;
874 entry->encoder=(EncodeImageHandler *) WritePCDImage;
875 entry->magick=(IsImageFormatHandler *) IsPCD;
dirk08e9a112015-02-22 01:51:41 +0000876 entry->flags^=CoderAdjoinFlag;
877 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +0000878 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000879 entry=AcquireMagickInfo("PCD","PCDS","Photo CD");
cristy3ed852e2009-09-05 21:47:34 +0000880 entry->decoder=(DecodeImageHandler *) ReadPCDImage;
881 entry->encoder=(EncodeImageHandler *) WritePCDImage;
dirk08e9a112015-02-22 01:51:41 +0000882 entry->flags^=CoderAdjoinFlag;
883 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +0000884 (void) RegisterMagickInfo(entry);
885 return(MagickImageCoderSignature);
886}
887
888/*
889%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
890% %
891% %
892% %
893% U n r e g i s t e r P C D I m a g e %
894% %
895% %
896% %
897%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
898%
899% UnregisterPCDImage() removes format registrations made by the
900% PCD module from the list of supported formats.
901%
902% The format of the UnregisterPCDImage method is:
903%
904% UnregisterPCDImage(void)
905%
906*/
907ModuleExport void UnregisterPCDImage(void)
908{
909 (void) UnregisterMagickInfo("PCD");
910 (void) UnregisterMagickInfo("PCDS");
911}
912
913/*
914%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
915% %
916% %
917% %
918% W r i t e P C D I m a g e %
919% %
920% %
921% %
922%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
923%
924% WritePCDImage() writes an image in the Photo CD encoded image format.
925%
926% The format of the WritePCDImage method is:
927%
cristy1e178e72011-08-28 19:44:34 +0000928% MagickBooleanType WritePCDImage(const ImageInfo *image_info,
929% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000930%
931% A description of each parameter follows.
932%
933% o image_info: the image info.
934%
935% o image: The image.
936%
cristy1e178e72011-08-28 19:44:34 +0000937% o exception: return any errors or warnings in this structure.
938%
cristy3ed852e2009-09-05 21:47:34 +0000939*/
940
941static MagickBooleanType WritePCDTile(Image *image,const char *page_geometry,
cristye941a752011-10-15 01:52:48 +0000942 const char *tile_geometry,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000943{
944 GeometryInfo
945 geometry_info;
946
947 Image
948 *downsample_image,
949 *tile_image;
950
cristy3ed852e2009-09-05 21:47:34 +0000951 MagickBooleanType
952 status;
953
954 MagickStatusType
955 flags;
956
957 RectangleInfo
958 geometry;
959
cristy4c08aed2011-07-01 19:47:50 +0000960 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000961 *p,
962 *q;
963
cristybb503372010-05-27 20:51:26 +0000964 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000965 i,
966 x;
967
cristyaff6d802011-04-26 01:46:31 +0000968 ssize_t
969 y;
970
cristy3ed852e2009-09-05 21:47:34 +0000971 /*
972 Scale image to tile size.
973 */
974 SetGeometry(image,&geometry);
975 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
976 &geometry.width,&geometry.height);
977 if ((geometry.width % 2) != 0)
978 geometry.width--;
979 if ((geometry.height % 2) != 0)
980 geometry.height--;
981 tile_image=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
cristyaa2c16c2012-03-25 22:21:35 +0000982 exception);
cristy3ed852e2009-09-05 21:47:34 +0000983 if (tile_image == (Image *) NULL)
984 return(MagickFalse);
985 flags=ParseGeometry(page_geometry,&geometry_info);
cristybb503372010-05-27 20:51:26 +0000986 geometry.width=(size_t) geometry_info.rho;
987 geometry.height=(size_t) geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +0000988 if ((flags & SigmaValue) == 0)
989 geometry.height=geometry.width;
990 if ((tile_image->columns != geometry.width) ||
991 (tile_image->rows != geometry.height))
992 {
993 Image
994 *bordered_image;
995
996 RectangleInfo
997 border_info;
998
999 /*
1000 Put a border around the image.
1001 */
1002 border_info.width=(geometry.width-tile_image->columns+1) >> 1;
1003 border_info.height=(geometry.height-tile_image->rows+1) >> 1;
cristy633f0c62011-09-15 13:27:36 +00001004 bordered_image=BorderImage(tile_image,&border_info,image->compose,
cristyc82a27b2011-10-21 01:07:16 +00001005 exception);
cristy3ed852e2009-09-05 21:47:34 +00001006 if (bordered_image == (Image *) NULL)
1007 return(MagickFalse);
1008 tile_image=DestroyImage(tile_image);
1009 tile_image=bordered_image;
1010 }
cristye941a752011-10-15 01:52:48 +00001011 (void) TransformImage(&tile_image,(char *) NULL,tile_geometry,exception);
cristyaf8d3912014-02-21 14:50:33 +00001012 (void) TransformImageColorspace(tile_image,YCCColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001013 downsample_image=ResizeImage(tile_image,tile_image->columns/2,
cristyaa2c16c2012-03-25 22:21:35 +00001014 tile_image->rows/2,TriangleFilter,exception);
cristy3ed852e2009-09-05 21:47:34 +00001015 if (downsample_image == (Image *) NULL)
1016 return(MagickFalse);
1017 /*
1018 Write tile to PCD file.
1019 */
cristybb503372010-05-27 20:51:26 +00001020 for (y=0; y < (ssize_t) tile_image->rows; y+=2)
cristy3ed852e2009-09-05 21:47:34 +00001021 {
cristyc82a27b2011-10-21 01:07:16 +00001022 p=GetVirtualPixels(tile_image,0,y,tile_image->columns,2,exception);
cristy4c08aed2011-07-01 19:47:50 +00001023 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001024 break;
cristybb503372010-05-27 20:51:26 +00001025 for (x=0; x < (ssize_t) (tile_image->columns << 1); x++)
cristy3ed852e2009-09-05 21:47:34 +00001026 {
cristy4c08aed2011-07-01 19:47:50 +00001027 (void) WriteBlobByte(image,ScaleQuantumToChar(GetPixelRed(tile_image,p)));
cristyed231572011-07-14 02:18:59 +00001028 p+=GetPixelChannels(tile_image);
cristy3ed852e2009-09-05 21:47:34 +00001029 }
cristyc82a27b2011-10-21 01:07:16 +00001030 q=GetVirtualPixels(downsample_image,0,y >> 1,downsample_image->columns,1,
1031 exception);
cristyacd2ed22011-08-30 01:44:23 +00001032 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001033 break;
cristybb503372010-05-27 20:51:26 +00001034 for (x=0; x < (ssize_t) downsample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001035 {
cristy4c08aed2011-07-01 19:47:50 +00001036 (void) WriteBlobByte(image,ScaleQuantumToChar(
1037 GetPixelGreen(tile_image,q)));
cristy3ed852e2009-09-05 21:47:34 +00001038 q++;
1039 }
cristyc82a27b2011-10-21 01:07:16 +00001040 q=GetVirtualPixels(downsample_image,0,y >> 1,downsample_image->columns,1,
1041 exception);
cristyacd2ed22011-08-30 01:44:23 +00001042 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001043 break;
cristybb503372010-05-27 20:51:26 +00001044 for (x=0; x < (ssize_t) downsample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001045 {
cristy4c08aed2011-07-01 19:47:50 +00001046 (void) WriteBlobByte(image,ScaleQuantumToChar(
1047 GetPixelBlue(tile_image,q)));
cristy3ed852e2009-09-05 21:47:34 +00001048 q++;
1049 }
1050 status=SetImageProgress(image,SaveImageTag,y,tile_image->rows);
1051 if (status == MagickFalse)
1052 break;
1053 }
1054 for (i=0; i < 0x800; i++)
1055 (void) WriteBlobByte(image,'\0');
1056 downsample_image=DestroyImage(downsample_image);
1057 tile_image=DestroyImage(tile_image);
1058 return(MagickTrue);
1059}
1060
cristy1e178e72011-08-28 19:44:34 +00001061static MagickBooleanType WritePCDImage(const ImageInfo *image_info,Image *image,
1062 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001063{
1064 Image
1065 *pcd_image;
1066
1067 MagickBooleanType
1068 status;
1069
cristybb503372010-05-27 20:51:26 +00001070 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001071 i;
1072
1073 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001074 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001075 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001076 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001077 if (image->debug != MagickFalse)
1078 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1079 pcd_image=image;
1080 if (image->columns < image->rows)
1081 {
1082 Image
1083 *rotate_image;
1084
1085 /*
1086 Rotate portrait to landscape.
1087 */
cristy1e178e72011-08-28 19:44:34 +00001088 rotate_image=RotateImage(image,90.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00001089 if (rotate_image == (Image *) NULL)
1090 return(MagickFalse);
1091 pcd_image=rotate_image;
1092 }
1093 /*
1094 Open output image file.
1095 */
cristy1e178e72011-08-28 19:44:34 +00001096 status=OpenBlob(image_info,pcd_image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001097 if (status == MagickFalse)
1098 return(status);
cristy3d9f5ba2012-06-26 13:37:31 +00001099 if (IssRGBCompatibleColorspace(pcd_image->colorspace) == MagickFalse)
cristyc511e882012-04-16 21:11:14 +00001100 (void) TransformImageColorspace(pcd_image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001101 /*
1102 Write PCD image header.
1103 */
1104 for (i=0; i < 32; i++)
1105 (void) WriteBlobByte(pcd_image,0xff);
1106 for (i=0; i < 4; i++)
1107 (void) WriteBlobByte(pcd_image,0x0e);
1108 for (i=0; i < 8; i++)
1109 (void) WriteBlobByte(pcd_image,'\0');
1110 for (i=0; i < 4; i++)
1111 (void) WriteBlobByte(pcd_image,0x01);
1112 for (i=0; i < 4; i++)
1113 (void) WriteBlobByte(pcd_image,0x05);
1114 for (i=0; i < 8; i++)
1115 (void) WriteBlobByte(pcd_image,'\0');
1116 for (i=0; i < 4; i++)
1117 (void) WriteBlobByte(pcd_image,0x0A);
1118 for (i=0; i < 36; i++)
1119 (void) WriteBlobByte(pcd_image,'\0');
1120 for (i=0; i < 4; i++)
1121 (void) WriteBlobByte(pcd_image,0x01);
1122 for (i=0; i < 1944; i++)
1123 (void) WriteBlobByte(pcd_image,'\0');
1124 (void) WriteBlob(pcd_image,7,(const unsigned char *) "PCD_IPI");
1125 (void) WriteBlobByte(pcd_image,0x06);
1126 for (i=0; i < 1530; i++)
1127 (void) WriteBlobByte(pcd_image,'\0');
1128 if (image->columns < image->rows)
1129 (void) WriteBlobByte(pcd_image,'\1');
1130 else
1131 (void) WriteBlobByte(pcd_image,'\0');
1132 for (i=0; i < (3*0x800-1539); i++)
1133 (void) WriteBlobByte(pcd_image,'\0');
1134 /*
1135 Write PCD tiles.
1136 */
cristye941a752011-10-15 01:52:48 +00001137 status=WritePCDTile(pcd_image,"768x512>","192x128",exception);
1138 status=WritePCDTile(pcd_image,"768x512>","384x256",exception);
1139 status=WritePCDTile(pcd_image,"768x512>","768x512",exception);
cristy3ed852e2009-09-05 21:47:34 +00001140 (void) CloseBlob(pcd_image);
1141 if (pcd_image != image)
1142 pcd_image=DestroyImage(pcd_image);
1143 return(status);
1144}