blob: da1e447ec3eded2d62d34d301c3c2405ec32d454 [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% %
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/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
cristy3ed852e2009-09-05 21:47:34 +0000114 MagickBooleanType
115 status;
116
117 register IndexPacket
118 *indexes;
119
cristybb503372010-05-27 20:51:26 +0000120 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000121 x;
122
123 register PixelPacket
124 *q;
125
126 register unsigned char
127 *p;
128
129 ssize_t
cristy524222d2011-04-25 00:37:06 +0000130 count,
131 length,
132 y;
cristy3ed852e2009-09-05 21:47:34 +0000133
134 struct jbg_dec_state
135 jbig_info;
136
137 unsigned char
138 bit,
139 *buffer,
140 byte;
141
142 /*
143 Open image file.
144 */
145 assert(image_info != (const ImageInfo *) NULL);
146 assert(image_info->signature == MagickSignature);
147 if (image_info->debug != MagickFalse)
148 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
149 image_info->filename);
150 assert(exception != (ExceptionInfo *) NULL);
151 assert(exception->signature == MagickSignature);
152 image=AcquireImage(image_info);
153 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
154 if (status == MagickFalse)
155 {
156 image=DestroyImageList(image);
157 return((Image *) NULL);
158 }
159 /*
160 Initialize JBIG toolkit.
161 */
162 jbg_dec_init(&jbig_info);
cristyf6fe0a12010-05-30 00:44:47 +0000163 jbg_dec_maxsize(&jbig_info,(unsigned long) image->columns,(unsigned long)
cristy3ed852e2009-09-05 21:47:34 +0000164 image->rows);
165 image->columns=jbg_dec_getwidth(&jbig_info);
166 image->rows=jbg_dec_getheight(&jbig_info);
167 image->depth=8;
168 image->storage_class=PseudoClass;
169 image->colors=2;
170 /*
171 Read JBIG file.
172 */
173 buffer=(unsigned char *) AcquireQuantumMemory(MagickMaxBufferExtent,
174 sizeof(*buffer));
175 if (buffer == (unsigned char *) NULL)
176 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
177 status=JBG_EAGAIN;
178 do
179 {
cristybb503372010-05-27 20:51:26 +0000180 length=(ssize_t) ReadBlob(image,MagickMaxBufferExtent,buffer);
cristy3ed852e2009-09-05 21:47:34 +0000181 if (length == 0)
182 break;
183 p=buffer;
184 count=0;
185 while ((length > 0) && ((status == JBG_EAGAIN) || (status == JBG_EOK)))
186 {
187 size_t
188 count;
189
190 status=jbg_dec_in(&jbig_info,p,length,&count);
191 p+=count;
cristybb503372010-05-27 20:51:26 +0000192 length-=(ssize_t) count;
cristy3ed852e2009-09-05 21:47:34 +0000193 }
194 } while ((status == JBG_EAGAIN) || (status == JBG_EOK));
195 /*
196 Create colormap.
197 */
198 image->columns=jbg_dec_getwidth(&jbig_info);
199 image->rows=jbg_dec_getheight(&jbig_info);
cristy6d5e20f2011-04-25 13:48:54 +0000200 image->compression=JBIG2Compression;
cristy3ed852e2009-09-05 21:47:34 +0000201 if (AcquireImageColormap(image,2) == MagickFalse)
202 {
203 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
204 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
205 }
206 image->colormap[0].red=0;
207 image->colormap[0].green=0;
208 image->colormap[0].blue=0;
209 image->colormap[1].red=QuantumRange;
210 image->colormap[1].green=QuantumRange;
211 image->colormap[1].blue=QuantumRange;
212 image->x_resolution=300;
213 image->y_resolution=300;
214 if (image_info->ping != MagickFalse)
215 {
216 (void) CloseBlob(image);
217 return(GetFirstImageInList(image));
218 }
219 /*
220 Convert X bitmap image to pixel packets.
221 */
222 p=jbg_dec_getimage(&jbig_info,0);
cristybb503372010-05-27 20:51:26 +0000223 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000224 {
225 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
226 if (q == (PixelPacket *) NULL)
227 break;
228 indexes=GetAuthenticIndexQueue(image);
229 bit=0;
230 byte=0;
cristybb503372010-05-27 20:51:26 +0000231 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000232 {
233 if (bit == 0)
234 byte=(*p++);
235 index=(byte & 0x80) ? 0 : 1;
236 bit++;
237 byte<<=1;
238 if (bit == 8)
239 bit=0;
cristy524222d2011-04-25 00:37:06 +0000240 SetIndexPixelComponent(indexes+x,index);
cristy6a1c5a92011-04-28 01:57:04 +0000241 SetRedPixelComponent(q,image->colormap[(ssize_t) index].red);
242 SetGreenPixelComponent(q,image->colormap[(ssize_t) index].green);
243 SetBluePixelComponent(q,image->colormap[(ssize_t) index].blue);
244 q++;
cristy3ed852e2009-09-05 21:47:34 +0000245 }
246 if (SyncAuthenticPixels(image,exception) == MagickFalse)
247 break;
cristycee97112010-05-28 00:44:52 +0000248 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +0000249 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000250 if (status == MagickFalse)
251 break;
252 }
253 /*
254 Free scale resource.
255 */
256 jbg_dec_free(&jbig_info);
257 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
258 (void) CloseBlob(image);
259 return(GetFirstImageInList(image));
260}
261#endif
262
263/*
264%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265% %
266% %
267% %
268% R e g i s t e r J B I G I m a g e %
269% %
270% %
271% %
272%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
273%
274% RegisterJBIGImage() adds attributes for the JBIG image format to
275% the list of supported formats. The attributes include the image format
276% tag, a method to read and/or write the format, whether the format
277% supports the saving of more than one frame to the same file or blob,
278% whether the format supports native in-memory I/O, and a brief
279% description of the format.
280%
281% The format of the RegisterJBIGImage method is:
282%
cristybb503372010-05-27 20:51:26 +0000283% size_t RegisterJBIGImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000284%
285*/
cristybb503372010-05-27 20:51:26 +0000286ModuleExport size_t RegisterJBIGImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000287{
288#define JBIGDescription "Joint Bi-level Image experts Group interchange format"
289
290 char
291 version[MaxTextExtent];
292
293 MagickInfo
294 *entry;
295
296 *version='\0';
297#if defined(JBG_VERSION)
298 (void) CopyMagickString(version,JBG_VERSION,MaxTextExtent);
299#endif
300 entry=SetMagickInfo("BIE");
301#if defined(MAGICKCORE_JBIG_DELEGATE)
302 entry->decoder=(DecodeImageHandler *) ReadJBIGImage;
303 entry->encoder=(EncodeImageHandler *) WriteJBIGImage;
304#endif
305 entry->adjoin=MagickFalse;
306 entry->description=ConstantString(JBIGDescription);
307 if (*version != '\0')
308 entry->version=ConstantString(version);
309 entry->module=ConstantString("JBIG");
310 (void) RegisterMagickInfo(entry);
311 entry=SetMagickInfo("JBG");
312#if defined(MAGICKCORE_JBIG_DELEGATE)
313 entry->decoder=(DecodeImageHandler *) ReadJBIGImage;
314 entry->encoder=(EncodeImageHandler *) WriteJBIGImage;
315#endif
316 entry->description=ConstantString(JBIGDescription);
317 if (*version != '\0')
318 entry->version=ConstantString(version);
319 entry->module=ConstantString("JBIG");
320 (void) RegisterMagickInfo(entry);
321 entry=SetMagickInfo("JBIG");
322#if defined(MAGICKCORE_JBIG_DELEGATE)
323 entry->decoder=(DecodeImageHandler *) ReadJBIGImage;
324 entry->encoder=(EncodeImageHandler *) WriteJBIGImage;
325#endif
326 entry->description=ConstantString(JBIGDescription);
327 if (*version != '\0')
328 entry->version=ConstantString(version);
329 entry->module=ConstantString("JBIG");
330 (void) RegisterMagickInfo(entry);
331 return(MagickImageCoderSignature);
332}
333
334/*
335%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
336% %
337% %
338% %
339% U n r e g i s t e r J B I G I m a g e %
340% %
341% %
342% %
343%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
344%
345% UnregisterJBIGImage() removes format registrations made by the
346% JBIG module from the list of supported formats.
347%
348% The format of the UnregisterJBIGImage method is:
349%
350% UnregisterJBIGImage(void)
351%
352*/
353ModuleExport void UnregisterJBIGImage(void)
354{
355 (void) UnregisterMagickInfo("BIE");
356 (void) UnregisterMagickInfo("JBG");
357 (void) UnregisterMagickInfo("JBIG");
358}
359
360#if defined(MAGICKCORE_JBIG_DELEGATE)
361/*
362%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
363% %
364% %
365% %
366% W r i t e J B I G I m a g e %
367% %
368% %
369% %
370%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
371%
372% WriteJBIGImage() writes an image in the JBIG encoded image format.
373%
374% The format of the WriteJBIGImage method is:
375%
376% MagickBooleanType WriteJBIGImage(const ImageInfo *image_info,Image *image)
377%
378% A description of each parameter follows.
379%
380% o image_info: the image info.
381%
382% o image: The image.
383%
384%
385*/
386
387static void JBIGEncode(unsigned char *pixels,size_t length,void *data)
388{
389 Image
390 *image;
391
392 image=(Image *) data;
393 (void) WriteBlob(image,length,pixels);
394}
395
396static MagickBooleanType WriteJBIGImage(const ImageInfo *image_info,
397 Image *image)
398{
399 double
400 version;
401
cristy3ed852e2009-09-05 21:47:34 +0000402 MagickBooleanType
403 status;
404
405 MagickOffsetType
406 scene;
407
408 register const IndexPacket
409 *indexes;
410
411 register const PixelPacket
412 *p;
413
cristybb503372010-05-27 20:51:26 +0000414 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000415 x;
416
417 register unsigned char
418 *q;
419
cristy524222d2011-04-25 00:37:06 +0000420 size_t
421 number_packets;
422
423 ssize_t
424 y;
425
cristy3ed852e2009-09-05 21:47:34 +0000426 struct jbg_enc_state
427 jbig_info;
428
429 unsigned char
430 bit,
431 byte,
432 *pixels;
433
cristy3ed852e2009-09-05 21:47:34 +0000434 /*
435 Open image file.
436 */
437 assert(image_info != (const ImageInfo *) NULL);
438 assert(image_info->signature == MagickSignature);
439 assert(image != (Image *) NULL);
440 assert(image->signature == MagickSignature);
441 if (image->debug != MagickFalse)
442 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
443 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
444 if (status == MagickFalse)
445 return(status);
cristy0df696d2011-05-18 19:55:22 +0000446 version=StringToDouble(JBG_VERSION,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000447 scene=0;
448 do
449 {
450 /*
451 Allocate pixel data.
452 */
453 if (image->colorspace != RGBColorspace)
454 (void) TransformImageColorspace(image,RGBColorspace);
455 number_packets=(image->columns+7)/8;
456 pixels=(unsigned char *) AcquireQuantumMemory(number_packets,
457 image->rows*sizeof(*pixels));
458 if (pixels == (unsigned char *) NULL)
459 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
460 /*
461 Convert pixels to a bitmap.
462 */
463 (void) SetImageType(image,BilevelType);
464 q=pixels;
cristybb503372010-05-27 20:51:26 +0000465 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000466 {
467 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
468 if (p == (const PixelPacket *) NULL)
469 break;
470 indexes=GetVirtualIndexQueue(image);
471 bit=0;
472 byte=0;
cristybb503372010-05-27 20:51:26 +0000473 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000474 {
475 byte<<=1;
476 if (PixelIntensity(p) < (QuantumRange/2.0))
477 byte|=0x01;
478 bit++;
479 if (bit == 8)
480 {
481 *q++=byte;
482 bit=0;
483 byte=0;
484 }
485 p++;
486 }
487 if (bit != 0)
488 *q++=byte << (8-bit);
489 if (image->previous == (Image *) NULL)
490 {
cristycee97112010-05-28 00:44:52 +0000491 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +0000492 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000493 if (status == MagickFalse)
494 break;
495 }
496 }
497 /*
498 Initialize JBIG info structure.
499 */
cristyf6fe0a12010-05-30 00:44:47 +0000500 jbg_enc_init(&jbig_info,(unsigned long) image->columns,(unsigned long)
501 image->rows,1,&pixels,(void (*)(unsigned char *,size_t,void *))
502 JBIGEncode,image);
cristy3ed852e2009-09-05 21:47:34 +0000503 if (image_info->scene != 0)
504 jbg_enc_layers(&jbig_info,(int) image_info->scene);
505 else
506 {
cristybb503372010-05-27 20:51:26 +0000507 size_t
cristy3ed852e2009-09-05 21:47:34 +0000508 x_resolution,
509 y_resolution;
510
cristy524222d2011-04-25 00:37:06 +0000511 ssize_t
512 sans_offset;
513
cristy3ed852e2009-09-05 21:47:34 +0000514 x_resolution=640;
515 y_resolution=480;
516 sans_offset=0;
517 if (image_info->density != (char *) NULL)
518 {
519 GeometryInfo
520 geometry_info;
521
522 MagickStatusType
523 flags;
524
525 flags=ParseGeometry(image_info->density,&geometry_info);
526 x_resolution=geometry_info.rho;
527 y_resolution=geometry_info.sigma;
528 if ((flags & SigmaValue) == 0)
529 y_resolution=x_resolution;
530 }
531 if (image->units == PixelsPerCentimeterResolution)
532 {
cristybb503372010-05-27 20:51:26 +0000533 x_resolution=(size_t) (100.0*2.54*x_resolution+0.5)/100.0;
534 y_resolution=(size_t) (100.0*2.54*y_resolution+0.5)/100.0;
cristy3ed852e2009-09-05 21:47:34 +0000535 }
cristyf6fe0a12010-05-30 00:44:47 +0000536 (void) jbg_enc_lrlmax(&jbig_info,(unsigned long) x_resolution,
537 (unsigned long) y_resolution);
cristy3ed852e2009-09-05 21:47:34 +0000538 }
539 (void) jbg_enc_lrange(&jbig_info,-1,-1);
540 jbg_enc_options(&jbig_info,JBG_ILEAVE | JBG_SMID,JBG_TPDON | JBG_TPBON |
541 JBG_DPON,version < 1.6 ? -1 : 0,-1,-1);
542 /*
543 Write JBIG image.
544 */
545 jbg_enc_out(&jbig_info);
546 jbg_enc_free(&jbig_info);
547 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
548 if (GetNextImageInList(image) == (Image *) NULL)
549 break;
550 image=SyncNextImageInList(image);
551 status=SetImageProgress(image,SaveImagesTag,scene++,
552 GetImageListLength(image));
553 if (status == MagickFalse)
554 break;
555 } while (image_info->adjoin != MagickFalse);
556 (void) CloseBlob(image);
557 return(MagickTrue);
558}
559#endif