1 /*
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                                %
16 %                                   Cristy                                    %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2021 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/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 */
42 #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"
49 #include "MagickCore/colorspace-private.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/decorate.h"
52 #include "MagickCore/distort.h"
53 #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"
67 #include "MagickCore/resource_.h"
68 #include "MagickCore/quantum-private.h"
69 #include "MagickCore/static.h"
70 #include "MagickCore/string_.h"
71 #include "MagickCore/module.h"
72 #include "MagickCore/utility.h"
73 
74 /*
75   Forward declarations.
76 */
77 static MagickBooleanType
78   WritePCDImage(const ImageInfo *,Image *,ExceptionInfo *);
79 
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 */
DecodeImage(Image * image,unsigned char * luma,unsigned char * chroma1,unsigned char * chroma2,ExceptionInfo * exception)113 static MagickBooleanType DecodeImage(Image *image,unsigned char *luma,
114   unsigned char *chroma1,unsigned char *chroma2,ExceptionInfo *exception)
115 {
116 #define IsSync(sum)  ((sum & 0xffffff00UL) == 0xfffffe00UL)
117 #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   } \
132 }
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 
147   PCDTable
148     *pcd_table[3];
149 
150   ssize_t
151     i,
152     j;
153 
154   PCDTable
155     *r;
156 
157   unsigned char
158     *p,
159     *q;
160 
161   size_t
162     bits,
163     length,
164     plane,
165     pcd_length[3],
166     row,
167     sum;
168 
169   ssize_t
170     count,
171     quantum;
172 
173   unsigned char
174     *buffer;
175 
176   /*
177     Initialize Huffman tables.
178   */
179   assert(image != (const Image *) NULL);
180   assert(image->signature == MagickCoreSignature);
181   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;
193   for (i=0; i < 3; i++)
194   {
195     pcd_table[i]=(PCDTable *) NULL;
196     pcd_length[i]=0;
197   }
198   for (i=0; i < (ssize_t) (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         for (j=0; j < i; j++)
208           pcd_table[j]=(PCDTable *) RelinquishMagickMemory(pcd_table[j]);
209         ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
210           image->filename);
211       }
212     r=pcd_table[i];
213     for (j=0; j < (ssize_t) length; j++)
214     {
215       PCDGetBits(8);
216       r->length=(unsigned int) (sum & 0xff)+1;
217       if (r->length > 16)
218         {
219           buffer=(unsigned char *) RelinquishMagickMemory(buffer);
220           for (j=0; j <= i; j++)
221             pcd_table[j]=(PCDTable *) RelinquishMagickMemory(pcd_table[j]);
222           return(MagickFalse);
223         }
224       PCDGetBits(16);
225       r->sequence=(unsigned int) (sum & 0xffff) << 16;
226       PCDGetBits(8);
227       r->key=(unsigned char) (sum & 0xff);
228       r->mask=(~((1U << (32-r->length))-1));
229       r++;
230     }
231     pcd_length[i]=(size_t) length;
232   }
233   if (EOFBlob(image) == MagickFalse)
234     {
235       /*
236         Search for Sync byte.
237       */
238       for (i=0; i < 1; i++)
239         PCDGetBits(16);
240       for (i=0; i < 1; i++)
241         PCDGetBits(16);
242       while ((sum & 0x00fff000UL) != 0x00fff000UL)
243         PCDGetBits(8);
244       while (IsSync(sum) == 0)
245         PCDGetBits(1);
246     }
247   /*
248     Recover the Huffman encoded luminance and chrominance deltas.
249   */
250   count=0;
251   length=0;
252   plane=0;
253   row=0;
254   for (q=luma; EOFBlob(image) == MagickFalse; )
255   {
256     if (IsSync(sum) != 0)
257       {
258         /*
259           Determine plane and row number.
260         */
261         PCDGetBits(16);
262         row=((sum >> 9) & 0x1fff);
263         if (row == image->rows)
264           break;
265         PCDGetBits(8);
266         plane=sum >> 30;
267         PCDGetBits(16);
268         switch (plane)
269         {
270           case 0:
271           {
272             q=luma+row*image->columns;
273             count=(ssize_t) image->columns;
274             break;
275           }
276           case 2:
277           {
278             q=chroma1+(row >> 1)*image->columns;
279             count=(ssize_t) (image->columns >> 1);
280             plane--;
281             break;
282           }
283           case 3:
284           {
285             q=chroma2+(row >> 1)*image->columns;
286             count=(ssize_t) (image->columns >> 1);
287             plane--;
288             break;
289           }
290           default:
291           {
292             for (i=0; i < (ssize_t) (image->columns > 1536 ? 3 : 1); i++)
293               pcd_table[i]=(PCDTable *) RelinquishMagickMemory(pcd_table[i]);
294             buffer=(unsigned char *) RelinquishMagickMemory(buffer);
295             ThrowBinaryException(CorruptImageError,"CorruptImage",
296               image->filename);
297           }
298         }
299         length=pcd_length[plane];
300         continue;
301       }
302     /*
303       Decode luminance or chrominance deltas.
304     */
305     r=pcd_table[plane];
306     for (i=0; ((i < (ssize_t) length) && ((sum & r->mask) != r->sequence)); i++)
307       r++;
308     if ((row > image->rows) || (r == (PCDTable *) NULL))
309       {
310         (void) ThrowMagickException(exception,GetMagickModule(),
311           CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
312         while ((sum & 0x00fff000) != 0x00fff000)
313           PCDGetBits(8);
314         while (IsSync(sum) == 0)
315           PCDGetBits(1);
316         continue;
317       }
318     if (r->key < 128)
319       quantum=(ssize_t) (*q)+r->key;
320     else
321       quantum=(ssize_t) (*q)+r->key-256;
322     *q=(unsigned char) ((quantum < 0) ? 0 : (quantum > 255) ? 255 : quantum);
323     q++;
324     PCDGetBits(r->length);
325     count--;
326   }
327   /*
328     Relinquish resources.
329   */
330   for (i=0; i < (ssize_t) (image->columns > 1536 ? 3 : 1); i++)
331     pcd_table[i]=(PCDTable *) RelinquishMagickMemory(pcd_table[i]);
332   buffer=(unsigned char *) RelinquishMagickMemory(buffer);
333   return(MagickTrue);
334 }
335 
336 /*
337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
338 %                                                                             %
339 %                                                                             %
340 %                                                                             %
341 %   I s P C D                                                                 %
342 %                                                                             %
343 %                                                                             %
344 %                                                                             %
345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
346 %
347 %  IsPCD() returns MagickTrue if the image format type, identified by the
348 %  magick string, is PCD.
349 %
350 %  The format of the IsPCD method is:
351 %
352 %      MagickBooleanType IsPCD(const unsigned char *magick,const size_t length)
353 %
354 %  A description of each parameter follows:
355 %
356 %    o magick: compare image format pattern against these bytes.
357 %
358 %    o length: Specifies the length of the magick string.
359 %
360 */
IsPCD(const unsigned char * magick,const size_t length)361 static MagickBooleanType IsPCD(const unsigned char *magick,const size_t length)
362 {
363   if (length < 2052)
364     return(MagickFalse);
365   if (LocaleNCompare((const char *) magick+2048,"PCD_",4) == 0)
366     return(MagickTrue);
367   return(MagickFalse);
368 }
369 
370 /*
371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
372 %                                                                             %
373 %                                                                             %
374 %                                                                             %
375 %   R e a d P C D I m a g e                                                   %
376 %                                                                             %
377 %                                                                             %
378 %                                                                             %
379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
380 %
381 %  ReadPCDImage() reads a Photo CD image file and returns it.  It
382 %  allocates the memory necessary for the new Image structure and returns a
383 %  pointer to the new image.  Much of the PCD decoder was derived from
384 %  the program hpcdtoppm(1) by Hadmut Danisch.
385 %
386 %  The format of the ReadPCDImage method is:
387 %
388 %      image=ReadPCDImage(image_info)
389 %
390 %  A description of each parameter follows:
391 %
392 %    o image_info: the image info.
393 %
394 %    o exception: return any errors or warnings in this structure.
395 %
396 */
397 
OverviewImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)398 static Image *OverviewImage(const ImageInfo *image_info,Image *image,
399   ExceptionInfo *exception)
400 {
401   Image
402     *montage_image;
403 
404   MontageInfo
405     *montage_info;
406 
407   Image
408     *p;
409 
410   /*
411     Create the PCD Overview image.
412   */
413   for (p=image; p != (Image *) NULL; p=p->next)
414   {
415     (void) DeleteImageProperty(p,"label");
416     (void) SetImageProperty(p,"label",DefaultTileLabel,exception);
417   }
418   montage_info=CloneMontageInfo(image_info,(MontageInfo *) NULL);
419   (void) CopyMagickString(montage_info->filename,image_info->filename,
420     MagickPathExtent);
421   montage_image=MontageImageList(image_info,montage_info,image,exception);
422   montage_info=DestroyMontageInfo(montage_info);
423   if (montage_image == (Image *) NULL)
424     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
425   image=DestroyImageList(image);
426   return(montage_image);
427 }
428 
Upsample(const size_t width,const size_t height,const size_t scaled_width,unsigned char * pixels)429 static void Upsample(const size_t width,const size_t height,
430   const size_t scaled_width,unsigned char *pixels)
431 {
432   ssize_t
433     x,
434     y;
435 
436   unsigned char
437     *p,
438     *q,
439     *r;
440 
441   /*
442     Create a new image that is a integral size greater than an existing one.
443   */
444   assert(pixels != (unsigned char *) NULL);
445   for (y=0; y < (ssize_t) height; y++)
446   {
447     p=pixels+(height-1-y)*scaled_width+(width-1);
448     q=pixels+((height-1-y) << 1)*scaled_width+((width-1) << 1);
449     *q=(*p);
450     *(q+1)=(*(p));
451     for (x=1; x < (ssize_t) width; x++)
452     {
453       p--;
454       q-=2;
455       *q=(*p);
456       *(q+1)=(unsigned char) ((((size_t) *p)+((size_t) *(p+1))+1) >> 1);
457     }
458   }
459   for (y=0; y < (ssize_t) (height-1); y++)
460   {
461     p=pixels+((size_t) y << 1)*scaled_width;
462     q=p+scaled_width;
463     r=q+scaled_width;
464     for (x=0; x < (ssize_t) (width-1); x++)
465     {
466       *q=(unsigned char) ((((size_t) *p)+((size_t) *r)+1) >> 1);
467       *(q+1)=(unsigned char) ((((size_t) *p)+((size_t) *(p+2))+
468         ((size_t) *r)+((size_t) *(r+2))+2) >> 2);
469       q+=2;
470       p+=2;
471       r+=2;
472     }
473     *q++=(unsigned char) ((((size_t) *p++)+((size_t) *r++)+1) >> 1);
474     *q++=(unsigned char) ((((size_t) *p++)+((size_t) *r++)+1) >> 1);
475   }
476   p=pixels+(2*height-2)*scaled_width;
477   q=pixels+(2*height-1)*scaled_width;
478   (void) memcpy(q,p,(size_t) (2*width));
479 }
480 
ReadPCDImage(const ImageInfo * image_info,ExceptionInfo * exception)481 static Image *ReadPCDImage(const ImageInfo *image_info,ExceptionInfo *exception)
482 {
483 #define ThrowPCDException(exception,message) \
484 { \
485   if (header != (unsigned char *) NULL) \
486     header=(unsigned char *) RelinquishMagickMemory(header); \
487   if (pixel_info != (MemoryInfo *) NULL) \
488     pixel_info=RelinquishVirtualMemory(pixel_info); \
489   ThrowReaderException((exception),(message)); \
490 }
491 
492   Image
493     *image;
494 
495   MagickBooleanType
496     status;
497 
498   MagickOffsetType
499     offset;
500 
501   MemoryInfo
502     *pixel_info;
503 
504   ssize_t
505     i,
506     y;
507 
508   Quantum
509     *q;
510 
511   unsigned char
512     *c1,
513     *c2,
514     *yy;
515 
516   size_t
517     height,
518     number_images,
519     number_pixels,
520     rotate,
521     scene,
522     width;
523 
524   ssize_t
525     count,
526     x;
527 
528   unsigned char
529     *chroma1,
530     *chroma2,
531     *header,
532     *luma;
533 
534   unsigned int
535     overview;
536 
537   /*
538     Open image file.
539   */
540   assert(image_info != (const ImageInfo *) NULL);
541   assert(image_info->signature == MagickCoreSignature);
542   if (image_info->debug != MagickFalse)
543     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
544       image_info->filename);
545   assert(exception != (ExceptionInfo *) NULL);
546   assert(exception->signature == MagickCoreSignature);
547   image=AcquireImage(image_info,exception);
548   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
549   if (status == MagickFalse)
550     {
551       image=DestroyImageList(image);
552       return((Image *) NULL);
553     }
554   /*
555     Determine if this a PCD file.
556   */
557   header=(unsigned char *) AcquireQuantumMemory(0x800,3UL*sizeof(*header));
558   if (header == (unsigned char *) NULL)
559     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
560   pixel_info=(MemoryInfo *) NULL;
561   count=ReadBlob(image,3*0x800,header);
562   if (count != (3*0x800))
563     ThrowPCDException(CorruptImageError,"ImproperImageHeader");
564   overview=LocaleNCompare((char *) header,"PCD_OPA",7) == 0;
565   if ((LocaleNCompare((char *) header+0x800,"PCD",3) != 0) && (overview == 0))
566     ThrowPCDException(CorruptImageError,"ImproperImageHeader");
567   rotate=header[0x0e02] & 0x03;
568   number_images=((header[10] << 8) | header[11]) & 0xffff;
569   header=(unsigned char *) RelinquishMagickMemory(header);
570   if ((overview != 0) &&
571       (AcquireMagickResource(ListLengthResource,number_images) == MagickFalse))
572     ThrowPCDException(ResourceLimitError,"ListLengthExceedsLimit");
573   /*
574     Determine resolution by scene specification.
575   */
576   if ((image->columns == 0) || (image->rows == 0))
577     scene=3;
578   else
579     {
580       width=192;
581       height=128;
582       for (scene=1; scene < 6; scene++)
583       {
584         if ((width >= image->columns) && (height >= image->rows))
585           break;
586         width<<=1;
587         height<<=1;
588       }
589     }
590   if (image_info->number_scenes != 0)
591     scene=(size_t) MagickMin(image_info->scene,6);
592   if (overview != 0)
593     scene=1;
594   /*
595     Initialize image structure.
596   */
597   width=192;
598   height=128;
599   for (i=1; i < (ssize_t) MagickMin(scene,3); i++)
600   {
601     width<<=1;
602     height<<=1;
603   }
604   image->columns=width;
605   image->rows=height;
606   image->depth=8;
607   for ( ; i < (ssize_t) scene; i++)
608   {
609     image->columns<<=1;
610     image->rows<<=1;
611   }
612   status=SetImageExtent(image,image->columns,image->rows,exception);
613   if (status == MagickFalse)
614     return(DestroyImageList(image));
615   status=ResetImagePixels(image,exception);
616   if (status == MagickFalse)
617     return(DestroyImageList(image));
618   /*
619     Allocate luma and chroma memory.
620   */
621   pixel_info=AcquireVirtualMemory(image->columns+1UL,30*image->rows*
622     sizeof(*luma));
623   if (pixel_info == (MemoryInfo *) NULL)
624     ThrowPCDException(ResourceLimitError,"MemoryAllocationFailed");
625   number_pixels=(image->columns+1UL)*10*image->rows*sizeof(*luma);
626   luma=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
627   chroma1=(unsigned char *) GetVirtualMemoryBlob(pixel_info)+number_pixels;
628   chroma2=(unsigned char *) GetVirtualMemoryBlob(pixel_info)+2*number_pixels;
629   (void) memset(luma,0,3*number_pixels);
630   /*
631     Advance to image data.
632   */
633   offset=93;
634   if (overview != 0)
635     offset=2;
636   else
637     if (scene == 2)
638       offset=20;
639     else
640       if (scene <= 1)
641         offset=1;
642   for (i=0; i < (ssize_t) (offset*0x800); i++)
643     if (ReadBlobByte(image) == EOF)
644       ThrowPCDException(CorruptImageError,"UnexpectedEndOfFile");
645   if (overview != 0)
646     {
647       MagickProgressMonitor
648         progress_monitor;
649 
650       ssize_t
651         j;
652 
653       /*
654         Read thumbnails from overview image.
655       */
656       for (j=1; j <= (ssize_t) number_images; j++)
657       {
658         progress_monitor=SetImageProgressMonitor(image,
659           (MagickProgressMonitor) NULL,image->client_data);
660         (void) FormatLocaleString(image->filename,MagickPathExtent,
661           "images/img%04ld.pcd",(long) j);
662         (void) FormatLocaleString(image->magick_filename,MagickPathExtent,
663           "images/img%04ld.pcd",(long) j);
664         image->scene=(size_t) j;
665         image->columns=width;
666         image->rows=height;
667         image->depth=8;
668         yy=luma;
669         c1=chroma1;
670         c2=chroma2;
671         for (y=0; y < (ssize_t) height; y+=2)
672         {
673           count=ReadBlob(image,width,yy);
674           yy+=image->columns;
675           count=ReadBlob(image,width,yy);
676           yy+=image->columns;
677           count=ReadBlob(image,width >> 1,c1);
678           c1+=image->columns;
679           count=ReadBlob(image,width >> 1,c2);
680           c2+=image->columns;
681           if (EOFBlob(image) != MagickFalse)
682             ThrowPCDException(CorruptImageError,"UnexpectedEndOfFile");
683         }
684         Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
685         Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
686         /*
687           Transfer luminance and chrominance channels.
688         */
689         yy=luma;
690         c1=chroma1;
691         c2=chroma2;
692         for (y=0; y < (ssize_t) image->rows; y++)
693         {
694           q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
695           if (q == (Quantum *) NULL)
696             break;
697           for (x=0; x < (ssize_t) image->columns; x++)
698           {
699             SetPixelRed(image,ScaleCharToQuantum(*yy++),q);
700             SetPixelGreen(image,ScaleCharToQuantum(*c1++),q);
701             SetPixelBlue(image,ScaleCharToQuantum(*c2++),q);
702             q+=GetPixelChannels(image);
703           }
704           if (SyncAuthenticPixels(image,exception) == MagickFalse)
705             break;
706         }
707         image->colorspace=YCCColorspace;
708         if (LocaleCompare(image_info->magick,"PCDS") == 0)
709           (void) SetImageColorspace(image,sRGBColorspace,exception);
710         if (EOFBlob(image) != MagickFalse)
711           break;
712         if (j < (ssize_t) number_images)
713           {
714             /*
715               Allocate next image structure.
716             */
717             AcquireNextImage(image_info,image,exception);
718             if (GetNextImageInList(image) == (Image *) NULL)
719               {
720                 status=MagickFalse;
721                 break;
722               }
723             image=SyncNextImageInList(image);
724           }
725         (void) SetImageProgressMonitor(image,progress_monitor,
726           image->client_data);
727         if (image->previous == (Image *) NULL)
728           {
729             status=SetImageProgress(image,LoadImageTag,j-1,number_images);
730             if (status == MagickFalse)
731               break;
732           }
733       }
734       pixel_info=RelinquishVirtualMemory(pixel_info);
735       if (status == MagickFalse)
736         return(DestroyImageList(image));
737       return(OverviewImage(image_info,GetFirstImageInList(image),exception));
738     }
739   /*
740     Read interleaved image.
741   */
742   yy=luma;
743   c1=chroma1;
744   c2=chroma2;
745   for (y=0; y < (ssize_t) height; y+=2)
746   {
747     count=ReadBlob(image,width,yy);
748     yy+=image->columns;
749     count=ReadBlob(image,width,yy);
750     yy+=image->columns;
751     count=ReadBlob(image,width >> 1,c1);
752     c1+=image->columns;
753     count=ReadBlob(image,width >> 1,c2);
754     c2+=image->columns;
755     if (EOFBlob(image) != MagickFalse)
756       ThrowPCDException(CorruptImageError,"UnexpectedEndOfFile");
757   }
758   if (scene >= 4)
759     {
760       /*
761         Recover luminance deltas for 1536x1024 image.
762       */
763       Upsample(768,512,image->columns,luma);
764       Upsample(384,256,image->columns,chroma1);
765       Upsample(384,256,image->columns,chroma2);
766       image->rows=1024;
767       for (i=0; i < (4*0x800); i++)
768         (void) ReadBlobByte(image);
769       status=DecodeImage(image,luma,chroma1,chroma2,exception);
770       if ((scene >= 5) && status)
771         {
772           /*
773             Recover luminance deltas for 3072x2048 image.
774           */
775           Upsample(1536,1024,image->columns,luma);
776           Upsample(768,512,image->columns,chroma1);
777           Upsample(768,512,image->columns,chroma2);
778           image->rows=2048;
779           offset=TellBlob(image)/0x800+12;
780           offset=SeekBlob(image,offset*0x800,SEEK_SET);
781           status=DecodeImage(image,luma,chroma1,chroma2,exception);
782           if ((scene >= 6) && (status != MagickFalse))
783             {
784               /*
785                 Recover luminance deltas for 6144x4096 image (vaporware).
786               */
787               Upsample(3072,2048,image->columns,luma);
788               Upsample(1536,1024,image->columns,chroma1);
789               Upsample(1536,1024,image->columns,chroma2);
790               image->rows=4096;
791             }
792         }
793     }
794   Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
795   Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
796   /*
797     Transfer luminance and chrominance channels.
798   */
799   yy=luma;
800   c1=chroma1;
801   c2=chroma2;
802   for (y=0; y < (ssize_t) image->rows; y++)
803   {
804     q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
805     if (q == (Quantum *) NULL)
806       break;
807     for (x=0; x < (ssize_t) image->columns; x++)
808     {
809       SetPixelRed(image,ScaleCharToQuantum(*yy++),q);
810       SetPixelGreen(image,ScaleCharToQuantum(*c1++),q);
811       SetPixelBlue(image,ScaleCharToQuantum(*c2++),q);
812       q+=GetPixelChannels(image);
813     }
814     if (SyncAuthenticPixels(image,exception) == MagickFalse)
815       break;
816     if (image->previous == (Image *) NULL)
817       {
818         status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
819           image->rows);
820         if (status == MagickFalse)
821           break;
822       }
823   }
824   pixel_info=RelinquishVirtualMemory(pixel_info);
825   if (EOFBlob(image) != MagickFalse)
826     ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
827       image->filename);
828   (void) CloseBlob(image);
829   if (image_info->ping == MagickFalse)
830     if ((rotate == 1) || (rotate == 3))
831       {
832         double
833           degrees;
834 
835         Image
836           *rotate_image;
837 
838         /*
839           Rotate image.
840         */
841         degrees=rotate == 1 ? -90.0 : 90.0;
842         rotate_image=RotateImage(image,degrees,exception);
843         if (rotate_image != (Image *) NULL)
844           {
845             image=DestroyImage(image);
846             image=rotate_image;
847           }
848       }
849   /*
850     Set CCIR 709 primaries with a D65 white point.
851   */
852   image->chromaticity.red_primary.x=0.6400f;
853   image->chromaticity.red_primary.y=0.3300f;
854   image->chromaticity.green_primary.x=0.3000f;
855   image->chromaticity.green_primary.y=0.6000f;
856   image->chromaticity.blue_primary.x=0.1500f;
857   image->chromaticity.blue_primary.y=0.0600f;
858   image->chromaticity.white_point.x=0.3127f;
859   image->chromaticity.white_point.y=0.3290f;
860   image->gamma=1.000f/2.200f;
861   image->colorspace=YCCColorspace;
862   if (LocaleCompare(image_info->magick,"PCDS") == 0)
863     (void) SetImageColorspace(image,sRGBColorspace,exception);
864   if (image_info->scene != 0)
865     for (i=0; i < (ssize_t) image_info->scene; i++)
866       AppendImageToList(&image,CloneImage(image,0,0,MagickTrue,exception));
867   return(GetFirstImageInList(image));
868 }
869 
870 /*
871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
872 %                                                                             %
873 %                                                                             %
874 %                                                                             %
875 %   R e g i s t e r P C D I m a g e                                           %
876 %                                                                             %
877 %                                                                             %
878 %                                                                             %
879 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
880 %
881 %  RegisterPCDImage() adds attributes for the PCD image format to
882 %  the list of supported formats.  The attributes include the image format
883 %  tag, a method to read and/or write the format, whether the format
884 %  supports the saving of more than one frame to the same file or blob,
885 %  whether the format supports native in-memory I/O, and a brief
886 %  description of the format.
887 %
888 %  The format of the RegisterPCDImage method is:
889 %
890 %      size_t RegisterPCDImage(void)
891 %
892 */
RegisterPCDImage(void)893 ModuleExport size_t RegisterPCDImage(void)
894 {
895   MagickInfo
896     *entry;
897 
898   entry=AcquireMagickInfo("PCD","PCD","Photo CD");
899   entry->decoder=(DecodeImageHandler *) ReadPCDImage;
900   entry->encoder=(EncodeImageHandler *) WritePCDImage;
901   entry->magick=(IsImageFormatHandler *) IsPCD;
902   entry->flags^=CoderAdjoinFlag;
903   entry->flags|=CoderDecoderSeekableStreamFlag;
904   (void) RegisterMagickInfo(entry);
905   entry=AcquireMagickInfo("PCD","PCDS","Photo CD");
906   entry->decoder=(DecodeImageHandler *) ReadPCDImage;
907   entry->encoder=(EncodeImageHandler *) WritePCDImage;
908   entry->flags^=CoderAdjoinFlag;
909   entry->flags|=CoderDecoderSeekableStreamFlag;
910   (void) RegisterMagickInfo(entry);
911   return(MagickImageCoderSignature);
912 }
913 
914 /*
915 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
916 %                                                                             %
917 %                                                                             %
918 %                                                                             %
919 %   U n r e g i s t e r P C D I m a g e                                       %
920 %                                                                             %
921 %                                                                             %
922 %                                                                             %
923 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
924 %
925 %  UnregisterPCDImage() removes format registrations made by the
926 %  PCD module from the list of supported formats.
927 %
928 %  The format of the UnregisterPCDImage method is:
929 %
930 %      UnregisterPCDImage(void)
931 %
932 */
UnregisterPCDImage(void)933 ModuleExport void UnregisterPCDImage(void)
934 {
935   (void) UnregisterMagickInfo("PCD");
936   (void) UnregisterMagickInfo("PCDS");
937 }
938 
939 /*
940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
941 %                                                                             %
942 %                                                                             %
943 %                                                                             %
944 %   W r i t e P C D I m a g e                                                 %
945 %                                                                             %
946 %                                                                             %
947 %                                                                             %
948 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
949 %
950 %  WritePCDImage() writes an image in the Photo CD encoded image format.
951 %
952 %  The format of the WritePCDImage method is:
953 %
954 %      MagickBooleanType WritePCDImage(const ImageInfo *image_info,
955 %        Image *image,ExceptionInfo *exception)
956 %
957 %  A description of each parameter follows.
958 %
959 %    o image_info: the image info.
960 %
961 %    o image:  The image.
962 %
963 %    o exception: return any errors or warnings in this structure.
964 %
965 */
966 
WritePCDTile(Image * image,const char * page_geometry,const size_t tile_columns,const size_t tile_rows,ExceptionInfo * exception)967 static MagickBooleanType WritePCDTile(Image *image,const char *page_geometry,
968   const size_t tile_columns,const size_t tile_rows,ExceptionInfo *exception)
969 {
970   GeometryInfo
971     geometry_info;
972 
973   Image
974     *downsample_image,
975     *tile_image;
976 
977   MagickBooleanType
978     status;
979 
980   MagickStatusType
981     flags;
982 
983   RectangleInfo
984     geometry;
985 
986   const Quantum
987     *p,
988     *q;
989 
990   ssize_t
991     i,
992     x;
993 
994   ssize_t
995     y;
996 
997   /*
998     Scale image to tile size.
999   */
1000   SetGeometry(image,&geometry);
1001   (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1002     &geometry.width,&geometry.height);
1003   if ((geometry.width % 2) != 0)
1004     geometry.width--;
1005   if ((geometry.height % 2) != 0)
1006     geometry.height--;
1007   tile_image=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
1008     exception);
1009   if (tile_image == (Image *) NULL)
1010     return(MagickFalse);
1011   flags=ParseGeometry(page_geometry,&geometry_info);
1012   geometry.width=(size_t) geometry_info.rho;
1013   geometry.height=(size_t) geometry_info.sigma;
1014   if ((flags & SigmaValue) == 0)
1015     geometry.height=geometry.width;
1016   if ((tile_image->columns != geometry.width) ||
1017       (tile_image->rows != geometry.height))
1018     {
1019       Image
1020         *bordered_image;
1021 
1022       RectangleInfo
1023         border_info;
1024 
1025       /*
1026         Put a border around the image.
1027       */
1028       border_info.width=(geometry.width-tile_image->columns+1) >> 1;
1029       border_info.height=(geometry.height-tile_image->rows+1) >> 1;
1030       bordered_image=BorderImage(tile_image,&border_info,image->compose,
1031         exception);
1032       if (bordered_image == (Image *) NULL)
1033         return(MagickFalse);
1034       tile_image=DestroyImage(tile_image);
1035       tile_image=bordered_image;
1036     }
1037   if ((tile_image->columns != tile_columns) || (tile_image->rows != tile_rows))
1038     {
1039       Image
1040         *resize_image;
1041 
1042       resize_image=ResizeImage(tile_image,tile_columns,tile_rows,
1043         tile_image->filter,exception);
1044       if (resize_image != (Image *) NULL)
1045         {
1046           tile_image=DestroyImage(tile_image);
1047           tile_image=resize_image;
1048         }
1049     }
1050   (void) TransformImageColorspace(tile_image,YCCColorspace,exception);
1051   downsample_image=ResizeImage(tile_image,tile_image->columns/2,
1052     tile_image->rows/2,TriangleFilter,exception);
1053   if (downsample_image == (Image *) NULL)
1054     return(MagickFalse);
1055   /*
1056     Write tile to PCD file.
1057   */
1058   for (y=0; y < (ssize_t) tile_image->rows; y+=2)
1059   {
1060     p=GetVirtualPixels(tile_image,0,y,tile_image->columns,2,exception);
1061     if (p == (const Quantum *) NULL)
1062       break;
1063     for (x=0; x < (ssize_t) (tile_image->columns << 1); x++)
1064     {
1065       (void) WriteBlobByte(image,ScaleQuantumToChar(GetPixelRed(tile_image,p)));
1066       p+=GetPixelChannels(tile_image);
1067     }
1068     q=GetVirtualPixels(downsample_image,0,y >> 1,downsample_image->columns,1,
1069       exception);
1070     if (q == (Quantum *) NULL)
1071       break;
1072     for (x=0; x < (ssize_t) downsample_image->columns; x++)
1073     {
1074       (void) WriteBlobByte(image,ScaleQuantumToChar(
1075         GetPixelGreen(tile_image,q)));
1076       q+=GetPixelChannels(tile_image);
1077     }
1078     q=GetVirtualPixels(downsample_image,0,y >> 1,downsample_image->columns,1,
1079       exception);
1080     if (q == (Quantum *) NULL)
1081       break;
1082     for (x=0; x < (ssize_t) downsample_image->columns; x++)
1083     {
1084       (void) WriteBlobByte(image,ScaleQuantumToChar(
1085         GetPixelBlue(tile_image,q)));
1086       q+=GetPixelChannels(tile_image);
1087     }
1088     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1089       tile_image->rows);
1090     if (status == MagickFalse)
1091       break;
1092   }
1093   for (i=0; i < 0x800; i++)
1094     (void) WriteBlobByte(image,'\0');
1095   downsample_image=DestroyImage(downsample_image);
1096   tile_image=DestroyImage(tile_image);
1097   return(MagickTrue);
1098 }
1099 
WritePCDImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)1100 static MagickBooleanType WritePCDImage(const ImageInfo *image_info,Image *image,
1101   ExceptionInfo *exception)
1102 {
1103   Image
1104     *pcd_image;
1105 
1106   MagickBooleanType
1107     status;
1108 
1109   ssize_t
1110     i;
1111 
1112   assert(image_info != (const ImageInfo *) NULL);
1113   assert(image_info->signature == MagickCoreSignature);
1114   assert(image != (Image *) NULL);
1115   assert(image->signature == MagickCoreSignature);
1116   if (image->debug != MagickFalse)
1117     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1118   pcd_image=image;
1119   if (image->columns < image->rows)
1120     {
1121       Image
1122         *rotate_image;
1123 
1124       /*
1125         Rotate portrait to landscape.
1126       */
1127       rotate_image=RotateImage(image,90.0,exception);
1128       if (rotate_image == (Image *) NULL)
1129         return(MagickFalse);
1130       pcd_image=rotate_image;
1131       DestroyBlob(rotate_image);
1132       pcd_image->blob=ReferenceBlob(image->blob);
1133     }
1134   /*
1135     Open output image file.
1136   */
1137   status=OpenBlob(image_info,pcd_image,WriteBinaryBlobMode,exception);
1138   if (status == MagickFalse)
1139     {
1140       if (pcd_image != image)
1141         pcd_image=DestroyImage(pcd_image);
1142       return(status);
1143     }
1144   if (IssRGBCompatibleColorspace(pcd_image->colorspace) == MagickFalse)
1145     (void) TransformImageColorspace(pcd_image,sRGBColorspace,exception);
1146   /*
1147     Write PCD image header.
1148   */
1149   for (i=0; i < 32; i++)
1150     (void) WriteBlobByte(pcd_image,0xff);
1151   for (i=0; i < 4; i++)
1152     (void) WriteBlobByte(pcd_image,0x0e);
1153   for (i=0; i < 8; i++)
1154     (void) WriteBlobByte(pcd_image,'\0');
1155   for (i=0; i < 4; i++)
1156     (void) WriteBlobByte(pcd_image,0x01);
1157   for (i=0; i < 4; i++)
1158     (void) WriteBlobByte(pcd_image,0x05);
1159   for (i=0; i < 8; i++)
1160     (void) WriteBlobByte(pcd_image,'\0');
1161   for (i=0; i < 4; i++)
1162     (void) WriteBlobByte(pcd_image,0x0A);
1163   for (i=0; i < 36; i++)
1164     (void) WriteBlobByte(pcd_image,'\0');
1165   for (i=0; i < 4; i++)
1166     (void) WriteBlobByte(pcd_image,0x01);
1167   for (i=0; i < 1944; i++)
1168     (void) WriteBlobByte(pcd_image,'\0');
1169   (void) WriteBlob(pcd_image,7,(const unsigned char *) "PCD_IPI");
1170   (void) WriteBlobByte(pcd_image,0x06);
1171   for (i=0; i < 1530; i++)
1172     (void) WriteBlobByte(pcd_image,'\0');
1173   if (image->columns < image->rows)
1174     (void) WriteBlobByte(pcd_image,'\1');
1175   else
1176     (void) WriteBlobByte(pcd_image,'\0');
1177   for (i=0; i < (3*0x800-1539); i++)
1178     (void) WriteBlobByte(pcd_image,'\0');
1179   /*
1180     Write PCD tiles.
1181   */
1182   status=WritePCDTile(pcd_image,"768x512>",192,128,exception);
1183   status=WritePCDTile(pcd_image,"768x512>",384,256,exception);
1184   status=WritePCDTile(pcd_image,"768x512>",768,512,exception);
1185   (void) CloseBlob(pcd_image);
1186   if (pcd_image != image)
1187     pcd_image=DestroyImage(pcd_image);
1188   return(status);
1189 }
1190