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