blob: 67b5ebafec996bd818ec69c327829fa3686ffa37 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP DDDD BBBB %
7% P P D D B B %
8% PPPP D D BBBB %
9% P D D B B %
10% P DDDD BBBB %
11% %
12% %
13% Read/Write Palm Database ImageViewer Image Format %
14% %
15% Software Design %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
Cristy7ce65e72015-12-12 18:03:16 -050020% Copyright 1999-2016 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%
cristy98cba562014-11-20 23:46:19 +000037% 20071202 TS * rewrote RLE decoder - old version could cause buffer overflows
38% * failure of RLE decoding now thows error RLEDecoderError
39% * fixed bug in RLE decoding - now all rows are decoded, not just
40% the first one
41% * fixed bug in reader - record offsets now handled correctly
42% * fixed bug in reader - only bits 0..2 indicate compression type
43% * in writer: now using image color count instead of depth
cristy3ed852e2009-09-05 21:47:34 +000044*/
45
46/*
47 Include declarations.
48*/
cristy4c08aed2011-07-01 19:47:50 +000049#include "MagickCore/studio.h"
50#include "MagickCore/attribute.h"
51#include "MagickCore/blob.h"
52#include "MagickCore/blob-private.h"
53#include "MagickCore/cache.h"
54#include "MagickCore/colormap-private.h"
55#include "MagickCore/color-private.h"
56#include "MagickCore/colormap.h"
57#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000058#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000059#include "MagickCore/constitute.h"
60#include "MagickCore/exception.h"
61#include "MagickCore/exception-private.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/list.h"
65#include "MagickCore/magick.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/monitor.h"
68#include "MagickCore/monitor-private.h"
69#include "MagickCore/pixel-accessor.h"
70#include "MagickCore/property.h"
71#include "MagickCore/quantum-private.h"
72#include "MagickCore/quantum-private.h"
73#include "MagickCore/static.h"
74#include "MagickCore/string_.h"
75#include "MagickCore/module.h"
cristy3ed852e2009-09-05 21:47:34 +000076
77/*
78 Typedef declarations.
79*/
80typedef struct _PDBInfo
81{
82 char
83 name[32];
84
85 short int
86 attributes,
87 version;
88
cristybb503372010-05-27 20:51:26 +000089 size_t
cristy3ed852e2009-09-05 21:47:34 +000090 create_time,
91 modify_time,
92 archive_time,
93 modify_number,
94 application_info,
95 sort_info;
96
97 char
98 type[4], /* database type identifier "vIMG" */
99 id[4]; /* database creator identifier "View" */
100
cristybb503372010-05-27 20:51:26 +0000101 size_t
cristy3ed852e2009-09-05 21:47:34 +0000102 seed,
103 next_record;
104
105 short int
106 number_records;
107} PDBInfo;
108
109typedef struct _PDBImage
110{
111 char
112 name[32],
dirk93b02b72013-11-16 16:03:36 +0000113 version;
cristy3ed852e2009-09-05 21:47:34 +0000114
cristybb503372010-05-27 20:51:26 +0000115 size_t
cristy3ed852e2009-09-05 21:47:34 +0000116 reserved_1,
117 note;
118
119 short int
120 x_last,
121 y_last;
122
cristybb503372010-05-27 20:51:26 +0000123 size_t
cristy3ed852e2009-09-05 21:47:34 +0000124 reserved_2;
125
126 short int
cristy3ed852e2009-09-05 21:47:34 +0000127 width,
128 height;
dirk93b02b72013-11-16 16:03:36 +0000129
130 unsigned char
131 type;
132
133 unsigned short
134 x_anchor,
135 y_anchor;
cristy3ed852e2009-09-05 21:47:34 +0000136} PDBImage;
137/*
138 Forward declarations.
139*/
140static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000141 WritePDBImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000142
143/*
144%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
145% %
146% %
147% %
148% D e c o d e I m a g e %
149% %
150% %
151% %
152%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153%
154% DecodeImage unpacks the packed image pixels into runlength-encoded
155% pixel packets.
156%
157% The format of the DecodeImage method is:
158%
159% MagickBooleanType DecodeImage(Image *image,unsigned char *pixels,
160% const size_t length)
161%
162% A description of each parameter follows:
163%
164% o image: the address of a structure of type Image.
165%
166% o pixels: The address of a byte (8 bits) array of pixel data created by
167% the decoding process.
168%
169% o length: Number of bytes to read into buffer 'pixels'.
170%
171*/
172static MagickBooleanType DecodeImage(Image *image, unsigned char *pixels,
173 const size_t length)
174{
175#define RLE_MODE_NONE -1
176#define RLE_MODE_COPY 0
177#define RLE_MODE_RUN 1
178
cristyc5de6992009-10-06 19:19:48 +0000179 int data = 0, count = 0;
180 unsigned char *p;
181 int mode = RLE_MODE_NONE;
cristy3ed852e2009-09-05 21:47:34 +0000182
cristyc5de6992009-10-06 19:19:48 +0000183 for (p = pixels; p < pixels + length; p++) {
184 if (0 == count) {
185 data = ReadBlobByte( image );
186 if (-1 == data) return MagickFalse;
187 if (data > 128) {
188 mode = RLE_MODE_RUN;
189 count = data - 128 + 1;
190 data = ReadBlobByte( image );
191 if (-1 == data) return MagickFalse;
192 } else {
193 mode = RLE_MODE_COPY;
194 count = data + 1;
195 }
196 }
cristy3ed852e2009-09-05 21:47:34 +0000197
cristyc5de6992009-10-06 19:19:48 +0000198 if (RLE_MODE_COPY == mode) {
199 data = ReadBlobByte( image );
200 if (-1 == data) return MagickFalse;
201 }
202 *p = (unsigned char)data;
203 --count;
204 }
205 return MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000206}
207
208/*
209%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
210% %
211% %
212% %
213% I s P D B %
214% %
215% %
216% %
217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
218%
219% IsPDB() returns MagickTrue if the image format type, identified by the
220% magick string, is PDB.
221%
222% The format of the ReadPDBImage method is:
223%
224% MagickBooleanType IsPDB(const unsigned char *magick,const size_t length)
225%
226% A description of each parameter follows:
227%
228% o magick: compare image format pattern against these bytes.
229%
230% o length: Specifies the length of the magick string.
231%
232*/
233static MagickBooleanType IsPDB(const unsigned char *magick,const size_t length)
234{
235 if (length < 68)
236 return(MagickFalse);
237 if (memcmp(magick+60,"vIMGView",8) == 0)
238 return(MagickTrue);
239 return(MagickFalse);
240}
241
242/*
243%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244% %
245% %
246% %
247% R e a d P D B I m a g e %
248% %
249% %
250% %
251%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
252%
253% ReadPDBImage() reads an Pilot image file and returns it. It
254% allocates the memory necessary for the new Image structure and returns a
255% pointer to the new image.
256%
257% The format of the ReadPDBImage method is:
258%
259% Image *ReadPDBImage(const ImageInfo *image_info,ExceptionInfo *exception)
260%
261% A description of each parameter follows:
262%
263% o image_info: the image info.
264%
265% o exception: return any errors or warnings in this structure.
266%
267*/
268static Image *ReadPDBImage(const ImageInfo *image_info,ExceptionInfo *exception)
269{
270 unsigned char
cristy98cba562014-11-20 23:46:19 +0000271 attributes,
cristy3ed852e2009-09-05 21:47:34 +0000272 tag[3];
273
274 Image
275 *image;
276
cristy3ed852e2009-09-05 21:47:34 +0000277 MagickBooleanType
278 status;
279
280 PDBImage
281 pdb_image;
282
283 PDBInfo
284 pdb_info;
285
cristy4c08aed2011-07-01 19:47:50 +0000286 Quantum
287 index;
cristy3ed852e2009-09-05 21:47:34 +0000288
cristybb503372010-05-27 20:51:26 +0000289 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000290 x;
291
cristy4c08aed2011-07-01 19:47:50 +0000292 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000293 *q;
294
295 register unsigned char
296 *p;
297
cristybb503372010-05-27 20:51:26 +0000298 size_t
cristy3ed852e2009-09-05 21:47:34 +0000299 bits_per_pixel,
cristy98cba562014-11-20 23:46:19 +0000300 num_pad_bytes,
cristyeaedf062010-05-29 22:36:02 +0000301 one,
cristy3ed852e2009-09-05 21:47:34 +0000302 packets;
303
cristyaff6d802011-04-26 01:46:31 +0000304 ssize_t
305 count,
cristy98cba562014-11-20 23:46:19 +0000306 img_offset,
cristyaff6d802011-04-26 01:46:31 +0000307 comment_offset = 0,
308 y;
309
310 unsigned char
311 *pixels;
312
cristy3ed852e2009-09-05 21:47:34 +0000313 /*
314 Open image file.
315 */
316 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000317 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000318 if (image_info->debug != MagickFalse)
319 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
320 image_info->filename);
321 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000322 assert(exception->signature == MagickCoreSignature);
cristy9950d572011-10-01 18:22:35 +0000323 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000324 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
325 if (status == MagickFalse)
326 {
327 image=DestroyImageList(image);
328 return((Image *) NULL);
329 }
330 /*
331 Determine if this a PDB image file.
332 */
cristyca793d72015-01-23 13:03:55 +0000333 count=ReadBlob(image,sizeof(pdb_info.name),(unsigned char *) pdb_info.name);
334 if (count != sizeof(pdb_info.name))
335 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +0000336 pdb_info.attributes=(short) ReadBlobMSBShort(image);
337 pdb_info.version=(short) ReadBlobMSBShort(image);
338 pdb_info.create_time=ReadBlobMSBLong(image);
339 pdb_info.modify_time=ReadBlobMSBLong(image);
340 pdb_info.archive_time=ReadBlobMSBLong(image);
341 pdb_info.modify_number=ReadBlobMSBLong(image);
342 pdb_info.application_info=ReadBlobMSBLong(image);
343 pdb_info.sort_info=ReadBlobMSBLong(image);
cristy7e5d1da2014-12-28 00:40:02 +0000344 (void) ReadBlob(image,4,(unsigned char *) pdb_info.type);
345 (void) ReadBlob(image,4,(unsigned char *) pdb_info.id);
cristy3ed852e2009-09-05 21:47:34 +0000346 pdb_info.seed=ReadBlobMSBLong(image);
347 pdb_info.next_record=ReadBlobMSBLong(image);
348 pdb_info.number_records=(short) ReadBlobMSBShort(image);
cristy7e5d1da2014-12-28 00:40:02 +0000349 if ((memcmp(pdb_info.type,"vIMG",4) != 0) ||
350 (memcmp(pdb_info.id,"View",4) != 0))
351 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +0000352 if (pdb_info.next_record != 0)
353 ThrowReaderException(CoderError,"MultipleRecordListNotSupported");
354 /*
355 Read record header.
356 */
cristy98cba562014-11-20 23:46:19 +0000357 img_offset=(ssize_t) ((int) ReadBlobMSBLong(image));
cristy3ed852e2009-09-05 21:47:34 +0000358 attributes=(unsigned char) ReadBlobByte(image);
cristyda16f162011-02-19 23:52:17 +0000359 (void) attributes;
cristy3ed852e2009-09-05 21:47:34 +0000360 count=ReadBlob(image,3,(unsigned char *) tag);
361 if (count != 3 || memcmp(tag,"\x6f\x80\x00",3) != 0)
362 ThrowReaderException(CorruptImageError,"CorruptImage");
cristy3ed852e2009-09-05 21:47:34 +0000363 if (pdb_info.number_records > 1)
364 {
cristy98cba562014-11-20 23:46:19 +0000365 comment_offset=(ssize_t) ((int) ReadBlobMSBLong(image));
cristy3ed852e2009-09-05 21:47:34 +0000366 attributes=(unsigned char) ReadBlobByte(image);
367 count=ReadBlob(image,3,(unsigned char *) tag);
368 if (count != 3 || memcmp(tag,"\x6f\x80\x01",3) != 0)
369 ThrowReaderException(CorruptImageError,"CorruptImage");
370 }
cristybb503372010-05-27 20:51:26 +0000371 num_pad_bytes = (size_t) (img_offset - TellBlob( image ));
cristy89f839d2015-01-25 17:32:32 +0000372 while (num_pad_bytes-- != 0)
373 {
374 int
375 c;
376
377 c=ReadBlobByte(image);
378 if (c == EOF)
379 break;
380 }
cristy3ed852e2009-09-05 21:47:34 +0000381 /*
382 Read image header.
383 */
cristyca793d72015-01-23 13:03:55 +0000384 count=ReadBlob(image,sizeof(pdb_image.name),(unsigned char *) pdb_image.name);
385 if (count != sizeof(pdb_image.name))
386 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +0000387 pdb_image.version=ReadBlobByte(image);
cristy98cba562014-11-20 23:46:19 +0000388 pdb_image.type=(unsigned char) ((int) ReadBlobByte(image));
cristy3ed852e2009-09-05 21:47:34 +0000389 pdb_image.reserved_1=ReadBlobMSBLong(image);
390 pdb_image.note=ReadBlobMSBLong(image);
391 pdb_image.x_last=(short) ReadBlobMSBShort(image);
392 pdb_image.y_last=(short) ReadBlobMSBShort(image);
393 pdb_image.reserved_2=ReadBlobMSBLong(image);
dirk93b02b72013-11-16 16:03:36 +0000394 pdb_image.x_anchor=ReadBlobMSBShort(image);
395 pdb_image.y_anchor=ReadBlobMSBShort(image);
cristy3ed852e2009-09-05 21:47:34 +0000396 pdb_image.width=(short) ReadBlobMSBShort(image);
397 pdb_image.height=(short) ReadBlobMSBShort(image);
398 /*
399 Initialize image structure.
400 */
cristybb503372010-05-27 20:51:26 +0000401 image->columns=(size_t) pdb_image.width;
402 image->rows=(size_t) pdb_image.height;
cristy3ed852e2009-09-05 21:47:34 +0000403 image->depth=8;
cristyacabb842014-12-14 23:36:33 +0000404 status=SetImageExtent(image,image->columns,image->rows,exception);
405 if (status == MagickFalse)
406 return(DestroyImageList(image));
cristy3ed852e2009-09-05 21:47:34 +0000407 image->storage_class=PseudoClass;
408 bits_per_pixel=pdb_image.type == 0 ? 2UL : pdb_image.type == 2 ? 4UL : 1UL;
cristyeaedf062010-05-29 22:36:02 +0000409 one=1;
cristy018f07f2011-09-04 21:15:19 +0000410 if (AcquireImageColormap(image,one << bits_per_pixel,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000411 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
412 if (image_info->ping != MagickFalse)
413 {
414 (void) CloseBlob(image);
415 return(GetFirstImageInList(image));
416 }
cristy55a26ef2014-11-29 17:16:29 +0000417 packets=(bits_per_pixel*image->columns+7)/8;
cristy3ed852e2009-09-05 21:47:34 +0000418 pixels=(unsigned char *) AcquireQuantumMemory(packets+256UL,image->rows*
419 sizeof(*pixels));
420 if (pixels == (unsigned char *) NULL)
421 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy98cba562014-11-20 23:46:19 +0000422 switch (pdb_image.version & 0x07)
cristy3ed852e2009-09-05 21:47:34 +0000423 {
424 case 0:
425 {
426 image->compression=NoCompression;
427 count=(ssize_t) ReadBlob(image, packets * image -> rows, pixels);
428 break;
429 }
430 case 1:
431 {
432 image->compression=RLECompression;
433 if (!DecodeImage(image, pixels, packets * image -> rows))
cristy98cba562014-11-20 23:46:19 +0000434 ThrowReaderException( CorruptImageError, "RLEDecoderError" );
cristy3ed852e2009-09-05 21:47:34 +0000435 break;
436 }
437 default:
438 ThrowReaderException(CorruptImageError,
cristyc5de6992009-10-06 19:19:48 +0000439 "UnrecognizedImageCompressionType" );
cristy3ed852e2009-09-05 21:47:34 +0000440 }
441 p=pixels;
442 switch (bits_per_pixel)
443 {
444 case 1:
445 {
446 int
447 bit;
448
449 /*
450 Read 1-bit PDB image.
451 */
cristybb503372010-05-27 20:51:26 +0000452 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000453 {
454 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000455 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000456 break;
cristybb503372010-05-27 20:51:26 +0000457 for (x=0; x < ((ssize_t) image->columns-7); x+=8)
cristy3ed852e2009-09-05 21:47:34 +0000458 {
459 for (bit=0; bit < 8; bit++)
460 {
cristy4c08aed2011-07-01 19:47:50 +0000461 index=(Quantum) (*p & (0x80 >> bit) ? 0x00 : 0x01);
462 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +0000463 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000464 }
465 p++;
466 }
467 if (SyncAuthenticPixels(image,exception) == MagickFalse)
468 break;
cristycee97112010-05-28 00:44:52 +0000469 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristyaff6d802011-04-26 01:46:31 +0000470 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000471 if (status == MagickFalse)
472 break;
473 }
cristyea1a8aa2011-10-20 13:24:06 +0000474 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000475 break;
476 }
477 case 2:
478 {
479 /*
480 Read 2-bit PDB image.
481 */
cristybb503372010-05-27 20:51:26 +0000482 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000483 {
484 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000485 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000486 break;
cristy29d40332014-12-30 23:48:22 +0000487 for (x=0; x < (ssize_t) image->columns-3; x+=4)
cristy3ed852e2009-09-05 21:47:34 +0000488 {
cristyc82a27b2011-10-21 01:07:16 +0000489 index=ConstrainColormapIndex(image,3UL-((*p >> 6) & 0x03),exception);
cristy4c08aed2011-07-01 19:47:50 +0000490 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +0000491 q+=GetPixelChannels(image);
cristyc82a27b2011-10-21 01:07:16 +0000492 index=ConstrainColormapIndex(image,3UL-((*p >> 4) & 0x03),exception);
cristy4c08aed2011-07-01 19:47:50 +0000493 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +0000494 q+=GetPixelChannels(image);
cristyc82a27b2011-10-21 01:07:16 +0000495 index=ConstrainColormapIndex(image,3UL-((*p >> 2) & 0x03),exception);
cristy4c08aed2011-07-01 19:47:50 +0000496 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +0000497 q+=GetPixelChannels(image);
cristyc82a27b2011-10-21 01:07:16 +0000498 index=ConstrainColormapIndex(image,3UL-((*p) & 0x03),exception);
cristy4c08aed2011-07-01 19:47:50 +0000499 SetPixelIndex(image,index,q);
cristy3ed852e2009-09-05 21:47:34 +0000500 p++;
cristyed231572011-07-14 02:18:59 +0000501 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000502 }
503 if (SyncAuthenticPixels(image,exception) == MagickFalse)
504 break;
cristycee97112010-05-28 00:44:52 +0000505 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristyaff6d802011-04-26 01:46:31 +0000506 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000507 if (status == MagickFalse)
508 break;
509 }
cristyea1a8aa2011-10-20 13:24:06 +0000510 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000511 break;
512 }
513 case 4:
514 {
515 /*
516 Read 4-bit PDB image.
517 */
cristybb503372010-05-27 20:51:26 +0000518 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000519 {
520 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000521 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000522 break;
cristy29d40332014-12-30 23:48:22 +0000523 for (x=0; x < (ssize_t) image->columns-1; x+=2)
cristy3ed852e2009-09-05 21:47:34 +0000524 {
cristyc82a27b2011-10-21 01:07:16 +0000525 index=ConstrainColormapIndex(image,15UL-((*p >> 4) & 0x0f),exception);
cristy4c08aed2011-07-01 19:47:50 +0000526 SetPixelIndex(image,index,q);
cristyed231572011-07-14 02:18:59 +0000527 q+=GetPixelChannels(image);
cristyc82a27b2011-10-21 01:07:16 +0000528 index=ConstrainColormapIndex(image,15UL-((*p) & 0x0f),exception);
cristy4c08aed2011-07-01 19:47:50 +0000529 SetPixelIndex(image,index,q);
cristy3ed852e2009-09-05 21:47:34 +0000530 p++;
cristyed231572011-07-14 02:18:59 +0000531 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000532 }
533 if (SyncAuthenticPixels(image,exception) == MagickFalse)
534 break;
cristycee97112010-05-28 00:44:52 +0000535 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristyaff6d802011-04-26 01:46:31 +0000536 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000537 if (status == MagickFalse)
538 break;
539 }
cristyea1a8aa2011-10-20 13:24:06 +0000540 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000541 break;
542 }
543 default:
544 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
545 }
cristy3ed852e2009-09-05 21:47:34 +0000546 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
547 if (EOFBlob(image) != MagickFalse)
548 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
549 image->filename);
cristy98cba562014-11-20 23:46:19 +0000550 if (pdb_info.number_records > 1)
cristy3ed852e2009-09-05 21:47:34 +0000551 {
552 char
553 *comment;
554
555 int
556 c;
557
558 register char
559 *p;
560
cristybb503372010-05-27 20:51:26 +0000561 size_t
cristy3ed852e2009-09-05 21:47:34 +0000562 length;
563
cristybb503372010-05-27 20:51:26 +0000564 num_pad_bytes = (size_t) (comment_offset - TellBlob( image ));
cristy3ed852e2009-09-05 21:47:34 +0000565 while (num_pad_bytes--) ReadBlobByte( image );
566
567 /*
568 Read comment.
569 */
570 c=ReadBlobByte(image);
cristy151b66d2015-04-15 10:50:31 +0000571 length=MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +0000572 comment=AcquireString((char *) NULL);
573 for (p=comment; c != EOF; p++)
574 {
cristy151b66d2015-04-15 10:50:31 +0000575 if ((size_t) (p-comment+MagickPathExtent) >= length)
cristy3ed852e2009-09-05 21:47:34 +0000576 {
577 *p='\0';
578 length<<=1;
cristy151b66d2015-04-15 10:50:31 +0000579 length+=MagickPathExtent;
580 comment=(char *) ResizeQuantumMemory(comment,length+MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000581 sizeof(*comment));
582 if (comment == (char *) NULL)
583 break;
584 p=comment+strlen(comment);
585 }
586 *p=c;
587 c=ReadBlobByte(image);
588 }
589 *p='\0';
590 if (comment == (char *) NULL)
591 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristyd15e6592011-10-15 00:13:06 +0000592 (void) SetImageProperty(image,"comment",comment,exception);
cristy3ed852e2009-09-05 21:47:34 +0000593 comment=DestroyString(comment);
594 }
595 (void) CloseBlob(image);
596 return(GetFirstImageInList(image));
597}
598
599/*
600%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
601% %
602% %
603% %
604% R e g i s t e r P D B I m a g e %
605% %
606% %
607% %
608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609%
610% RegisterPDBImage() adds properties for the PDB image format to
611% the list of supported formats. The properties include the image format
612% tag, a method to read and/or write the format, whether the format
613% supports the saving of more than one frame to the same file or blob,
614% whether the format supports native in-memory I/O, and a brief
615% description of the format.
616%
617% The format of the RegisterPDBImage method is:
618%
cristybb503372010-05-27 20:51:26 +0000619% size_t RegisterPDBImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000620%
621*/
cristybb503372010-05-27 20:51:26 +0000622ModuleExport size_t RegisterPDBImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000623{
624 MagickInfo
625 *entry;
626
dirk06b627a2015-04-06 18:59:17 +0000627 entry=AcquireMagickInfo("PDB","PDB","Palm Database ImageViewer Format");
cristy3ed852e2009-09-05 21:47:34 +0000628 entry->decoder=(DecodeImageHandler *) ReadPDBImage;
629 entry->encoder=(EncodeImageHandler *) WritePDBImage;
630 entry->magick=(IsImageFormatHandler *) IsPDB;
cristy3ed852e2009-09-05 21:47:34 +0000631 (void) RegisterMagickInfo(entry);
632 return(MagickImageCoderSignature);
633}
634
635/*
636%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
637% %
638% %
639% %
640% U n r e g i s t e r P D B I m a g e %
641% %
642% %
643% %
644%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
645%
646% UnregisterPDBImage() removes format registrations made by the
647% PDB module from the list of supported formats.
648%
649% The format of the UnregisterPDBImage method is:
650%
651% UnregisterPDBImage(void)
652%
653*/
654ModuleExport void UnregisterPDBImage(void)
655{
656 (void) UnregisterMagickInfo("PDB");
657}
658
659/*
660%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
661% %
662% %
663% %
664% W r i t e P D B I m a g e %
665% %
666% %
667% %
668%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
669%
670% WritePDBImage() writes an image
671%
672% The format of the WritePDBImage method is:
673%
cristy1e178e72011-08-28 19:44:34 +0000674% MagickBooleanType WritePDBImage(const ImageInfo *image_info,
675% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000676%
677% A description of each parameter follows.
678%
679% o image_info: the image info.
680%
681% o image: The image.
682%
cristy1e178e72011-08-28 19:44:34 +0000683% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +0000684%
685*/
686
687static unsigned char *EncodeRLE(unsigned char *destination,
cristybb503372010-05-27 20:51:26 +0000688 unsigned char *source,size_t literal,size_t repeat)
cristy3ed852e2009-09-05 21:47:34 +0000689{
690 if (literal > 0)
691 *destination++=(unsigned char) (literal-1);
692 (void) CopyMagickMemory(destination,source,literal);
693 destination+=literal;
694 if (repeat > 0)
695 {
696 *destination++=(unsigned char) (0x80 | (repeat-1));
697 *destination++=source[literal];
698 }
699 return(destination);
700}
701
cristy1e178e72011-08-28 19:44:34 +0000702static MagickBooleanType WritePDBImage(const ImageInfo *image_info,Image *image,
703 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000704{
705 const char
706 *comment;
707
708 int
709 bits;
710
cristy3ed852e2009-09-05 21:47:34 +0000711 MagickBooleanType
712 status;
713
714 PDBImage
715 pdb_image;
716
717 PDBInfo
718 pdb_info;
719
720 QuantumInfo
721 *quantum_info;
722
cristy4c08aed2011-07-01 19:47:50 +0000723 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000724 *p;
725
cristybb503372010-05-27 20:51:26 +0000726 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000727 x;
728
729 register unsigned char
730 *q;
731
732 size_t
cristyaff6d802011-04-26 01:46:31 +0000733 bits_per_pixel,
734 literal,
735 packets,
736 packet_size,
737 repeat;
738
739 ssize_t
740 y;
cristy3ed852e2009-09-05 21:47:34 +0000741
742 unsigned char
743 *buffer,
744 *runlength,
745 *scanline;
746
cristy3ed852e2009-09-05 21:47:34 +0000747 /*
748 Open output image file.
749 */
750 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000751 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000752 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000753 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000754 if (image->debug != MagickFalse)
755 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000756 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000757 assert(exception->signature == MagickCoreSignature);
cristy1e178e72011-08-28 19:44:34 +0000758 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000759 if (status == MagickFalse)
760 return(status);
cristyaf8d3912014-02-21 14:50:33 +0000761 (void) TransformImageColorspace(image,sRGBColorspace,exception);
dirkab4f0bb2015-07-25 11:46:32 +0000762 if (SetImageMonochrome(image,exception) != MagickFalse) {
cristy98cba562014-11-20 23:46:19 +0000763 bits_per_pixel=1;
764 } else if (image->colors <= 4) {
765 bits_per_pixel=2;
766 } else if (image->colors <= 8) {
767 bits_per_pixel=3;
cristy3ed852e2009-09-05 21:47:34 +0000768 } else {
cristy98cba562014-11-20 23:46:19 +0000769 bits_per_pixel=4;
cristy3ed852e2009-09-05 21:47:34 +0000770 }
cristy72437802015-03-23 00:42:27 +0000771 (void) ResetMagickMemory(&pdb_info,0,sizeof(pdb_info));
cristyca793d72015-01-23 13:03:55 +0000772 (void) CopyMagickString(pdb_info.name,image_info->filename,
773 sizeof(pdb_info.name));
cristy3ed852e2009-09-05 21:47:34 +0000774 pdb_info.attributes=0;
775 pdb_info.version=0;
776 pdb_info.create_time=time(NULL);
777 pdb_info.modify_time=pdb_info.create_time;
778 pdb_info.archive_time=0;
779 pdb_info.modify_number=0;
780 pdb_info.application_info=0;
781 pdb_info.sort_info=0;
782 (void) CopyMagickMemory(pdb_info.type,"vIMG",4);
783 (void) CopyMagickMemory(pdb_info.id,"View",4);
784 pdb_info.seed=0;
785 pdb_info.next_record=0;
cristyd15e6592011-10-15 00:13:06 +0000786 comment=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +0000787 pdb_info.number_records=(comment == (const char *) NULL ? 1 : 2);
cristyca793d72015-01-23 13:03:55 +0000788 (void) WriteBlob(image,sizeof(pdb_info.name),(unsigned char *) pdb_info.name);
cristy3ed852e2009-09-05 21:47:34 +0000789 (void) WriteBlobMSBShort(image,(unsigned short) pdb_info.attributes);
790 (void) WriteBlobMSBShort(image,(unsigned short) pdb_info.version);
cristyeaedf062010-05-29 22:36:02 +0000791 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.create_time);
792 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.modify_time);
793 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.archive_time);
794 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.modify_number);
795 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.application_info);
796 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.sort_info);
cristy3ed852e2009-09-05 21:47:34 +0000797 (void) WriteBlob(image,4,(unsigned char *) pdb_info.type);
798 (void) WriteBlob(image,4,(unsigned char *) pdb_info.id);
cristyeaedf062010-05-29 22:36:02 +0000799 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.seed);
800 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.next_record);
cristy3ed852e2009-09-05 21:47:34 +0000801 (void) WriteBlobMSBShort(image,(unsigned short) pdb_info.number_records);
cristyca793d72015-01-23 13:03:55 +0000802 (void) CopyMagickString(pdb_image.name,pdb_info.name,sizeof(pdb_image.name));
cristy3ed852e2009-09-05 21:47:34 +0000803 pdb_image.version=1; /* RLE Compressed */
804 switch (bits_per_pixel)
805 {
dirk93b02b72013-11-16 16:03:36 +0000806 case 1: pdb_image.type=(unsigned char) 0xff; break; /* monochrome */
807 case 2: pdb_image.type=(unsigned char) 0x00; break; /* 2 bit gray */
808 default: pdb_image.type=(unsigned char) 0x02; /* 4 bit gray */
cristy3ed852e2009-09-05 21:47:34 +0000809 }
810 pdb_image.reserved_1=0;
811 pdb_image.note=0;
812 pdb_image.x_last=0;
813 pdb_image.y_last=0;
814 pdb_image.reserved_2=0;
dirk93b02b72013-11-16 16:03:36 +0000815 pdb_image.x_anchor=(unsigned short) 0xffff;
816 pdb_image.y_anchor=(unsigned short) 0xffff;
cristy3ed852e2009-09-05 21:47:34 +0000817 pdb_image.width=(short) image->columns;
818 if (image->columns % 16)
819 pdb_image.width=(short) (16*(image->columns/16+1));
820 pdb_image.height=(short) image->rows;
cristy7e5d1da2014-12-28 00:40:02 +0000821 packets=((bits_per_pixel*image->columns+7)/8);
cristy3ed852e2009-09-05 21:47:34 +0000822 runlength=(unsigned char *) AcquireQuantumMemory(2UL*packets,
cristy7e5d1da2014-12-28 00:40:02 +0000823 image->rows*sizeof(*runlength));
cristy3ed852e2009-09-05 21:47:34 +0000824 if (runlength == (unsigned char *) NULL)
825 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
826 buffer=(unsigned char *) AcquireQuantumMemory(256UL,sizeof(*buffer));
827 if (buffer == (unsigned char *) NULL)
828 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
829 packet_size=(size_t) (image->depth > 8 ? 2: 1);
830 scanline=(unsigned char *) AcquireQuantumMemory(image->columns,packet_size*
831 sizeof(*scanline));
832 if (scanline == (unsigned char *) NULL)
833 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3d9f5ba2012-06-26 13:37:31 +0000834 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +0000835 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000836 /*
837 Convert to GRAY raster scanline.
838 */
cristy5f766ef2014-12-14 21:12:47 +0000839 quantum_info=AcquireQuantumInfo(image_info,image);
cristy3ed852e2009-09-05 21:47:34 +0000840 if (quantum_info == (QuantumInfo *) NULL)
841 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristyf9cca6a2010-06-04 23:49:28 +0000842 bits=8/(int) bits_per_pixel-1; /* start at most significant bits */
cristy3ed852e2009-09-05 21:47:34 +0000843 literal=0;
844 repeat=0;
845 q=runlength;
846 buffer[0]=0x00;
cristybb503372010-05-27 20:51:26 +0000847 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000848 {
cristy1e178e72011-08-28 19:44:34 +0000849 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000850 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000851 break;
cristy4c08aed2011-07-01 19:47:50 +0000852 (void) ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
cristy1e178e72011-08-28 19:44:34 +0000853 GrayQuantum,scanline,exception);
cristy98cba562014-11-20 23:46:19 +0000854 for (x=0; x < (ssize_t) pdb_image.width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000855 {
cristybb503372010-05-27 20:51:26 +0000856 if (x < (ssize_t) image->columns)
cristy3ed852e2009-09-05 21:47:34 +0000857 buffer[literal+repeat]|=(0xff-scanline[x*packet_size]) >>
858 (8-bits_per_pixel) << bits*bits_per_pixel;
859 bits--;
860 if (bits < 0)
861 {
862 if (((literal+repeat) > 0) &&
863 (buffer[literal+repeat] == buffer[literal+repeat-1]))
864 {
865 if (repeat == 0)
866 {
867 literal--;
868 repeat++;
869 }
870 repeat++;
871 if (0x7f < repeat)
872 {
873 q=EncodeRLE(q,buffer,literal,repeat);
874 literal=0;
875 repeat=0;
876 }
877 }
878 else
879 {
880 if (repeat >= 2)
881 literal+=repeat;
882 else
883 {
884 q=EncodeRLE(q,buffer,literal,repeat);
885 buffer[0]=buffer[literal+repeat];
886 literal=0;
887 }
888 literal++;
889 repeat=0;
890 if (0x7f < literal)
891 {
892 q=EncodeRLE(q,buffer,(literal < 0x80 ? literal : 0x80),0);
893 (void) CopyMagickMemory(buffer,buffer+literal+repeat,0x80);
894 literal-=0x80;
895 }
896 }
cristyf9cca6a2010-06-04 23:49:28 +0000897 bits=8/(int) bits_per_pixel-1;
cristy3ed852e2009-09-05 21:47:34 +0000898 buffer[literal+repeat]=0x00;
899 }
900 }
cristycee97112010-05-28 00:44:52 +0000901 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy98cba562014-11-20 23:46:19 +0000902 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000903 if (status == MagickFalse)
904 break;
905 }
906 q=EncodeRLE(q,buffer,literal,repeat);
907 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
908 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
909 quantum_info=DestroyQuantumInfo(quantum_info);
910 /*
911 Write the Image record header.
912 */
cristyf6fe0a12010-05-30 00:44:47 +0000913 (void) WriteBlobMSBLong(image,(unsigned int)
cristy3ed852e2009-09-05 21:47:34 +0000914 (TellBlob(image)+8*pdb_info.number_records));
915 (void) WriteBlobByte(image,0x40);
916 (void) WriteBlobByte(image,0x6f);
917 (void) WriteBlobByte(image,0x80);
918 (void) WriteBlobByte(image,0);
919 if (pdb_info.number_records > 1)
920 {
921 /*
922 Write the comment record header.
923 */
cristyf6fe0a12010-05-30 00:44:47 +0000924 (void) WriteBlobMSBLong(image,(unsigned int) (TellBlob(image)+8+58+q-
cristy3ed852e2009-09-05 21:47:34 +0000925 runlength));
926 (void) WriteBlobByte(image,0x40);
927 (void) WriteBlobByte(image,0x6f);
928 (void) WriteBlobByte(image,0x80);
929 (void) WriteBlobByte(image,1);
930 }
931 /*
932 Write the Image data.
933 */
cristyca793d72015-01-23 13:03:55 +0000934 (void) WriteBlob(image,sizeof(pdb_image.name),(unsigned char *)
935 pdb_image.name);
cristy3ed852e2009-09-05 21:47:34 +0000936 (void) WriteBlobByte(image,(unsigned char) pdb_image.version);
dirk93b02b72013-11-16 16:03:36 +0000937 (void) WriteBlobByte(image,pdb_image.type);
cristyeaedf062010-05-29 22:36:02 +0000938 (void) WriteBlobMSBLong(image,(unsigned int) pdb_image.reserved_1);
939 (void) WriteBlobMSBLong(image,(unsigned int) pdb_image.note);
cristy3ed852e2009-09-05 21:47:34 +0000940 (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.x_last);
941 (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.y_last);
cristyeaedf062010-05-29 22:36:02 +0000942 (void) WriteBlobMSBLong(image,(unsigned int) pdb_image.reserved_2);
dirk93b02b72013-11-16 16:03:36 +0000943 (void) WriteBlobMSBShort(image,pdb_image.x_anchor);
944 (void) WriteBlobMSBShort(image,pdb_image.y_anchor);
cristy3ed852e2009-09-05 21:47:34 +0000945 (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.width);
946 (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.height);
947 (void) WriteBlob(image,(size_t) (q-runlength),runlength);
948 runlength=(unsigned char *) RelinquishMagickMemory(runlength);
949 if (pdb_info.number_records > 1)
950 (void) WriteBlobString(image,comment);
951 (void) CloseBlob(image);
952 return(MagickTrue);
953}