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