blob: 3d4115fdd5303154133cb60ba2a2ef5439c5a68f [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% JJJJJ BBBB IIIII GGGG %
7% J B B I G %
8% J BBBB I G GG %
9% J J B B I G G %
10% JJJ BBBB IIIII GGG %
11% %
12% %
13% Read/Write JBIG Image Format %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 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/blob.h"
44#include "magick/blob-private.h"
45#include "magick/cache.h"
46#include "magick/color-private.h"
cristyf4cf3072010-04-25 20:07:02 +000047#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +000048#include "magick/colorspace.h"
49#include "magick/constitute.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/geometry.h"
53#include "magick/image.h"
54#include "magick/image-private.h"
55#include "magick/list.h"
56#include "magick/magick.h"
57#include "magick/memory_.h"
58#include "magick/monitor.h"
59#include "magick/monitor-private.h"
60#include "magick/nt-feature.h"
61#include "magick/quantum-private.h"
62#include "magick/static.h"
63#include "magick/string_.h"
64#include "magick/module.h"
65#if defined(MAGICKCORE_JBIG_DELEGATE)
66#include "jbig.h"
67#endif
68
69/*
70 Forward declarations.
71*/
72#if defined(MAGICKCORE_JBIG_DELEGATE)
73static MagickBooleanType
74 WriteJBIGImage(const ImageInfo *,Image *);
75#endif
76
77#if defined(MAGICKCORE_JBIG_DELEGATE)
78/*
79%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80% %
81% %
82% %
83% R e a d J B I G I m a g e %
84% %
85% %
86% %
87%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88%
89% ReadJBIGImage() reads a JBIG image file and returns it. It
90% allocates the memory necessary for the new Image structure and returns a
91% pointer to the new image.
92%
93% The format of the ReadJBIGImage method is:
94%
95% Image *ReadJBIGImage(const ImageInfo *image_info,
96% ExceptionInfo *exception)
97%
98% A description of each parameter follows:
99%
100% o image_info: the image info.
101%
102% o exception: return any errors or warnings in this structure.
103%
104*/
105static Image *ReadJBIGImage(const ImageInfo *image_info,
106 ExceptionInfo *exception)
107{
108 Image
109 *image;
110
111 IndexPacket
112 index;
113
cristybb503372010-05-27 20:51:26 +0000114 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000115 length,
116 y;
117
118 MagickBooleanType
119 status;
120
121 register IndexPacket
122 *indexes;
123
cristybb503372010-05-27 20:51:26 +0000124 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000125 x;
126
127 register PixelPacket
128 *q;
129
130 register unsigned char
131 *p;
132
133 ssize_t
134 count;
135
136 struct jbg_dec_state
137 jbig_info;
138
139 unsigned char
140 bit,
141 *buffer,
142 byte;
143
144 /*
145 Open image file.
146 */
147 assert(image_info != (const ImageInfo *) NULL);
148 assert(image_info->signature == MagickSignature);
149 if (image_info->debug != MagickFalse)
150 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
151 image_info->filename);
152 assert(exception != (ExceptionInfo *) NULL);
153 assert(exception->signature == MagickSignature);
154 image=AcquireImage(image_info);
155 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
156 if (status == MagickFalse)
157 {
158 image=DestroyImageList(image);
159 return((Image *) NULL);
160 }
161 /*
162 Initialize JBIG toolkit.
163 */
164 jbg_dec_init(&jbig_info);
cristyf6fe0a12010-05-30 00:44:47 +0000165 jbg_dec_maxsize(&jbig_info,(unsigned long) image->columns,(unsigned long)
cristy3ed852e2009-09-05 21:47:34 +0000166 image->rows);
167 image->columns=jbg_dec_getwidth(&jbig_info);
168 image->rows=jbg_dec_getheight(&jbig_info);
169 image->depth=8;
170 image->storage_class=PseudoClass;
171 image->colors=2;
172 /*
173 Read JBIG file.
174 */
175 buffer=(unsigned char *) AcquireQuantumMemory(MagickMaxBufferExtent,
176 sizeof(*buffer));
177 if (buffer == (unsigned char *) NULL)
178 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
179 status=JBG_EAGAIN;
180 do
181 {
cristybb503372010-05-27 20:51:26 +0000182 length=(ssize_t) ReadBlob(image,MagickMaxBufferExtent,buffer);
cristy3ed852e2009-09-05 21:47:34 +0000183 if (length == 0)
184 break;
185 p=buffer;
186 count=0;
187 while ((length > 0) && ((status == JBG_EAGAIN) || (status == JBG_EOK)))
188 {
189 size_t
190 count;
191
192 status=jbg_dec_in(&jbig_info,p,length,&count);
193 p+=count;
cristybb503372010-05-27 20:51:26 +0000194 length-=(ssize_t) count;
cristy3ed852e2009-09-05 21:47:34 +0000195 }
196 } while ((status == JBG_EAGAIN) || (status == JBG_EOK));
197 /*
198 Create colormap.
199 */
200 image->columns=jbg_dec_getwidth(&jbig_info);
201 image->rows=jbg_dec_getheight(&jbig_info);
202 if (AcquireImageColormap(image,2) == MagickFalse)
203 {
204 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
205 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
206 }
207 image->colormap[0].red=0;
208 image->colormap[0].green=0;
209 image->colormap[0].blue=0;
210 image->colormap[1].red=QuantumRange;
211 image->colormap[1].green=QuantumRange;
212 image->colormap[1].blue=QuantumRange;
213 image->x_resolution=300;
214 image->y_resolution=300;
215 if (image_info->ping != MagickFalse)
216 {
217 (void) CloseBlob(image);
218 return(GetFirstImageInList(image));
219 }
220 /*
221 Convert X bitmap image to pixel packets.
222 */
223 p=jbg_dec_getimage(&jbig_info,0);
cristybb503372010-05-27 20:51:26 +0000224 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000225 {
226 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
227 if (q == (PixelPacket *) NULL)
228 break;
229 indexes=GetAuthenticIndexQueue(image);
230 bit=0;
231 byte=0;
cristybb503372010-05-27 20:51:26 +0000232 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000233 {
234 if (bit == 0)
235 byte=(*p++);
236 index=(byte & 0x80) ? 0 : 1;
237 bit++;
238 byte<<=1;
239 if (bit == 8)
240 bit=0;
241 indexes[x]=index;
cristybb503372010-05-27 20:51:26 +0000242 *q++=image->colormap[(ssize_t) index];
cristy3ed852e2009-09-05 21:47:34 +0000243 }
244 if (SyncAuthenticPixels(image,exception) == MagickFalse)
245 break;
cristycee97112010-05-28 00:44:52 +0000246 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
247 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000248 if (status == MagickFalse)
249 break;
250 }
251 /*
252 Free scale resource.
253 */
254 jbg_dec_free(&jbig_info);
255 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
256 (void) CloseBlob(image);
257 return(GetFirstImageInList(image));
258}
259#endif
260
261/*
262%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
263% %
264% %
265% %
266% R e g i s t e r J B I G I m a g e %
267% %
268% %
269% %
270%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
271%
272% RegisterJBIGImage() adds attributes for the JBIG image format to
273% the list of supported formats. The attributes include the image format
274% tag, a method to read and/or write the format, whether the format
275% supports the saving of more than one frame to the same file or blob,
276% whether the format supports native in-memory I/O, and a brief
277% description of the format.
278%
279% The format of the RegisterJBIGImage method is:
280%
cristybb503372010-05-27 20:51:26 +0000281% size_t RegisterJBIGImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000282%
283*/
cristybb503372010-05-27 20:51:26 +0000284ModuleExport size_t RegisterJBIGImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000285{
286#define JBIGDescription "Joint Bi-level Image experts Group interchange format"
287
288 char
289 version[MaxTextExtent];
290
291 MagickInfo
292 *entry;
293
294 *version='\0';
295#if defined(JBG_VERSION)
296 (void) CopyMagickString(version,JBG_VERSION,MaxTextExtent);
297#endif
298 entry=SetMagickInfo("BIE");
299#if defined(MAGICKCORE_JBIG_DELEGATE)
300 entry->decoder=(DecodeImageHandler *) ReadJBIGImage;
301 entry->encoder=(EncodeImageHandler *) WriteJBIGImage;
302#endif
303 entry->adjoin=MagickFalse;
304 entry->description=ConstantString(JBIGDescription);
305 if (*version != '\0')
306 entry->version=ConstantString(version);
307 entry->module=ConstantString("JBIG");
308 (void) RegisterMagickInfo(entry);
309 entry=SetMagickInfo("JBG");
310#if defined(MAGICKCORE_JBIG_DELEGATE)
311 entry->decoder=(DecodeImageHandler *) ReadJBIGImage;
312 entry->encoder=(EncodeImageHandler *) WriteJBIGImage;
313#endif
314 entry->description=ConstantString(JBIGDescription);
315 if (*version != '\0')
316 entry->version=ConstantString(version);
317 entry->module=ConstantString("JBIG");
318 (void) RegisterMagickInfo(entry);
319 entry=SetMagickInfo("JBIG");
320#if defined(MAGICKCORE_JBIG_DELEGATE)
321 entry->decoder=(DecodeImageHandler *) ReadJBIGImage;
322 entry->encoder=(EncodeImageHandler *) WriteJBIGImage;
323#endif
324 entry->description=ConstantString(JBIGDescription);
325 if (*version != '\0')
326 entry->version=ConstantString(version);
327 entry->module=ConstantString("JBIG");
328 (void) RegisterMagickInfo(entry);
329 return(MagickImageCoderSignature);
330}
331
332/*
333%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
334% %
335% %
336% %
337% U n r e g i s t e r J B I G I m a g e %
338% %
339% %
340% %
341%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
342%
343% UnregisterJBIGImage() removes format registrations made by the
344% JBIG module from the list of supported formats.
345%
346% The format of the UnregisterJBIGImage method is:
347%
348% UnregisterJBIGImage(void)
349%
350*/
351ModuleExport void UnregisterJBIGImage(void)
352{
353 (void) UnregisterMagickInfo("BIE");
354 (void) UnregisterMagickInfo("JBG");
355 (void) UnregisterMagickInfo("JBIG");
356}
357
358#if defined(MAGICKCORE_JBIG_DELEGATE)
359/*
360%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
361% %
362% %
363% %
364% W r i t e J B I G I m a g e %
365% %
366% %
367% %
368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
369%
370% WriteJBIGImage() writes an image in the JBIG encoded image format.
371%
372% The format of the WriteJBIGImage method is:
373%
374% MagickBooleanType WriteJBIGImage(const ImageInfo *image_info,Image *image)
375%
376% A description of each parameter follows.
377%
378% o image_info: the image info.
379%
380% o image: The image.
381%
382%
383*/
384
385static void JBIGEncode(unsigned char *pixels,size_t length,void *data)
386{
387 Image
388 *image;
389
390 image=(Image *) data;
391 (void) WriteBlob(image,length,pixels);
392}
393
394static MagickBooleanType WriteJBIGImage(const ImageInfo *image_info,
395 Image *image)
396{
397 double
398 version;
399
cristybb503372010-05-27 20:51:26 +0000400 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000401 y;
402
403 MagickBooleanType
404 status;
405
406 MagickOffsetType
407 scene;
408
409 register const IndexPacket
410 *indexes;
411
412 register const PixelPacket
413 *p;
414
cristybb503372010-05-27 20:51:26 +0000415 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000416 x;
417
418 register unsigned char
419 *q;
420
421 struct jbg_enc_state
422 jbig_info;
423
424 unsigned char
425 bit,
426 byte,
427 *pixels;
428
cristybb503372010-05-27 20:51:26 +0000429 size_t
cristy3ed852e2009-09-05 21:47:34 +0000430 number_packets;
431
432 /*
433 Open image file.
434 */
435 assert(image_info != (const ImageInfo *) NULL);
436 assert(image_info->signature == MagickSignature);
437 assert(image != (Image *) NULL);
438 assert(image->signature == MagickSignature);
439 if (image->debug != MagickFalse)
440 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
441 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
442 if (status == MagickFalse)
443 return(status);
444 version=strtod(JBG_VERSION,(char **) NULL);
445 scene=0;
446 do
447 {
448 /*
449 Allocate pixel data.
450 */
451 if (image->colorspace != RGBColorspace)
452 (void) TransformImageColorspace(image,RGBColorspace);
453 number_packets=(image->columns+7)/8;
454 pixels=(unsigned char *) AcquireQuantumMemory(number_packets,
455 image->rows*sizeof(*pixels));
456 if (pixels == (unsigned char *) NULL)
457 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
458 /*
459 Convert pixels to a bitmap.
460 */
461 (void) SetImageType(image,BilevelType);
462 q=pixels;
cristybb503372010-05-27 20:51:26 +0000463 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000464 {
465 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
466 if (p == (const PixelPacket *) NULL)
467 break;
468 indexes=GetVirtualIndexQueue(image);
469 bit=0;
470 byte=0;
cristybb503372010-05-27 20:51:26 +0000471 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000472 {
473 byte<<=1;
474 if (PixelIntensity(p) < (QuantumRange/2.0))
475 byte|=0x01;
476 bit++;
477 if (bit == 8)
478 {
479 *q++=byte;
480 bit=0;
481 byte=0;
482 }
483 p++;
484 }
485 if (bit != 0)
486 *q++=byte << (8-bit);
487 if (image->previous == (Image *) NULL)
488 {
cristycee97112010-05-28 00:44:52 +0000489 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
490 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000491 if (status == MagickFalse)
492 break;
493 }
494 }
495 /*
496 Initialize JBIG info structure.
497 */
cristyf6fe0a12010-05-30 00:44:47 +0000498 jbg_enc_init(&jbig_info,(unsigned long) image->columns,(unsigned long)
499 image->rows,1,&pixels,(void (*)(unsigned char *,size_t,void *))
500 JBIGEncode,image);
cristy3ed852e2009-09-05 21:47:34 +0000501 if (image_info->scene != 0)
502 jbg_enc_layers(&jbig_info,(int) image_info->scene);
503 else
504 {
cristybb503372010-05-27 20:51:26 +0000505 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000506 sans_offset;
507
cristybb503372010-05-27 20:51:26 +0000508 size_t
cristy3ed852e2009-09-05 21:47:34 +0000509 x_resolution,
510 y_resolution;
511
512 x_resolution=640;
513 y_resolution=480;
514 sans_offset=0;
515 if (image_info->density != (char *) NULL)
516 {
517 GeometryInfo
518 geometry_info;
519
520 MagickStatusType
521 flags;
522
523 flags=ParseGeometry(image_info->density,&geometry_info);
524 x_resolution=geometry_info.rho;
525 y_resolution=geometry_info.sigma;
526 if ((flags & SigmaValue) == 0)
527 y_resolution=x_resolution;
528 }
529 if (image->units == PixelsPerCentimeterResolution)
530 {
cristybb503372010-05-27 20:51:26 +0000531 x_resolution=(size_t) (100.0*2.54*x_resolution+0.5)/100.0;
532 y_resolution=(size_t) (100.0*2.54*y_resolution+0.5)/100.0;
cristy3ed852e2009-09-05 21:47:34 +0000533 }
cristyf6fe0a12010-05-30 00:44:47 +0000534 (void) jbg_enc_lrlmax(&jbig_info,(unsigned long) x_resolution,
535 (unsigned long) y_resolution);
cristy3ed852e2009-09-05 21:47:34 +0000536 }
537 (void) jbg_enc_lrange(&jbig_info,-1,-1);
538 jbg_enc_options(&jbig_info,JBG_ILEAVE | JBG_SMID,JBG_TPDON | JBG_TPBON |
539 JBG_DPON,version < 1.6 ? -1 : 0,-1,-1);
540 /*
541 Write JBIG image.
542 */
543 jbg_enc_out(&jbig_info);
544 jbg_enc_free(&jbig_info);
545 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
546 if (GetNextImageInList(image) == (Image *) NULL)
547 break;
548 image=SyncNextImageInList(image);
549 status=SetImageProgress(image,SaveImagesTag,scene++,
550 GetImageListLength(image));
551 if (status == MagickFalse)
552 break;
553 } while (image_info->adjoin != MagickFalse);
554 (void) CloseBlob(image);
555 return(MagickTrue);
556}
557#endif