root / ImageMagick / trunk / coders / pcd.c

Revision 12035, 34.7 kB (checked in by cristy, 2 weeks ago)
Line 
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-2008 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 "magick/studio.h"
43#include "magick/property.h"
44#include "magick/blob.h"
45#include "magick/blob-private.h"
46#include "magick/client.h"
47#include "magick/colorspace.h"
48#include "magick/constitute.h"
49#include "magick/decorate.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/gem.h"
53#include "magick/geometry.h"
54#include "magick/image.h"
55#include "magick/image-private.h"
56#include "magick/list.h"
57#include "magick/magick.h"
58#include "magick/memory_.h"
59#include "magick/monitor.h"
60#include "magick/monitor-private.h"
61#include "magick/montage.h"
62#include "magick/resize.h"
63#include "magick/shear.h"
64#include "magick/quantum-private.h"
65#include "magick/static.h"
66#include "magick/string_.h"
67#include "magick/module.h"
68#include "magick/transform.h"
69#include "magick/utility.h"
70
71/*
72  Forward declarations.
73*/
74static MagickBooleanType
75  WritePCDImage(const ImageInfo *,Image *);
76
77/*
78%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79%                                                                             %
80%                                                                             %
81%                                                                             %
82%   D e c o d e I m a g e                                                     %
83%                                                                             %
84%                                                                             %
85%                                                                             %
86%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87%
88%  DecodeImage recovers the Huffman encoded luminance and chrominance
89%  deltas.
90%
91%  The format of the DecodeImage method is:
92%
93%      MagickBooleanType DecodeImage(Image *image,unsigned char *luma,
94%        unsigned char *chroma1,unsigned char *chroma2)
95%
96%  A description of each parameter follows:
97%
98%    o image: the address of a structure of type Image.
99%
100%    o luma: the address of a character buffer that contains the
101%      luminance information.
102%
103%    o chroma1: the address of a character buffer that contains the
104%      chrominance information.
105%
106%    o chroma2: the address of a character buffer that contains the
107%      chrominance information.
108%
109*/
110static MagickBooleanType DecodeImage(Image *image,unsigned char *luma,
111  unsigned char *chroma1,unsigned char *chroma2)
112{
113#define IsSync  ((sum & 0xffffff00UL) == 0xfffffe00UL)
114#define PCDGetBits(n) \
115{  \
116  sum=(sum << n) & 0xffffffff; \
117  bits-=n; \
118  while (bits <= 24) \
119  { \
120    if (p >= (buffer+0x800)) \
121      { \
122        count=ReadBlob(image,0x800,buffer); \
123        p=buffer; \
124      } \
125    sum|=((unsigned int) (*p) << (24-bits)); \
126    bits+=8; \
127    p++; \
128  } \
129  if (EOFBlob(image) != MagickFalse) \
130    break; \
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  long
147    quantum;
148
149  PCDTable
150    *pcd_table[3];
151
152  register long
153    i,
154    j;
155
156  register PCDTable
157    *r;
158
159  register unsigned char
160    *p,
161    *q;
162
163  size_t
164    length;
165
166  ssize_t
167    count;
168
169  unsigned char
170    *buffer;
171
172  unsigned long
173    bits,
174    plane,
175    pcd_length[3],
176    row,
177    sum;
178
179  /*
180    Initialize Huffman tables.
181  */
182  assert(image != (const Image *) NULL);
183  assert(image->signature == MagickSignature);
184  if (image->debug != MagickFalse)
185    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
186  assert(luma != (unsigned char *) NULL);
187  assert(chroma1 != (unsigned char *) NULL);
188  assert(chroma2 != (unsigned char *) NULL);
189  buffer=(unsigned char *) AcquireQuantumMemory(0x800,sizeof(*buffer));
190  if (buffer == (unsigned char *) NULL)
191    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
192      image->filename);
193  sum=0;
194  bits=32;
195  p=buffer+0x800;
196  for (i=0; i < (image->columns > 1536 ? 3 : 1); i++)
197  {
198    PCDGetBits(8);
199    length=(sum & 0xff)+1;
200    pcd_table[i]=(PCDTable *) AcquireQuantumMemory(length,
201      sizeof(*pcd_table[i]));
202    if (pcd_table[i] == (PCDTable *) NULL)
203      {
204        buffer=(unsigned char *) RelinquishMagickMemory(buffer);
205        ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
206          image->filename);
207      }
208    r=pcd_table[i];
209    for (j=0; j < (long) length; j++)
210    {
211      PCDGetBits(8);
212      r->length=(unsigned int) (sum & 0xff)+1;
213      if (r->length > 16)
214        {
215          buffer=(unsigned char *) RelinquishMagickMemory(buffer);
216          return(MagickFalse);
217        }
218      PCDGetBits(16);
219      r->sequence=(unsigned int) (sum & 0xffff) << 16;
220      PCDGetBits(8);
221      r->key=(unsigned char) (sum & 0xff);
222      r->mask=(~((1U << (32-r->length))-1));
223      r++;
224    }
225    pcd_length[i]=(unsigned long) length;
226  }
227  /*
228    Search for Sync byte.
229  */
230  for (i=0; i < 1; i++)
231    PCDGetBits(16);
232  for (i=0; i < 1; i++)
233    PCDGetBits(16);
234  while ((sum & 0x00fff000UL) != 0x00fff000UL)
235    PCDGetBits(8);
236  while (IsSync == 0)
237    PCDGetBits(1);
238  /*
239    Recover the Huffman encoded luminance and chrominance deltas.
240  */
241  count=0;
242  length=0;
243  plane=0;
244  row=0;
245  q=luma;
246  for ( ; ; )
247  {
248    if (IsSync != 0)
249      {
250        /*
251          Determine plane and row number.
252        */
253        PCDGetBits(16);
254        row=((sum >> 9) & 0x1fff);
255        if (row == image->rows)
256          break;
257        PCDGetBits(8);
258        plane=sum >> 30;
259        PCDGetBits(16);
260        switch (plane)
261        {
262          case 0:
263          {
264            q=luma+row*image->columns;
265            count=(ssize_t) image->columns;
266            break;
267          }
268          case 2:
269          {
270            q=chroma1+(row >> 1)*image->columns;
271            count=(ssize_t) (image->columns >> 1);
272            plane--;
273            break;
274          }
275          case 3:
276          {
277            q=chroma2+(row >> 1)*image->columns;
278            count=(ssize_t) (image->columns >> 1);
279            plane--;
280            break;
281          }
282          default:
283          {
284            ThrowBinaryException(CorruptImageError,"CorruptImage",
285              image->filename);
286          }
287        }
288        length=pcd_length[plane];
289        continue;
290      }
291    /*
292      Decode luminance or chrominance deltas.
293    */
294    r=pcd_table[plane];
295    for (i=0; ((i < (long) length) && ((sum & r->mask) != r->sequence)); i++)
296      r++;
297    if ((row > image->rows) || (r == (PCDTable *) NULL) ||
298        ((size_t) (q-luma) > (image->columns*image->rows)))
299      {
300        (void) ThrowMagickException(&image->exception,GetMagickModule(),
301          CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
302        while ((sum & 0x00fff000) != 0x00fff000)
303          PCDGetBits(8);
304        while (IsSync == 0)
305          PCDGetBits(1);
306        continue;
307      }
308    if (r->key < 128)
309      quantum=(long) (*q)+r->key;
310    else
311      quantum=(long) (*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: This string is generally the first few bytes of an image file
347%      or blob.
348%
349%    o length: Specifies the length of the magick string.
350%
351%
352*/
353static MagickBooleanType IsPCD(const unsigned char *magick,const size_t length)
354{
355  if (length < 2052)
356    return(MagickFalse);
357  if (LocaleNCompare((char *) magick+2048,"PCD_",4) == 0)
358    return(MagickTrue);
359  return(MagickFalse);
360}
361
362/*
363%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
364%                                                                             %
365%                                                                             %
366%                                                                             %
367%   R e a d P C D I m a g e                                                   %
368%                                                                             %
369%                                                                             %
370%                                                                             %
371%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
372%
373%  ReadPCDImage() reads a Photo CD image file and returns it.  It
374%  allocates the memory necessary for the new Image structure and returns a
375%  pointer to the new image.  Much of the PCD decoder was derived from
376%  the program hpcdtoppm(1) by Hadmut Danisch.
377%
378%  The format of the ReadPCDImage method is:
379%
380%      image=ReadPCDImage(image_info)
381%
382%  A description of each parameter follows:
383%
384%    o image_info: the image info.
385%
386%    o exception: return any errors or warnings in this structure.
387%
388*/
389
390static inline size_t MagickMin(const size_t x,const size_t y)
391{
392  if (x < y)
393    return(x);
394  return(y);
395}
396
397static Image *OverviewImage(const ImageInfo *image_info,Image *image,
398  ExceptionInfo *exception)
399{
400  Image
401    *montage_image;
402
403  MontageInfo
404    *montage_info;
405
406  register Image
407    *p;
408
409  /*
410    Create the PCD Overview image.
411  */
412  for (p=image; p != (Image *) NULL; p=p->next)
413  {
414    (void) DeleteImageProperty(p,"label");
415    (void) SetImageProperty(p,"label",DefaultTileLabel);
416  }
417  montage_info=CloneMontageInfo(image_info,(MontageInfo *) NULL);
418  (void) CopyMagickString(montage_info->filename,image_info->filename,
419    MaxTextExtent);
420  montage_image=MontageImageList(image_info,montage_info,image,exception);
421  montage_info=DestroyMontageInfo(montage_info);
422  if (montage_image == (Image *) NULL)
423    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
424  image=DestroyImage(image);
425  return(montage_image);
426}
427
428static void Upsample(const unsigned long width,const unsigned long height,
429  const unsigned long scaled_width,unsigned char *pixels)
430{
431  register long
432    x,
433    y;
434
435  register unsigned char
436    *p,
437    *q,
438    *r;
439
440  /*
441    Create a new image that is a integral size greater than an existing one.
442  */
443  assert(pixels != (unsigned char *) NULL);
444  for (y=0; y < (long) height; y++)
445  {
446    p=pixels+(height-1-y)*scaled_width+(width-1);
447    q=pixels+((height-1-y) << 1)*scaled_width+((width-1) << 1);
448    *q=(*p);
449    *(q+1)=(*(p));
450    for (x=1; x < (long) width; x++)
451    {
452      p--;
453      q-=2;
454      *q=(*p);
455      *(q+1)=(unsigned char) ((((unsigned long) *p)+
456        ((unsigned long) *(p+1))+1) >> 1);
457    }
458  }
459  for (y=0; y < (long) (height-1); y++)
460  {
461    p=pixels+((unsigned long) y << 1)*scaled_width;
462    q=p+scaled_width;
463    r=q+scaled_width;
464    for (x=0; x < (long) (width-1); x++)
465    {
466      *q=(unsigned char) ((((unsigned long) *p)+((unsigned long) *r)+1) >> 1);
467      *(q+1)=(unsigned char) ((((unsigned long) *p)+((unsigned long) *(p+2))+
468        ((unsigned long) *r)+((unsigned long) *(r+2))+2) >> 2);
469      q+=2;
470      p+=2;
471      r+=2;
472    }
473    *q++=(unsigned char) ((((unsigned long) *p++)+
474      ((unsigned long) *r++)+1) >> 1);
475    *q++=(unsigned char) ((((unsigned long) *p++)+
476      ((unsigned long) *r++)+1) >> 1);
477  }
478  p=pixels+(2*height-2)*scaled_width;
479  q=pixels+(2*height-1)*scaled_width;
480  (void) CopyMagickMemory(q,p,(size_t) (2*width));
481}
482
483static Image *ReadPCDImage(const ImageInfo *image_info,ExceptionInfo *exception)
484{
485  Image
486    *image;
487
488  long
489    x;
490
491  MagickBooleanType
492    status;
493
494  MagickOffsetType
495    offset;
496
497  MagickSizeType
498    number_pixels;
499
500  register long
501    i,
502    y;
503
504  register PixelPacket
505    *q;
506
507  register unsigned char
508    *c1,
509    *c2,
510    *yy;
511
512  ssize_t
513    count;
514
515  unsigned char
516    *chroma1,
517    *chroma2,
518    *header,
519    *luma;
520
521  unsigned int
522    overview;
523
524  unsigned long
525    height,
526    number_images,
527    rotate,
528    scene,
529    width;
530
531  /*
532    Open image file.
533  */
534  assert(image_info != (const ImageInfo *) NULL);
535  assert(image_info->signature == MagickSignature);
536  if (image_info->debug != MagickFalse)
537    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
538      image_info->filename);
539  assert(exception != (ExceptionInfo *) NULL);
540  assert(exception->signature == MagickSignature);
541  image=AcquireImage(image_info);
542  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
543  if (status == MagickFalse)
544    {
545      image=DestroyImageList(image);
546      return((Image *) NULL);
547    }
548  /*
549    Determine if this a PCD file.
550  */
551  header=(unsigned char *) AcquireQuantumMemory(0x800,3UL*sizeof(*header));
552  if (header == (unsigned char *) NULL)
553    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
554  count=ReadBlob(image,3*0x800,header);
555  overview=LocaleNCompare((char *) header,"PCD_OPA",7) == 0;
556  if ((count == 0) ||
557      ((LocaleNCompare((char *) header+0x800,"PCD",3) != 0) && !overview))
558    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
559  rotate=header[0x0e02] & 0x03;
560  number_images=(header[10] << 8) | header[11];
561  header=(unsigned char *) RelinquishMagickMemory(header);
562  /*
563    Determine resolution by scene specification.
564  */
565  if ((image->columns == 0) || (image->rows == 0))
566    scene=3;
567  else
568    {
569      width=192;
570      height=128;
571      for (scene=1; scene < 6; scene++)
572      {
573        if ((width >= image->columns) && (height >= image->rows))
574          break;
575        width<<=1;
576        height<<=1;
577      }
578    }
579  if (image_info->number_scenes != 0)
580    scene=(unsigned long) MagickMin(image_info->scene,6);
581  if (overview)
582    scene=1;
583  /*
584    Initialize image structure.
585  */
586  width=192;
587  height=128;
588  for (i=1; i < (long) MagickMin(scene,3); i++)
589  {
590    width<<=1;
591    height<<=1;
592  }
593  image->columns=width;
594  image->rows=height;
595  image->depth=8;
596  for ( ; i < (long) scene; i++)
597  {
598    image->columns<<=1;
599    image->rows<<=1;
600  }
601  /*
602    Allocate lumand chroma memory.
603  */
604  if (SetImageExtent(image,0,0) == MagickFalse)
605    {
606      InheritException(exception,&image->exception);
607      return(DestroyImageList(image));
608    }
609  number_pixels=(MagickSizeType) image->columns*image->rows;
610  if (number_pixels != (size_t) number_pixels)
611    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
612  chroma1=(unsigned char *) AcquireQuantumMemory(image->columns+1UL,image->rows*
613    sizeof(*chroma1));
614  chroma2=(unsigned char *) AcquireQuantumMemory(image->columns+1UL,image->rows*
615    sizeof(*chroma2));
616  luma=(unsigned char *) AcquireQuantumMemory(image->columns+1UL,image->rows*
617    sizeof(*luma));
618  if ((chroma1 == (unsigned char *) NULL) ||
619      (chroma2 == (unsigned char *) NULL) || (luma == (unsigned char *) NULL))
620    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
621  /*
622    Advance to image data.
623  */
624  offset=93;
625  if (overview)
626    offset=2;
627  else
628    if (scene == 2)
629      offset=20;
630    else
631      if (scene <= 1)
632        offset=1;
633  for (i=0; i < (long) (offset*0x800); i++)
634    (void) ReadBlobByte(image);
635  if (overview)
636    {
637      Image
638        *overview_image;
639
640      MagickProgressMonitor
641        progress_monitor;
642
643      register long
644        j;
645
646      /*
647        Read thumbnails from overview image.
648      */
649      for (j=1; j <= (long) number_images; j++)
650      {
651        progress_monitor=SetImageProgressMonitor(image,
652          (MagickProgressMonitor) NULL,image->client_data);
653        (void) FormatMagickString(image->filename,MaxTextExtent,
654          "images/img%04ld.pcd",j);
655        (void) FormatMagickString(image->magick_filename,MaxTextExtent,
656          "images/img%04ld.pcd",j);
657        image->scene=(unsigned long) j;
658        image->columns=width;
659        image->rows=height;
660        image->depth=8;
661        yy=luma;
662        c1=chroma1;
663        c2=chroma2;
664        for (y=0; y < (long) height; y+=2)
665        {
666          count=ReadBlob(image,width,yy);
667          yy+=image->columns;
668          count=ReadBlob(image,width,yy);
669          yy+=image->columns;
670          count=ReadBlob(image,width >> 1,c1);
671          c1+=image->columns;
672          count=ReadBlob(image,width >> 1,c2);
673          c2+=image->columns;
674        }
675        Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
676        Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
677        /*
678          Transfer luminance and chrominance channels.
679        */
680        yy=luma;
681        c1=chroma1;
682        c2=chroma2;
683        for (y=0; y < (long) image->rows; y++)
684        {
685          q=SetImagePixels(image,0,y,image->columns,1);
686          if (q == (PixelPacket *) NULL)
687            break;
688          for (x=0; x < (long) image->columns; x++)
689          {
690            q->red=ScaleCharToQuantum(*yy++);
691            q->green=ScaleCharToQuantum(*c1++);
692            q->blue=ScaleCharToQuantum(*c2++);
693            q++;
694          }
695          if (SyncImagePixels(image) == MagickFalse)
696            break;
697        }
698        image->colorspace=YCCColorspace;
699        if (LocaleCompare(image_info->magick,"PCDS") == 0)
700          image->colorspace=sRGBColorspace;
701        if (j < (long) number_images)
702          {
703            /*
704              Allocate next image structure.
705            */
706            AcquireNextImage(image_info,image);
707            if (GetNextImageInList(image) == (Image *) NULL)
708              {
709                image=DestroyImageList(image);
710                return((Image *) NULL);
711              }
712            image=SyncNextImageInList(image);
713          }
714        (void) SetImageProgressMonitor(image,progress_monitor,
715          image->client_data);
716        if (image->previous == (Image *) NULL)
717          {
718            status=SetImageProgress(image,LoadImageTag,j-1,number_images);
719            if (status == MagickFalse)
720              break;
721          }
722      }
723      chroma2=(unsigned char *) RelinquishMagickMemory(chroma2);
724      chroma1=(unsigned char *) RelinquishMagickMemory(chroma1);
725      luma=(unsigned char *) RelinquishMagickMemory(luma);
726      image=GetFirstImageInList(image);
727      overview_image=OverviewImage(image_info,image,exception);
728      return(overview_image);
729    }
730  /*
731    Read interleaved image.
732  */
733  yy=luma;
734  c1=chroma1;
735  c2=chroma2;
736  for (y=0; y < (long) height; y+=2)
737  {
738    count=ReadBlob(image,width,yy);
739    yy+=image->columns;
740    count=ReadBlob(image,width,yy);
741    yy+=image->columns;
742    count=ReadBlob(image,width >> 1,c1);
743    c1+=image->columns;
744    count=ReadBlob(image,width >> 1,c2);
745    c2+=image->columns;
746  }
747  if (scene >= 4)
748    {
749      /*
750        Recover luminance deltas for 1536x1024 image.
751      */
752      Upsample(768,512,image->columns,luma);
753      Upsample(384,256,image->columns,chroma1);
754      Upsample(384,256,image->columns,chroma2);
755      image->rows=1024;
756      for (i=0; i < (4*0x800); i++)
757        (void) ReadBlobByte(image);
758      status=DecodeImage(image,luma,chroma1,chroma2);
759      if ((scene >= 5) && status)
760        {
761          /*
762            Recover luminance deltas for 3072x2048 image.
763          */
764          Upsample(1536,1024,image->columns,luma);
765          Upsample(768,512,image->columns,chroma1);
766          Upsample(768,512,image->columns,chroma2);
767          image->rows=2048;
768          offset=TellBlob(image)/0x800+12;
769          offset=SeekBlob(image,offset*0x800,SEEK_SET);
770          status=DecodeImage(image,luma,chroma1,chroma2);
771          if ((scene >= 6) && (status != MagickFalse))
772            {
773              /*
774                Recover luminance deltas for 6144x4096 image (vaporware).
775              */
776              Upsample(3072,2048,image->columns,luma);
777              Upsample(1536,1024,image->columns,chroma1);
778              Upsample(1536,1024,image->columns,chroma2);
779              image->rows=4096;
780            }
781        }
782    }
783  Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
784  Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
785  /*
786    Transfer luminance and chrominance channels.
787  */
788  yy=luma;
789  c1=chroma1;
790  c2=chroma2;
791  for (y=0; y < (long) image->rows; y++)
792  {
793    q=SetImagePixels(image,0,y,image->columns,1);
794    if (q == (PixelPacket *) NULL)
795      break;
796    for (x=0; x < (long) image->columns; x++)
797    {
798      q->red=ScaleCharToQuantum(*yy++);
799      q->green=ScaleCharToQuantum(*c1++);
800      q->blue=ScaleCharToQuantum(*c2++);
801      q++;
802    }
803    if (SyncImagePixels(image) == MagickFalse)
804      break;
805    if (image->previous == (Image *) NULL)
806      {
807        status=SetImageProgress(image,LoadImageTag,y,image->rows);
808        if (status == MagickFalse)
809          break;
810      }
811  }
812  chroma2=(unsigned char *) RelinquishMagickMemory(chroma2);
813  chroma1=(unsigned char *) RelinquishMagickMemory(chroma1);
814  luma=(unsigned char *) RelinquishMagickMemory(luma);
815  if (EOFBlob(image) != MagickFalse)
816    ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
817      image->filename);
818  (void) CloseBlob(image);
819  if (image_info->ping == MagickFalse)
820    if ((rotate == 1) || (rotate == 3))
821      {
822        double
823          degrees;
824
825        Image
826          *rotate_image;
827
828        /*
829          Rotate image.
830        */
831        degrees=rotate == 1 ? -90.0 : 90.0;
832        rotate_image=RotateImage(image,degrees,exception);
833        if (rotate_image != (Image *) NULL)
834          {
835            image=DestroyImage(image);
836            image=rotate_image;
837          }
838      }
839  /*
840    Set CCIR 709 primaries with a D65 white point.
841  */
842  image->chromaticity.red_primary.x=0.6400f;
843  image->chromaticity.red_primary.y=0.3300f;
844  image->chromaticity.green_primary.x=0.3000f;
845  image->chromaticity.green_primary.y=0.6000f;
846  image->chromaticity.blue_primary.x=0.1500f;
847  image->chromaticity.blue_primary.y=0.0600f;
848  image->chromaticity.white_point.x=0.3127f;
849  image->chromaticity.white_point.y=0.3290f;
850  image->gamma=1.000f/2.200f;
851  image->colorspace=YCCColorspace;
852  if (LocaleCompare(image_info->magick,"PCDS") == 0)
853    image->colorspace=sRGBColorspace;
854  return(GetFirstImageInList(image));
855}
856
857/*
858%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
859%                                                                             %
860%                                                                             %
861%                                                                             %
862%   R e g i s t e r P C D I m a g e                                           %
863%                                                                             %
864%                                                                             %
865%                                                                             %
866%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
867%
868%  RegisterPCDImage() adds attributes for the PCD image format to
869%  the list of supported formats.  The attributes include the image format
870%  tag, a method to read and/or write the format, whether the format
871%  supports the saving of more than one frame to the same file or blob,
872%  whether the format supports native in-memory I/O, and a brief
873%  description of the format.
874%
875%  The format of the RegisterPCDImage method is:
876%
877%      unsigned long RegisterPCDImage(void)
878%
879*/
880ModuleExport unsigned long RegisterPCDImage(void)
881{
882  MagickInfo
883    *entry;
884
885  entry=SetMagickInfo("PCD");
886  entry->decoder=(DecodeImageHandler *) ReadPCDImage;
887  entry->encoder=(EncodeImageHandler *) WritePCDImage;
888  entry->magick=(IsImageFormatHandler *) IsPCD;
889  entry->adjoin=MagickFalse;
890  entry->description=ConstantString("Photo CD");
891  entry->module=ConstantString("PCD");
892  (void) RegisterMagickInfo(entry);
893  entry=SetMagickInfo("PCDS");
894  entry->decoder=(DecodeImageHandler *) ReadPCDImage;
895  entry->encoder=(EncodeImageHandler *) WritePCDImage;
896  entry->adjoin=MagickFalse;
897  entry->description=ConstantString("Photo CD");
898  entry->module=ConstantString("PCD");
899  (void) RegisterMagickInfo(entry);
900  return(MagickImageCoderSignature);
901}
902
903/*
904%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
905%                                                                             %
906%                                                                             %
907%                                                                             %
908%   U n r e g i s t e r P C D I m a g e                                       %
909%                                                                             %
910%                                                                             %
911%                                                                             %
912%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
913%
914%  UnregisterPCDImage() removes format registrations made by the
915%  PCD module from the list of supported formats.
916%
917%  The format of the UnregisterPCDImage method is:
918%
919%      UnregisterPCDImage(void)
920%
921*/
922ModuleExport void UnregisterPCDImage(void)
923{
924  (void) UnregisterMagickInfo("PCD");
925  (void) UnregisterMagickInfo("PCDS");
926}
927
928/*
929%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
930%                                                                             %
931%                                                                             %
932%                                                                             %
933%   W r i t e P C D I m a g e                                                 %
934%                                                                             %
935%                                                                             %
936%                                                                             %
937%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
938%
939%  WritePCDImage() writes an image in the Photo CD encoded image
940%  format.
941%
942%  The format of the WritePCDImage method is:
943%
944%      MagickBooleanType WritePCDImage(const ImageInfo *image_info,Image *image)
945%
946%  A description of each parameter follows.
947%
948%    o image_info: the image info.
949%
950%    o image:  The image.
951%
952%
953*/
954
955static MagickBooleanType WritePCDTile(Image *image,const char *page_geometry,
956  const char *tile_geometry)
957{
958  GeometryInfo
959    geometry_info;
960
961  Image
962    *downsample_image,
963    *tile_image;
964
965  long
966    y;
967
968  MagickBooleanType
969    status;
970
971  MagickStatusType
972    flags;
973
974  RectangleInfo
975    geometry;
976
977  register const PixelPacket
978    *p,
979    *q;
980
981  register long
982    i,
983    x;
984
985  /*
986    Scale image to tile size.
987  */
988  SetGeometry(image,&geometry);
989  (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
990    &geometry.width,&geometry.height);
991  if ((geometry.width % 2) != 0)
992    geometry.width--;
993  if ((geometry.height % 2) != 0)
994    geometry.height--;
995  tile_image=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
996    1.0,&image->exception);
997  if (tile_image == (Image *) NULL)
998    return(MagickFalse);
999  flags=ParseGeometry(page_geometry,&geometry_info);
1000  geometry.width=(unsigned long) geometry_info.rho;
1001  geometry.height=(unsigned long) geometry_info.sigma;
1002  if ((flags & SigmaValue) == 0)
1003    geometry.height=geometry.width;
1004  if ((tile_image->columns != geometry.width) ||
1005      (tile_image->rows != geometry.height))
1006    {
1007      Image
1008        *bordered_image;
1009
1010      RectangleInfo
1011        border_info;
1012
1013      /*
1014        Put a border around the image.
1015      */
1016      border_info.width=(geometry.width-tile_image->columns+1) >> 1;
1017      border_info.height=(geometry.height-tile_image->rows+1) >> 1;
1018      bo