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