]> granicus.if.org Git - imagemagick/blob - coders/pcd.c
a823c641b5b0855b5381fd04d860694e7cb2f6bf
[imagemagick] / coders / pcd.c
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-2014 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 %    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 \f
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/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"
73 \f
74 /*
75   Forward declarations.
76 */
77 static MagickBooleanType
78   WritePCDImage(const ImageInfo *,Image *,ExceptionInfo *);
79 \f
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 */
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 == MagickSignature);
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         ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
208           image->filename);
209       }
210     r=pcd_table[i];
211     for (j=0; j < (ssize_t) length; j++)
212     {
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     }
227     pcd_length[i]=(size_t) length;
228   }
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);
238   while (IsSync(sum) == 0)
239     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   {
250     if (IsSync(sum) != 0)
251       {
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];
297     for (i=0; ((i < (ssize_t) length) && ((sum & r->mask) != r->sequence)); i++)
298       r++;
299     if ((row > image->rows) || (r == (PCDTable *) NULL))
300       {
301         (void) ThrowMagickException(exception,GetMagickModule(),
302           CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
303         while ((sum & 0x00fff000) != 0x00fff000)
304           PCDGetBits(8);
305         while (IsSync(sum) == 0)
306           PCDGetBits(1);
307         continue;
308       }
309     if (r->key < 128)
310       quantum=(ssize_t) (*q)+r->key;
311     else
312       quantum=(ssize_t) (*q)+r->key-256;
313     *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 \f
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 */
352 static 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 \f
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 */
388
389 static inline size_t MagickMin(const size_t x,const size_t y)
390 {
391   if (x < y)
392     return(x);
393   return(y);
394 }
395
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     MaxTextExtent);
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=DestroyImage(image);
424   return(montage_image);
425 }
426
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) CopyMagickMemory(q,p,(size_t) (2*width));
477 }
478
479 static Image *ReadPCDImage(const ImageInfo *image_info,ExceptionInfo *exception)
480 {
481   Image
482     *image;
483
484   MagickBooleanType
485     status;
486
487   MagickOffsetType
488     offset;
489
490   MagickSizeType
491     number_pixels;
492
493   register ssize_t
494     i,
495     y;
496
497   register Quantum
498     *q;
499
500   register unsigned char
501     *c1,
502     *c2,
503     *yy;
504
505   size_t
506     height,
507     number_images,
508     rotate,
509     scene,
510     width;
511
512   ssize_t
513     count,
514     x;
515
516   unsigned char
517     *chroma1,
518     *chroma2,
519     *header,
520     *luma;
521
522   unsigned int
523     overview;
524
525   /*
526     Open image file.
527   */
528   assert(image_info != (const ImageInfo *) NULL);
529   assert(image_info->signature == MagickSignature);
530   if (image_info->debug != MagickFalse)
531     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
532       image_info->filename);
533   assert(exception != (ExceptionInfo *) NULL);
534   assert(exception->signature == MagickSignature);
535   image=AcquireImage(image_info,exception);
536   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
537   if (status == MagickFalse)
538     {
539       image=DestroyImageList(image);
540       return((Image *) NULL);
541     }
542   /*
543     Determine if this a PCD file.
544   */
545   header=(unsigned char *) AcquireQuantumMemory(0x800,3UL*sizeof(*header));
546   if (header == (unsigned char *) NULL)
547     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
548   count=ReadBlob(image,3*0x800,header);
549   overview=LocaleNCompare((char *) header,"PCD_OPA",7) == 0;
550   if ((count == 0) ||
551       ((LocaleNCompare((char *) header+0x800,"PCD",3) != 0) && (overview ==0)))
552     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
553   rotate=header[0x0e02] & 0x03;
554   number_images=(header[10] << 8) | header[11];
555   if (number_images > 65535)
556     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
557   header=(unsigned char *) RelinquishMagickMemory(header);
558   /*
559     Determine resolution by scene specification.
560   */
561   if ((image->columns == 0) || (image->rows == 0))
562     scene=3;
563   else
564     {
565       width=192;
566       height=128;
567       for (scene=1; scene < 6; scene++)
568       {
569         if ((width >= image->columns) && (height >= image->rows))
570           break;
571         width<<=1;
572         height<<=1;
573       }
574     }
575   if (image_info->number_scenes != 0)
576     scene=(size_t) MagickMin(image_info->scene,6);
577   if (overview != 0)
578     scene=1;
579   /*
580     Initialize image structure.
581   */
582   width=192;
583   height=128;
584   for (i=1; i < (ssize_t) MagickMin(scene,3); i++)
585   {
586     width<<=1;
587     height<<=1;
588   }
589   image->columns=width;
590   image->rows=height;
591   image->depth=8;
592   for ( ; i < (ssize_t) scene; i++)
593   {
594     image->columns<<=1;
595     image->rows<<=1;
596   }
597   /*
598     Allocate luma and chroma memory.
599   */
600   number_pixels=(MagickSizeType) image->columns*image->rows;
601   if (number_pixels != (size_t) number_pixels)
602     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
603   chroma1=(unsigned char *) AcquireQuantumMemory(image->columns+1UL,image->rows*
604     10*sizeof(*chroma1));
605   chroma2=(unsigned char *) AcquireQuantumMemory(image->columns+1UL,image->rows*
606     10*sizeof(*chroma2));
607   luma=(unsigned char *) AcquireQuantumMemory(image->columns+1UL,image->rows*
608     10*sizeof(*luma));
609   if ((chroma1 == (unsigned char *) NULL) ||
610       (chroma2 == (unsigned char *) NULL) || (luma == (unsigned char *) NULL))
611     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
612   /*
613     Advance to image data.
614   */
615   offset=93;
616   if (overview != 0)
617     offset=2;
618   else
619     if (scene == 2)
620       offset=20;
621     else
622       if (scene <= 1)
623         offset=1;
624   for (i=0; i < (ssize_t) (offset*0x800); i++)
625     (void) ReadBlobByte(image);
626   if (overview != 0)
627     {
628       Image
629         *overview_image;
630
631       MagickProgressMonitor
632         progress_monitor;
633
634       register ssize_t
635         j;
636
637       /*
638         Read thumbnails from overview image.
639       */
640       for (j=1; j <= (ssize_t) number_images; j++)
641       {
642         progress_monitor=SetImageProgressMonitor(image,
643           (MagickProgressMonitor) NULL,image->client_data);
644         (void) FormatLocaleString(image->filename,MaxTextExtent,
645           "images/img%04ld.pcd",(long) j);
646         (void) FormatLocaleString(image->magick_filename,MaxTextExtent,
647           "images/img%04ld.pcd",(long) j);
648         image->scene=(size_t) j;
649         image->columns=width;
650         image->rows=height;
651         image->depth=8;
652         yy=luma;
653         c1=chroma1;
654         c2=chroma2;
655         for (y=0; y < (ssize_t) height; y+=2)
656         {
657           count=ReadBlob(image,width,yy);
658           yy+=image->columns;
659           count=ReadBlob(image,width,yy);
660           yy+=image->columns;
661           count=ReadBlob(image,width >> 1,c1);
662           c1+=image->columns;
663           count=ReadBlob(image,width >> 1,c2);
664           c2+=image->columns;
665         }
666         Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
667         Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
668         /*
669           Transfer luminance and chrominance channels.
670         */
671         yy=luma;
672         c1=chroma1;
673         c2=chroma2;
674         for (y=0; y < (ssize_t) image->rows; y++)
675         {
676           q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
677           if (q == (Quantum *) NULL)
678             break;
679           for (x=0; x < (ssize_t) image->columns; x++)
680           {
681             SetPixelRed(image,ScaleCharToQuantum(*yy++),q);
682             SetPixelGreen(image,ScaleCharToQuantum(*c1++),q);
683             SetPixelBlue(image,ScaleCharToQuantum(*c2++),q);
684             q+=GetPixelChannels(image);
685           }
686           if (SyncAuthenticPixels(image,exception) == MagickFalse)
687             break;
688         }
689         image->colorspace=YCCColorspace;
690         if (LocaleCompare(image_info->magick,"PCDS") == 0)
691           SetImageColorspace(image,sRGBColorspace,exception);
692         if (j < (ssize_t) number_images)
693           {
694             /*
695               Allocate next image structure.
696             */
697             AcquireNextImage(image_info,image,exception);
698             if (GetNextImageInList(image) == (Image *) NULL)
699               {
700                 image=DestroyImageList(image);
701                 return((Image *) NULL);
702               }
703             image=SyncNextImageInList(image);
704           }
705         (void) SetImageProgressMonitor(image,progress_monitor,
706           image->client_data);
707         if (image->previous == (Image *) NULL)
708           {
709             status=SetImageProgress(image,LoadImageTag,j-1,number_images);
710             if (status == MagickFalse)
711               break;
712           }
713       }
714       chroma2=(unsigned char *) RelinquishMagickMemory(chroma2);
715       chroma1=(unsigned char *) RelinquishMagickMemory(chroma1);
716       luma=(unsigned char *) RelinquishMagickMemory(luma);
717       image=GetFirstImageInList(image);
718       overview_image=OverviewImage(image_info,image,exception);
719       return(overview_image);
720     }
721   /*
722     Read interleaved image.
723   */
724   yy=luma;
725   c1=chroma1;
726   c2=chroma2;
727   for (y=0; y < (ssize_t) height; y+=2)
728   {
729     count=ReadBlob(image,width,yy);
730     yy+=image->columns;
731     count=ReadBlob(image,width,yy);
732     yy+=image->columns;
733     count=ReadBlob(image,width >> 1,c1);
734     c1+=image->columns;
735     count=ReadBlob(image,width >> 1,c2);
736     c2+=image->columns;
737   }
738   if (scene >= 4)
739     {
740       /*
741         Recover luminance deltas for 1536x1024 image.
742       */
743       Upsample(768,512,image->columns,luma);
744       Upsample(384,256,image->columns,chroma1);
745       Upsample(384,256,image->columns,chroma2);
746       image->rows=1024;
747       for (i=0; i < (4*0x800); i++)
748         (void) ReadBlobByte(image);
749       status=DecodeImage(image,luma,chroma1,chroma2,exception);
750       if ((scene >= 5) && status)
751         {
752           /*
753             Recover luminance deltas for 3072x2048 image.
754           */
755           Upsample(1536,1024,image->columns,luma);
756           Upsample(768,512,image->columns,chroma1);
757           Upsample(768,512,image->columns,chroma2);
758           image->rows=2048;
759           offset=TellBlob(image)/0x800+12;
760           offset=SeekBlob(image,offset*0x800,SEEK_SET);
761           status=DecodeImage(image,luma,chroma1,chroma2,exception);
762           if ((scene >= 6) && (status != MagickFalse))
763             {
764               /*
765                 Recover luminance deltas for 6144x4096 image (vaporware).
766               */
767               Upsample(3072,2048,image->columns,luma);
768               Upsample(1536,1024,image->columns,chroma1);
769               Upsample(1536,1024,image->columns,chroma2);
770               image->rows=4096;
771             }
772         }
773     }
774   Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
775   Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
776   /*
777     Transfer luminance and chrominance channels.
778   */
779   yy=luma;
780   c1=chroma1;
781   c2=chroma2;
782   for (y=0; y < (ssize_t) image->rows; y++)
783   {
784     q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
785     if (q == (Quantum *) NULL)
786       break;
787     for (x=0; x < (ssize_t) image->columns; x++)
788     {
789       SetPixelRed(image,ScaleCharToQuantum(*yy++),q);
790       SetPixelGreen(image,ScaleCharToQuantum(*c1++),q);
791       SetPixelBlue(image,ScaleCharToQuantum(*c2++),q);
792       q+=GetPixelChannels(image);
793     }
794     if (SyncAuthenticPixels(image,exception) == MagickFalse)
795       break;
796     if (image->previous == (Image *) NULL)
797       {
798         status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
799                 image->rows);
800         if (status == MagickFalse)
801           break;
802       }
803   }
804   chroma2=(unsigned char *) RelinquishMagickMemory(chroma2);
805   chroma1=(unsigned char *) RelinquishMagickMemory(chroma1);
806   luma=(unsigned char *) RelinquishMagickMemory(luma);
807   if (EOFBlob(image) != MagickFalse)
808     ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
809       image->filename);
810   (void) CloseBlob(image);
811   if (image_info->ping == MagickFalse)
812     if ((rotate == 1) || (rotate == 3))
813       {
814         double
815           degrees;
816
817         Image
818           *rotate_image;
819
820         /*
821           Rotate image.
822         */
823         degrees=rotate == 1 ? -90.0 : 90.0;
824         rotate_image=RotateImage(image,degrees,exception);
825         if (rotate_image != (Image *) NULL)
826           {
827             image=DestroyImage(image);
828             image=rotate_image;
829           }
830       }
831   /*
832     Set CCIR 709 primaries with a D65 white point.
833   */
834   image->chromaticity.red_primary.x=0.6400f;
835   image->chromaticity.red_primary.y=0.3300f;
836   image->chromaticity.green_primary.x=0.3000f;
837   image->chromaticity.green_primary.y=0.6000f;
838   image->chromaticity.blue_primary.x=0.1500f;
839   image->chromaticity.blue_primary.y=0.0600f;
840   image->chromaticity.white_point.x=0.3127f;
841   image->chromaticity.white_point.y=0.3290f;
842   image->gamma=1.000f/2.200f;
843   image->colorspace=YCCColorspace;
844   if (LocaleCompare(image_info->magick,"PCDS") == 0)
845     SetImageColorspace(image,sRGBColorspace,exception);
846   return(GetFirstImageInList(image));
847 }
848 \f
849 /*
850 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851 %                                                                             %
852 %                                                                             %
853 %                                                                             %
854 %   R e g i s t e r P C D I m a g e                                           %
855 %                                                                             %
856 %                                                                             %
857 %                                                                             %
858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
859 %
860 %  RegisterPCDImage() adds attributes for the PCD image format to
861 %  the list of supported formats.  The attributes include the image format
862 %  tag, a method to read and/or write the format, whether the format
863 %  supports the saving of more than one frame to the same file or blob,
864 %  whether the format supports native in-memory I/O, and a brief
865 %  description of the format.
866 %
867 %  The format of the RegisterPCDImage method is:
868 %
869 %      size_t RegisterPCDImage(void)
870 %
871 */
872 ModuleExport size_t RegisterPCDImage(void)
873 {
874   MagickInfo
875     *entry;
876
877   entry=SetMagickInfo("PCD");
878   entry->decoder=(DecodeImageHandler *) ReadPCDImage;
879   entry->encoder=(EncodeImageHandler *) WritePCDImage;
880   entry->magick=(IsImageFormatHandler *) IsPCD;
881   entry->adjoin=MagickFalse;
882   entry->seekable_stream=MagickTrue;
883   entry->description=ConstantString("Photo CD");
884   entry->module=ConstantString("PCD");
885   (void) RegisterMagickInfo(entry);
886   entry=SetMagickInfo("PCDS");
887   entry->decoder=(DecodeImageHandler *) ReadPCDImage;
888   entry->encoder=(EncodeImageHandler *) WritePCDImage;
889   entry->adjoin=MagickFalse;
890   entry->seekable_stream=MagickTrue;
891   entry->description=ConstantString("Photo CD");
892   entry->module=ConstantString("PCD");
893   (void) RegisterMagickInfo(entry);
894   return(MagickImageCoderSignature);
895 }
896 \f
897 /*
898 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
899 %                                                                             %
900 %                                                                             %
901 %                                                                             %
902 %   U n r e g i s t e r P C D I m a g e                                       %
903 %                                                                             %
904 %                                                                             %
905 %                                                                             %
906 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
907 %
908 %  UnregisterPCDImage() removes format registrations made by the
909 %  PCD module from the list of supported formats.
910 %
911 %  The format of the UnregisterPCDImage method is:
912 %
913 %      UnregisterPCDImage(void)
914 %
915 */
916 ModuleExport void UnregisterPCDImage(void)
917 {
918   (void) UnregisterMagickInfo("PCD");
919   (void) UnregisterMagickInfo("PCDS");
920 }
921 \f
922 /*
923 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
924 %                                                                             %
925 %                                                                             %
926 %                                                                             %
927 %   W r i t e P C D I m a g e                                                 %
928 %                                                                             %
929 %                                                                             %
930 %                                                                             %
931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
932 %
933 %  WritePCDImage() writes an image in the Photo CD encoded image format.
934 %
935 %  The format of the WritePCDImage method is:
936 %
937 %      MagickBooleanType WritePCDImage(const ImageInfo *image_info,
938 %        Image *image,ExceptionInfo *exception)
939 %
940 %  A description of each parameter follows.
941 %
942 %    o image_info: the image info.
943 %
944 %    o image:  The image.
945 %
946 %    o exception: return any errors or warnings in this structure.
947 %
948 */
949
950 static MagickBooleanType WritePCDTile(Image *image,const char *page_geometry,
951   const char *tile_geometry,ExceptionInfo *exception)
952 {
953   GeometryInfo
954     geometry_info;
955
956   Image
957     *downsample_image,
958     *tile_image;
959
960   MagickBooleanType
961     status;
962
963   MagickStatusType
964     flags;
965
966   RectangleInfo
967     geometry;
968
969   register const Quantum
970     *p,
971     *q;
972
973   register ssize_t
974     i,
975     x;
976
977   ssize_t
978     y;
979
980   /*
981     Scale image to tile size.
982   */
983   SetGeometry(image,&geometry);
984   (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
985     &geometry.width,&geometry.height);
986   if ((geometry.width % 2) != 0)
987     geometry.width--;
988   if ((geometry.height % 2) != 0)
989     geometry.height--;
990   tile_image=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
991     exception);
992   if (tile_image == (Image *) NULL)
993     return(MagickFalse);
994   flags=ParseGeometry(page_geometry,&geometry_info);
995   geometry.width=(size_t) geometry_info.rho;
996   geometry.height=(size_t) geometry_info.sigma;
997   if ((flags & SigmaValue) == 0)
998     geometry.height=geometry.width;
999   if ((tile_image->columns != geometry.width) ||
1000       (tile_image->rows != geometry.height))
1001     {
1002       Image
1003         *bordered_image;
1004
1005       RectangleInfo
1006         border_info;
1007
1008       /*
1009         Put a border around the image.
1010       */
1011       border_info.width=(geometry.width-tile_image->columns+1) >> 1;
1012       border_info.height=(geometry.height-tile_image->rows+1) >> 1;
1013       bordered_image=BorderImage(tile_image,&border_info,image->compose,
1014         exception);
1015       if (bordered_image == (Image *) NULL)
1016         return(MagickFalse);
1017       tile_image=DestroyImage(tile_image);
1018       tile_image=bordered_image;
1019     }
1020   (void) TransformImage(&tile_image,(char *) NULL,tile_geometry,exception);
1021   (void) TransformImageColorspace(tile_image,YCCColorspace,exception);
1022   downsample_image=ResizeImage(tile_image,tile_image->columns/2,
1023     tile_image->rows/2,TriangleFilter,exception);
1024   if (downsample_image == (Image *) NULL)
1025     return(MagickFalse);
1026   /*
1027     Write tile to PCD file.
1028   */
1029   for (y=0; y < (ssize_t) tile_image->rows; y+=2)
1030   {
1031     p=GetVirtualPixels(tile_image,0,y,tile_image->columns,2,exception);
1032     if (p == (const Quantum *) NULL)
1033       break;
1034     for (x=0; x < (ssize_t) (tile_image->columns << 1); x++)
1035     {
1036       (void) WriteBlobByte(image,ScaleQuantumToChar(GetPixelRed(tile_image,p)));
1037       p+=GetPixelChannels(tile_image);
1038     }
1039     q=GetVirtualPixels(downsample_image,0,y >> 1,downsample_image->columns,1,
1040       exception);
1041     if (q == (Quantum *) NULL)
1042       break;
1043     for (x=0; x < (ssize_t) downsample_image->columns; x++)
1044     {
1045       (void) WriteBlobByte(image,ScaleQuantumToChar(
1046         GetPixelGreen(tile_image,q)));
1047       q++;
1048     }
1049     q=GetVirtualPixels(downsample_image,0,y >> 1,downsample_image->columns,1,
1050       exception);
1051     if (q == (Quantum *) NULL)
1052       break;
1053     for (x=0; x < (ssize_t) downsample_image->columns; x++)
1054     {
1055       (void) WriteBlobByte(image,ScaleQuantumToChar(
1056         GetPixelBlue(tile_image,q)));
1057       q++;
1058     }
1059     status=SetImageProgress(image,SaveImageTag,y,tile_image->rows);
1060     if (status == MagickFalse)
1061       break;
1062   }
1063   for (i=0; i < 0x800; i++)
1064     (void) WriteBlobByte(image,'\0');
1065   downsample_image=DestroyImage(downsample_image);
1066   tile_image=DestroyImage(tile_image);
1067   return(MagickTrue);
1068 }
1069
1070 static MagickBooleanType WritePCDImage(const ImageInfo *image_info,Image *image,
1071   ExceptionInfo *exception)
1072 {
1073   Image
1074     *pcd_image;
1075
1076   MagickBooleanType
1077     status;
1078
1079   register ssize_t
1080     i;
1081
1082   assert(image_info != (const ImageInfo *) NULL);
1083   assert(image_info->signature == MagickSignature);
1084   assert(image != (Image *) NULL);
1085   assert(image->signature == MagickSignature);
1086   if (image->debug != MagickFalse)
1087     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1088   pcd_image=image;
1089   if (image->columns < image->rows)
1090     {
1091       Image
1092         *rotate_image;
1093
1094       /*
1095         Rotate portrait to landscape.
1096       */
1097       rotate_image=RotateImage(image,90.0,exception);
1098       if (rotate_image == (Image *) NULL)
1099         return(MagickFalse);
1100       pcd_image=rotate_image;
1101     }
1102   /*
1103     Open output image file.
1104   */
1105   status=OpenBlob(image_info,pcd_image,WriteBinaryBlobMode,exception);
1106   if (status == MagickFalse)
1107     return(status);
1108   if (IssRGBCompatibleColorspace(pcd_image->colorspace) == MagickFalse)
1109     (void) TransformImageColorspace(pcd_image,sRGBColorspace,exception);
1110   /*
1111     Write PCD image header.
1112   */
1113   for (i=0; i < 32; i++)
1114     (void) WriteBlobByte(pcd_image,0xff);
1115   for (i=0; i < 4; i++)
1116     (void) WriteBlobByte(pcd_image,0x0e);
1117   for (i=0; i < 8; i++)
1118     (void) WriteBlobByte(pcd_image,'\0');
1119   for (i=0; i < 4; i++)
1120     (void) WriteBlobByte(pcd_image,0x01);
1121   for (i=0; i < 4; i++)
1122     (void) WriteBlobByte(pcd_image,0x05);
1123   for (i=0; i < 8; i++)
1124     (void) WriteBlobByte(pcd_image,'\0');
1125   for (i=0; i < 4; i++)
1126     (void) WriteBlobByte(pcd_image,0x0A);
1127   for (i=0; i < 36; i++)
1128     (void) WriteBlobByte(pcd_image,'\0');
1129   for (i=0; i < 4; i++)
1130     (void) WriteBlobByte(pcd_image,0x01);
1131   for (i=0; i < 1944; i++)
1132     (void) WriteBlobByte(pcd_image,'\0');
1133   (void) WriteBlob(pcd_image,7,(const unsigned char *) "PCD_IPI");
1134   (void) WriteBlobByte(pcd_image,0x06);
1135   for (i=0; i < 1530; i++)
1136     (void) WriteBlobByte(pcd_image,'\0');
1137   if (image->columns < image->rows)
1138     (void) WriteBlobByte(pcd_image,'\1');
1139   else
1140     (void) WriteBlobByte(pcd_image,'\0');
1141   for (i=0; i < (3*0x800-1539); i++)
1142     (void) WriteBlobByte(pcd_image,'\0');
1143   /*
1144     Write PCD tiles.
1145   */
1146   status=WritePCDTile(pcd_image,"768x512>","192x128",exception);
1147   status=WritePCDTile(pcd_image,"768x512>","384x256",exception);
1148   status=WritePCDTile(pcd_image,"768x512>","768x512",exception);
1149   (void) CloseBlob(pcd_image);
1150   if (pcd_image != image)
1151     pcd_image=DestroyImage(pcd_image);
1152   return(status);
1153 }