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