blob: 70f5926aaebc804bc812a7acdd0e86b739eadf46 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP CCCC DDDD %
7% P P C D D %
8% PPPP C D D %
9% P C D D %
10% P CCCC DDDD %
11% %
12% %
13% Read/Write Photo CD Image Format %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/property.h"
44#include "magick/blob.h"
45#include "magick/blob-private.h"
46#include "magick/cache.h"
47#include "magick/client.h"
48#include "magick/colorspace.h"
49#include "magick/constitute.h"
50#include "magick/decorate.h"
51#include "magick/exception.h"
52#include "magick/exception-private.h"
53#include "magick/gem.h"
54#include "magick/geometry.h"
55#include "magick/image.h"
56#include "magick/image-private.h"
57#include "magick/list.h"
58#include "magick/magick.h"
59#include "magick/memory_.h"
60#include "magick/monitor.h"
61#include "magick/monitor-private.h"
62#include "magick/montage.h"
63#include "magick/resize.h"
64#include "magick/shear.h"
65#include "magick/quantum-private.h"
66#include "magick/static.h"
67#include "magick/string_.h"
68#include "magick/module.h"
69#include "magick/transform.h"
70#include "magick/utility.h"
71
72/*
73 Forward declarations.
74*/
75static MagickBooleanType
76 WritePCDImage(const ImageInfo *,Image *);
77
78/*
79%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80% %
81% %
82% %
83% D e c o d e I m a g e %
84% %
85% %
86% %
87%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88%
89% DecodeImage recovers the Huffman encoded luminance and chrominance
90% deltas.
91%
92% The format of the DecodeImage method is:
93%
94% MagickBooleanType DecodeImage(Image *image,unsigned char *luma,
95% unsigned char *chroma1,unsigned char *chroma2)
96%
97% A description of each parameter follows:
98%
99% o image: the address of a structure of type Image.
100%
101% o luma: the address of a character buffer that contains the
102% luminance information.
103%
104% o chroma1: the address of a character buffer that contains the
105% chrominance information.
106%
107% o chroma2: the address of a character buffer that contains the
108% chrominance information.
109%
110*/
111static MagickBooleanType DecodeImage(Image *image,unsigned char *luma,
112 unsigned char *chroma1,unsigned char *chroma2)
113{
114#define IsSync ((sum & 0xffffff00UL) == 0xfffffe00UL)
115#define PCDGetBits(n) \
116{ \
117 sum=(sum << n) & 0xffffffff; \
118 bits-=n; \
119 while (bits <= 24) \
120 { \
121 if (p >= (buffer+0x800)) \
122 { \
123 count=ReadBlob(image,0x800,buffer); \
124 p=buffer; \
125 } \
126 sum|=((unsigned int) (*p) << (24-bits)); \
127 bits+=8; \
128 p++; \
129 } \
130 if (EOFBlob(image) != MagickFalse) \
131 break; \
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
cristybb503372010-05-27 20:51:26 +0000147 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000148 quantum;
149
150 PCDTable
151 *pcd_table[3];
152
cristybb503372010-05-27 20:51:26 +0000153 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000154 i,
155 j;
156
157 register PCDTable
158 *r;
159
160 register unsigned char
161 *p,
162 *q;
163
164 size_t
165 length;
166
167 ssize_t
168 count;
169
170 unsigned char
171 *buffer;
172
cristybb503372010-05-27 20:51:26 +0000173 size_t
cristy3ed852e2009-09-05 21:47:34 +0000174 bits,
175 plane,
176 pcd_length[3],
177 row,
178 sum;
179
180 /*
181 Initialize Huffman tables.
182 */
183 assert(image != (const Image *) NULL);
184 assert(image->signature == MagickSignature);
185 if (image->debug != MagickFalse)
186 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
187 assert(luma != (unsigned char *) NULL);
188 assert(chroma1 != (unsigned char *) NULL);
189 assert(chroma2 != (unsigned char *) NULL);
190 buffer=(unsigned char *) AcquireQuantumMemory(0x800,sizeof(*buffer));
191 if (buffer == (unsigned char *) NULL)
192 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
193 image->filename);
194 sum=0;
195 bits=32;
196 p=buffer+0x800;
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];
cristybb503372010-05-27 20:51:26 +0000210 for (j=0; j < (ssize_t) length; j++)
cristy3ed852e2009-09-05 21:47:34 +0000211 {
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 }
cristybb503372010-05-27 20:51:26 +0000226 pcd_length[i]=(size_t) length;
cristy3ed852e2009-09-05 21:47:34 +0000227 }
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 == 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 != 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];
cristybb503372010-05-27 20:51:26 +0000296 for (i=0; ((i < (ssize_t) length) && ((sum & r->mask) != r->sequence)); i++)
cristy3ed852e2009-09-05 21:47:34 +0000297 r++;
cristy5d418802009-11-27 19:06:12 +0000298 if ((row > image->rows) || (r == (PCDTable *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000299 {
300 (void) ThrowMagickException(&image->exception,GetMagickModule(),
301 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
302 while ((sum & 0x00fff000) != 0x00fff000)
303 PCDGetBits(8);
304 while (IsSync == 0)
305 PCDGetBits(1);
306 continue;
307 }
308 if (r->key < 128)
cristybb503372010-05-27 20:51:26 +0000309 quantum=(ssize_t) (*q)+r->key;
cristy3ed852e2009-09-05 21:47:34 +0000310 else
cristybb503372010-05-27 20:51:26 +0000311 quantum=(ssize_t) (*q)+r->key-256;
cristy3ed852e2009-09-05 21:47:34 +0000312 *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*/
351static 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*/
387
388static inline size_t MagickMin(const size_t x,const size_t y)
389{
390 if (x < y)
391 return(x);
392 return(y);
393}
394
395static Image *OverviewImage(const ImageInfo *image_info,Image *image,
396 ExceptionInfo *exception)
397{
398 Image
399 *montage_image;
400
401 MontageInfo
402 *montage_info;
403
404 register Image
405 *p;
406
407 /*
408 Create the PCD Overview image.
409 */
410 for (p=image; p != (Image *) NULL; p=p->next)
411 {
412 (void) DeleteImageProperty(p,"label");
413 (void) SetImageProperty(p,"label",DefaultTileLabel);
414 }
415 montage_info=CloneMontageInfo(image_info,(MontageInfo *) NULL);
416 (void) CopyMagickString(montage_info->filename,image_info->filename,
417 MaxTextExtent);
418 montage_image=MontageImageList(image_info,montage_info,image,exception);
419 montage_info=DestroyMontageInfo(montage_info);
420 if (montage_image == (Image *) NULL)
421 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
422 image=DestroyImage(image);
423 return(montage_image);
424}
425
cristybb503372010-05-27 20:51:26 +0000426static void Upsample(const size_t width,const size_t height,
427 const size_t scaled_width,unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +0000428{
cristybb503372010-05-27 20:51:26 +0000429 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000430 x,
431 y;
432
433 register unsigned char
434 *p,
435 *q,
436 *r;
437
438 /*
439 Create a new image that is a integral size greater than an existing one.
440 */
441 assert(pixels != (unsigned char *) NULL);
cristybb503372010-05-27 20:51:26 +0000442 for (y=0; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000443 {
444 p=pixels+(height-1-y)*scaled_width+(width-1);
445 q=pixels+((height-1-y) << 1)*scaled_width+((width-1) << 1);
446 *q=(*p);
447 *(q+1)=(*(p));
cristybb503372010-05-27 20:51:26 +0000448 for (x=1; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000449 {
450 p--;
451 q-=2;
452 *q=(*p);
cristybb503372010-05-27 20:51:26 +0000453 *(q+1)=(unsigned char) ((((size_t) *p)+
454 ((size_t) *(p+1))+1) >> 1);
cristy3ed852e2009-09-05 21:47:34 +0000455 }
456 }
cristybb503372010-05-27 20:51:26 +0000457 for (y=0; y < (ssize_t) (height-1); y++)
cristy3ed852e2009-09-05 21:47:34 +0000458 {
cristybb503372010-05-27 20:51:26 +0000459 p=pixels+((size_t) y << 1)*scaled_width;
cristy3ed852e2009-09-05 21:47:34 +0000460 q=p+scaled_width;
461 r=q+scaled_width;
cristybb503372010-05-27 20:51:26 +0000462 for (x=0; x < (ssize_t) (width-1); x++)
cristy3ed852e2009-09-05 21:47:34 +0000463 {
cristybb503372010-05-27 20:51:26 +0000464 *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);
cristy3ed852e2009-09-05 21:47:34 +0000467 q+=2;
468 p+=2;
469 r+=2;
470 }
cristybb503372010-05-27 20:51:26 +0000471 *q++=(unsigned char) ((((size_t) *p++)+
472 ((size_t) *r++)+1) >> 1);
473 *q++=(unsigned char) ((((size_t) *p++)+
474 ((size_t) *r++)+1) >> 1);
cristy3ed852e2009-09-05 21:47:34 +0000475 }
476 p=pixels+(2*height-2)*scaled_width;
477 q=pixels+(2*height-1)*scaled_width;
478 (void) CopyMagickMemory(q,p,(size_t) (2*width));
479}
480
481static Image *ReadPCDImage(const ImageInfo *image_info,ExceptionInfo *exception)
482{
483 Image
484 *image;
485
cristybb503372010-05-27 20:51:26 +0000486 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000487 x;
488
489 MagickBooleanType
490 status;
491
492 MagickOffsetType
493 offset;
494
495 MagickSizeType
496 number_pixels;
497
cristybb503372010-05-27 20:51:26 +0000498 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000499 i,
500 y;
501
502 register PixelPacket
503 *q;
504
505 register unsigned char
506 *c1,
507 *c2,
508 *yy;
509
510 ssize_t
511 count;
512
513 unsigned char
514 *chroma1,
515 *chroma2,
516 *header,
517 *luma;
518
519 unsigned int
520 overview;
521
cristybb503372010-05-27 20:51:26 +0000522 size_t
cristy3ed852e2009-09-05 21:47:34 +0000523 height,
524 number_images,
525 rotate,
526 scene,
527 width;
528
529 /*
530 Open image file.
531 */
532 assert(image_info != (const ImageInfo *) NULL);
533 assert(image_info->signature == MagickSignature);
534 if (image_info->debug != MagickFalse)
535 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
536 image_info->filename);
537 assert(exception != (ExceptionInfo *) NULL);
538 assert(exception->signature == MagickSignature);
539 image=AcquireImage(image_info);
540 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
541 if (status == MagickFalse)
542 {
543 image=DestroyImageList(image);
544 return((Image *) NULL);
545 }
546 /*
547 Determine if this a PCD file.
548 */
549 header=(unsigned char *) AcquireQuantumMemory(0x800,3UL*sizeof(*header));
550 if (header == (unsigned char *) NULL)
551 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
552 count=ReadBlob(image,3*0x800,header);
553 overview=LocaleNCompare((char *) header,"PCD_OPA",7) == 0;
554 if ((count == 0) ||
555 ((LocaleNCompare((char *) header+0x800,"PCD",3) != 0) && !overview))
556 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
557 rotate=header[0x0e02] & 0x03;
558 number_images=(header[10] << 8) | header[11];
559 header=(unsigned char *) RelinquishMagickMemory(header);
560 /*
561 Determine resolution by scene specification.
562 */
563 if ((image->columns == 0) || (image->rows == 0))
564 scene=3;
565 else
566 {
567 width=192;
568 height=128;
569 for (scene=1; scene < 6; scene++)
570 {
571 if ((width >= image->columns) && (height >= image->rows))
572 break;
573 width<<=1;
574 height<<=1;
575 }
576 }
577 if (image_info->number_scenes != 0)
cristybb503372010-05-27 20:51:26 +0000578 scene=(size_t) MagickMin(image_info->scene,6);
cristy3ed852e2009-09-05 21:47:34 +0000579 if (overview)
580 scene=1;
581 /*
582 Initialize image structure.
583 */
584 width=192;
585 height=128;
cristybb503372010-05-27 20:51:26 +0000586 for (i=1; i < (ssize_t) MagickMin(scene,3); i++)
cristy3ed852e2009-09-05 21:47:34 +0000587 {
588 width<<=1;
589 height<<=1;
590 }
591 image->columns=width;
592 image->rows=height;
593 image->depth=8;
cristybb503372010-05-27 20:51:26 +0000594 for ( ; i < (ssize_t) scene; i++)
cristy3ed852e2009-09-05 21:47:34 +0000595 {
596 image->columns<<=1;
597 image->rows<<=1;
598 }
599 /*
cristy5d418802009-11-27 19:06:12 +0000600 Allocate luma and chroma memory.
cristy3ed852e2009-09-05 21:47:34 +0000601 */
602 number_pixels=(MagickSizeType) image->columns*image->rows;
603 if (number_pixels != (size_t) number_pixels)
604 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
605 chroma1=(unsigned char *) AcquireQuantumMemory(image->columns+1UL,image->rows*
606 sizeof(*chroma1));
607 chroma2=(unsigned char *) AcquireQuantumMemory(image->columns+1UL,image->rows*
608 sizeof(*chroma2));
609 luma=(unsigned char *) AcquireQuantumMemory(image->columns+1UL,image->rows*
610 sizeof(*luma));
611 if ((chroma1 == (unsigned char *) NULL) ||
612 (chroma2 == (unsigned char *) NULL) || (luma == (unsigned char *) NULL))
613 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
614 /*
615 Advance to image data.
616 */
617 offset=93;
618 if (overview)
619 offset=2;
620 else
621 if (scene == 2)
622 offset=20;
623 else
624 if (scene <= 1)
625 offset=1;
cristybb503372010-05-27 20:51:26 +0000626 for (i=0; i < (ssize_t) (offset*0x800); i++)
cristy3ed852e2009-09-05 21:47:34 +0000627 (void) ReadBlobByte(image);
628 if (overview)
629 {
630 Image
631 *overview_image;
632
633 MagickProgressMonitor
634 progress_monitor;
635
cristybb503372010-05-27 20:51:26 +0000636 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000637 j;
638
639 /*
640 Read thumbnails from overview image.
641 */
cristybb503372010-05-27 20:51:26 +0000642 for (j=1; j <= (ssize_t) number_images; j++)
cristy3ed852e2009-09-05 21:47:34 +0000643 {
644 progress_monitor=SetImageProgressMonitor(image,
645 (MagickProgressMonitor) NULL,image->client_data);
646 (void) FormatMagickString(image->filename,MaxTextExtent,
cristyf2faecf2010-05-28 19:19:36 +0000647 "images/img%04ld.pcd",(long) j);
cristy3ed852e2009-09-05 21:47:34 +0000648 (void) FormatMagickString(image->magick_filename,MaxTextExtent,
cristyf2faecf2010-05-28 19:19:36 +0000649 "images/img%04ld.pcd",(long) j);
cristybb503372010-05-27 20:51:26 +0000650 image->scene=(size_t) j;
cristy3ed852e2009-09-05 21:47:34 +0000651 image->columns=width;
652 image->rows=height;
653 image->depth=8;
654 yy=luma;
655 c1=chroma1;
656 c2=chroma2;
cristybb503372010-05-27 20:51:26 +0000657 for (y=0; y < (ssize_t) height; y+=2)
cristy3ed852e2009-09-05 21:47:34 +0000658 {
659 count=ReadBlob(image,width,yy);
660 yy+=image->columns;
661 count=ReadBlob(image,width,yy);
662 yy+=image->columns;
663 count=ReadBlob(image,width >> 1,c1);
664 c1+=image->columns;
665 count=ReadBlob(image,width >> 1,c2);
666 c2+=image->columns;
667 }
668 Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
669 Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
670 /*
671 Transfer luminance and chrominance channels.
672 */
673 yy=luma;
674 c1=chroma1;
675 c2=chroma2;
cristybb503372010-05-27 20:51:26 +0000676 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000677 {
678 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
679 if (q == (PixelPacket *) NULL)
680 break;
cristybb503372010-05-27 20:51:26 +0000681 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000682 {
683 q->red=ScaleCharToQuantum(*yy++);
684 q->green=ScaleCharToQuantum(*c1++);
685 q->blue=ScaleCharToQuantum(*c2++);
686 q++;
687 }
688 if (SyncAuthenticPixels(image,exception) == MagickFalse)
689 break;
690 }
691 image->colorspace=YCCColorspace;
692 if (LocaleCompare(image_info->magick,"PCDS") == 0)
693 image->colorspace=sRGBColorspace;
cristybb503372010-05-27 20:51:26 +0000694 if (j < (ssize_t) number_images)
cristy3ed852e2009-09-05 21:47:34 +0000695 {
696 /*
697 Allocate next image structure.
698 */
699 AcquireNextImage(image_info,image);
700 if (GetNextImageInList(image) == (Image *) NULL)
701 {
702 image=DestroyImageList(image);
703 return((Image *) NULL);
704 }
705 image=SyncNextImageInList(image);
706 }
707 (void) SetImageProgressMonitor(image,progress_monitor,
708 image->client_data);
709 if (image->previous == (Image *) NULL)
710 {
711 status=SetImageProgress(image,LoadImageTag,j-1,number_images);
712 if (status == MagickFalse)
713 break;
714 }
715 }
716 chroma2=(unsigned char *) RelinquishMagickMemory(chroma2);
717 chroma1=(unsigned char *) RelinquishMagickMemory(chroma1);
718 luma=(unsigned char *) RelinquishMagickMemory(luma);
719 image=GetFirstImageInList(image);
720 overview_image=OverviewImage(image_info,image,exception);
721 return(overview_image);
722 }
723 /*
724 Read interleaved image.
725 */
726 yy=luma;
727 c1=chroma1;
728 c2=chroma2;
cristybb503372010-05-27 20:51:26 +0000729 for (y=0; y < (ssize_t) height; y+=2)
cristy3ed852e2009-09-05 21:47:34 +0000730 {
731 count=ReadBlob(image,width,yy);
732 yy+=image->columns;
733 count=ReadBlob(image,width,yy);
734 yy+=image->columns;
735 count=ReadBlob(image,width >> 1,c1);
736 c1+=image->columns;
737 count=ReadBlob(image,width >> 1,c2);
738 c2+=image->columns;
739 }
740 if (scene >= 4)
741 {
742 /*
743 Recover luminance deltas for 1536x1024 image.
744 */
745 Upsample(768,512,image->columns,luma);
746 Upsample(384,256,image->columns,chroma1);
747 Upsample(384,256,image->columns,chroma2);
748 image->rows=1024;
749 for (i=0; i < (4*0x800); i++)
750 (void) ReadBlobByte(image);
751 status=DecodeImage(image,luma,chroma1,chroma2);
752 if ((scene >= 5) && status)
753 {
754 /*
755 Recover luminance deltas for 3072x2048 image.
756 */
757 Upsample(1536,1024,image->columns,luma);
758 Upsample(768,512,image->columns,chroma1);
759 Upsample(768,512,image->columns,chroma2);
760 image->rows=2048;
761 offset=TellBlob(image)/0x800+12;
762 offset=SeekBlob(image,offset*0x800,SEEK_SET);
763 status=DecodeImage(image,luma,chroma1,chroma2);
764 if ((scene >= 6) && (status != MagickFalse))
765 {
766 /*
767 Recover luminance deltas for 6144x4096 image (vaporware).
768 */
769 Upsample(3072,2048,image->columns,luma);
770 Upsample(1536,1024,image->columns,chroma1);
771 Upsample(1536,1024,image->columns,chroma2);
772 image->rows=4096;
773 }
774 }
775 }
776 Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
777 Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
778 /*
779 Transfer luminance and chrominance channels.
780 */
781 yy=luma;
782 c1=chroma1;
783 c2=chroma2;
cristybb503372010-05-27 20:51:26 +0000784 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000785 {
786 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
787 if (q == (PixelPacket *) NULL)
788 break;
cristybb503372010-05-27 20:51:26 +0000789 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000790 {
791 q->red=ScaleCharToQuantum(*yy++);
792 q->green=ScaleCharToQuantum(*c1++);
793 q->blue=ScaleCharToQuantum(*c2++);
794 q++;
795 }
796 if (SyncAuthenticPixels(image,exception) == MagickFalse)
797 break;
798 if (image->previous == (Image *) NULL)
799 {
cristycee97112010-05-28 00:44:52 +0000800 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
801 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000802 if (status == MagickFalse)
803 break;
804 }
805 }
806 chroma2=(unsigned char *) RelinquishMagickMemory(chroma2);
807 chroma1=(unsigned char *) RelinquishMagickMemory(chroma1);
808 luma=(unsigned char *) RelinquishMagickMemory(luma);
809 if (EOFBlob(image) != MagickFalse)
810 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
811 image->filename);
812 (void) CloseBlob(image);
813 if (image_info->ping == MagickFalse)
814 if ((rotate == 1) || (rotate == 3))
815 {
816 double
817 degrees;
818
819 Image
820 *rotate_image;
821
822 /*
823 Rotate image.
824 */
825 degrees=rotate == 1 ? -90.0 : 90.0;
826 rotate_image=RotateImage(image,degrees,exception);
827 if (rotate_image != (Image *) NULL)
828 {
829 image=DestroyImage(image);
830 image=rotate_image;
831 }
832 }
833 /*
834 Set CCIR 709 primaries with a D65 white point.
835 */
836 image->chromaticity.red_primary.x=0.6400f;
837 image->chromaticity.red_primary.y=0.3300f;
838 image->chromaticity.green_primary.x=0.3000f;
839 image->chromaticity.green_primary.y=0.6000f;
840 image->chromaticity.blue_primary.x=0.1500f;
841 image->chromaticity.blue_primary.y=0.0600f;
842 image->chromaticity.white_point.x=0.3127f;
843 image->chromaticity.white_point.y=0.3290f;
844 image->gamma=1.000f/2.200f;
845 image->colorspace=YCCColorspace;
846 if (LocaleCompare(image_info->magick,"PCDS") == 0)
847 image->colorspace=sRGBColorspace;
848 return(GetFirstImageInList(image));
849}
850
851/*
852%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
853% %
854% %
855% %
856% R e g i s t e r P C D I m a g e %
857% %
858% %
859% %
860%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
861%
862% RegisterPCDImage() adds attributes for the PCD image format to
863% the list of supported formats. The attributes include the image format
864% tag, a method to read and/or write the format, whether the format
865% supports the saving of more than one frame to the same file or blob,
866% whether the format supports native in-memory I/O, and a brief
867% description of the format.
868%
869% The format of the RegisterPCDImage method is:
870%
cristybb503372010-05-27 20:51:26 +0000871% size_t RegisterPCDImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000872%
873*/
cristybb503372010-05-27 20:51:26 +0000874ModuleExport size_t RegisterPCDImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000875{
876 MagickInfo
877 *entry;
878
879 entry=SetMagickInfo("PCD");
880 entry->decoder=(DecodeImageHandler *) ReadPCDImage;
881 entry->encoder=(EncodeImageHandler *) WritePCDImage;
882 entry->magick=(IsImageFormatHandler *) IsPCD;
883 entry->adjoin=MagickFalse;
cristyffaf9782011-04-13 19:50:51 +0000884 entry->seekable_stream=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000885 entry->description=ConstantString("Photo CD");
886 entry->module=ConstantString("PCD");
887 (void) RegisterMagickInfo(entry);
888 entry=SetMagickInfo("PCDS");
889 entry->decoder=(DecodeImageHandler *) ReadPCDImage;
890 entry->encoder=(EncodeImageHandler *) WritePCDImage;
891 entry->adjoin=MagickFalse;
cristyffaf9782011-04-13 19:50:51 +0000892 entry->seekable_stream=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000893 entry->description=ConstantString("Photo CD");
894 entry->module=ConstantString("PCD");
895 (void) RegisterMagickInfo(entry);
896 return(MagickImageCoderSignature);
897}
898
899/*
900%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
901% %
902% %
903% %
904% U n r e g i s t e r P C D I m a g e %
905% %
906% %
907% %
908%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
909%
910% UnregisterPCDImage() removes format registrations made by the
911% PCD module from the list of supported formats.
912%
913% The format of the UnregisterPCDImage method is:
914%
915% UnregisterPCDImage(void)
916%
917*/
918ModuleExport void UnregisterPCDImage(void)
919{
920 (void) UnregisterMagickInfo("PCD");
921 (void) UnregisterMagickInfo("PCDS");
922}
923
924/*
925%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
926% %
927% %
928% %
929% W r i t e P C D I m a g e %
930% %
931% %
932% %
933%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
934%
935% WritePCDImage() writes an image in the Photo CD encoded image format.
936%
937% The format of the WritePCDImage method is:
938%
939% MagickBooleanType WritePCDImage(const ImageInfo *image_info,Image *image)
940%
941% A description of each parameter follows.
942%
943% o image_info: the image info.
944%
945% o image: The image.
946%
947*/
948
949static MagickBooleanType WritePCDTile(Image *image,const char *page_geometry,
950 const char *tile_geometry)
951{
952 GeometryInfo
953 geometry_info;
954
955 Image
956 *downsample_image,
957 *tile_image;
958
cristybb503372010-05-27 20:51:26 +0000959 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000960 y;
961
962 MagickBooleanType
963 status;
964
965 MagickStatusType
966 flags;
967
968 RectangleInfo
969 geometry;
970
971 register const PixelPacket
972 *p,
973 *q;
974
cristybb503372010-05-27 20:51:26 +0000975 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000976 i,
977 x;
978
979 /*
980 Scale image to tile size.
981 */
982 SetGeometry(image,&geometry);
983 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
984 &geometry.width,&geometry.height);
985 if ((geometry.width % 2) != 0)
986 geometry.width--;
987 if ((geometry.height % 2) != 0)
988 geometry.height--;
989 tile_image=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
990 1.0,&image->exception);
991 if (tile_image == (Image *) NULL)
992 return(MagickFalse);
993 flags=ParseGeometry(page_geometry,&geometry_info);
cristybb503372010-05-27 20:51:26 +0000994 geometry.width=(size_t) geometry_info.rho;
995 geometry.height=(size_t) geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +0000996 if ((flags & SigmaValue) == 0)
997 geometry.height=geometry.width;
998 if ((tile_image->columns != geometry.width) ||
999 (tile_image->rows != geometry.height))
1000 {
1001 Image
1002 *bordered_image;
1003
1004 RectangleInfo
1005 border_info;
1006
1007 /*
1008 Put a border around the image.
1009 */
1010 border_info.width=(geometry.width-tile_image->columns+1) >> 1;
1011 border_info.height=(geometry.height-tile_image->rows+1) >> 1;
1012 bordered_image=BorderImage(tile_image,&border_info,&image->exception);
1013 if (bordered_image == (Image *) NULL)
1014 return(MagickFalse);
1015 tile_image=DestroyImage(tile_image);
1016 tile_image=bordered_image;
1017 }
1018 (void) TransformImage(&tile_image,(char *) NULL,tile_geometry);
1019 if (image->colorspace != RGBColorspace)
1020 (void) TransformImageColorspace(tile_image,YCCColorspace);
1021 downsample_image=ResizeImage(tile_image,tile_image->columns/2,
1022 tile_image->rows/2,TriangleFilter,1.0,&image->exception);
1023 if (downsample_image == (Image *) NULL)
1024 return(MagickFalse);
1025 /*
1026 Write tile to PCD file.
1027 */
cristybb503372010-05-27 20:51:26 +00001028 for (y=0; y < (ssize_t) tile_image->rows; y+=2)
cristy3ed852e2009-09-05 21:47:34 +00001029 {
1030 p=GetVirtualPixels(tile_image,0,y,tile_image->columns,2,
1031 &tile_image->exception);
1032 if (p == (const PixelPacket *) NULL)
1033 break;
cristybb503372010-05-27 20:51:26 +00001034 for (x=0; x < (ssize_t) (tile_image->columns << 1); x++)
cristy3ed852e2009-09-05 21:47:34 +00001035 {
cristyce70c172010-01-07 17:15:30 +00001036 (void) WriteBlobByte(image,ScaleQuantumToChar(GetRedPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +00001037 p++;
1038 }
1039 q=GetVirtualPixels(downsample_image,0,y >> 1,downsample_image->columns,
1040 1,&downsample_image->exception);
1041 if (q == (const PixelPacket *) NULL)
1042 break;
cristybb503372010-05-27 20:51:26 +00001043 for (x=0; x < (ssize_t) downsample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001044 {
1045 (void) WriteBlobByte(image,ScaleQuantumToChar(q->green));
1046 q++;
1047 }
1048 q=GetVirtualPixels(downsample_image,0,y >> 1,downsample_image->columns,
1049 1,&downsample_image->exception);
1050 if (q == (const PixelPacket *) NULL)
1051 break;
cristybb503372010-05-27 20:51:26 +00001052 for (x=0; x < (ssize_t) downsample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001053 {
1054 (void) WriteBlobByte(image,ScaleQuantumToChar(q->blue));
1055 q++;
1056 }
1057 status=SetImageProgress(image,SaveImageTag,y,tile_image->rows);
1058 if (status == MagickFalse)
1059 break;
1060 }
1061 for (i=0; i < 0x800; i++)
1062 (void) WriteBlobByte(image,'\0');
1063 downsample_image=DestroyImage(downsample_image);
1064 tile_image=DestroyImage(tile_image);
1065 return(MagickTrue);
1066}
1067
1068static MagickBooleanType WritePCDImage(const ImageInfo *image_info,Image *image)
1069{
1070 Image
1071 *pcd_image;
1072
1073 MagickBooleanType
1074 status;
1075
cristybb503372010-05-27 20:51:26 +00001076 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001077 i;
1078
1079 assert(image_info != (const ImageInfo *) NULL);
1080 assert(image_info->signature == MagickSignature);
1081 assert(image != (Image *) NULL);
1082 assert(image->signature == MagickSignature);
1083 if (image->debug != MagickFalse)
1084 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1085 pcd_image=image;
1086 if (image->columns < image->rows)
1087 {
1088 Image
1089 *rotate_image;
1090
1091 /*
1092 Rotate portrait to landscape.
1093 */
1094 rotate_image=RotateImage(image,90.0,&image->exception);
1095 if (rotate_image == (Image *) NULL)
1096 return(MagickFalse);
1097 pcd_image=rotate_image;
1098 }
1099 /*
1100 Open output image file.
1101 */
1102 status=OpenBlob(image_info,pcd_image,WriteBinaryBlobMode,&image->exception);
1103 if (status == MagickFalse)
1104 return(status);
1105 if (image->colorspace != RGBColorspace)
1106 (void) TransformImageColorspace(pcd_image,RGBColorspace);
1107 /*
1108 Write PCD image header.
1109 */
1110 for (i=0; i < 32; i++)
1111 (void) WriteBlobByte(pcd_image,0xff);
1112 for (i=0; i < 4; i++)
1113 (void) WriteBlobByte(pcd_image,0x0e);
1114 for (i=0; i < 8; i++)
1115 (void) WriteBlobByte(pcd_image,'\0');
1116 for (i=0; i < 4; i++)
1117 (void) WriteBlobByte(pcd_image,0x01);
1118 for (i=0; i < 4; i++)
1119 (void) WriteBlobByte(pcd_image,0x05);
1120 for (i=0; i < 8; i++)
1121 (void) WriteBlobByte(pcd_image,'\0');
1122 for (i=0; i < 4; i++)
1123 (void) WriteBlobByte(pcd_image,0x0A);
1124 for (i=0; i < 36; i++)
1125 (void) WriteBlobByte(pcd_image,'\0');
1126 for (i=0; i < 4; i++)
1127 (void) WriteBlobByte(pcd_image,0x01);
1128 for (i=0; i < 1944; i++)
1129 (void) WriteBlobByte(pcd_image,'\0');
1130 (void) WriteBlob(pcd_image,7,(const unsigned char *) "PCD_IPI");
1131 (void) WriteBlobByte(pcd_image,0x06);
1132 for (i=0; i < 1530; i++)
1133 (void) WriteBlobByte(pcd_image,'\0');
1134 if (image->columns < image->rows)
1135 (void) WriteBlobByte(pcd_image,'\1');
1136 else
1137 (void) WriteBlobByte(pcd_image,'\0');
1138 for (i=0; i < (3*0x800-1539); i++)
1139 (void) WriteBlobByte(pcd_image,'\0');
1140 /*
1141 Write PCD tiles.
1142 */
1143 status=WritePCDTile(pcd_image,"768x512>","192x128");
1144 status=WritePCDTile(pcd_image,"768x512>","384x256");
1145 status=WritePCDTile(pcd_image,"768x512>","768x512");
1146 (void) CloseBlob(pcd_image);
1147 if (pcd_image != image)
1148 pcd_image=DestroyImage(pcd_image);
1149 return(status);
1150}