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