Coverage Report

Created: 2026-11-12 07:35

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