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