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-2016 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
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
73/*
74  Forward declarations.
75*/
76static MagickBooleanType
77  WritePCDImage(const ImageInfo *,Image *,ExceptionInfo *);
78
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*/
112static 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
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*/
351static 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
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*/
387static 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
418static 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
470static 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
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*/
866ModuleExport 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
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*/
906ModuleExport void UnregisterPCDImage(void)
907{
908  (void) UnregisterMagickInfo("PCD");
909  (void) UnregisterMagickInfo("PCDS");
910}
911
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
940static 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
1072static 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}
1156