blob: aedc8ad5871732539d1caff3c4ab8dd739219c25 [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*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/blob.h"
44#include "MagickCore/blob-private.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/color-private.h"
47#include "MagickCore/colormap.h"
48#include "MagickCore/colorspace.h"
49#include "MagickCore/constitute.h"
50#include "MagickCore/exception.h"
51#include "MagickCore/exception-private.h"
52#include "MagickCore/geometry.h"
53#include "MagickCore/image.h"
54#include "MagickCore/image-private.h"
55#include "MagickCore/list.h"
56#include "MagickCore/magick.h"
57#include "MagickCore/memory_.h"
58#include "MagickCore/monitor.h"
59#include "MagickCore/monitor-private.h"
60#include "MagickCore/nt-feature.h"
61#include "MagickCore/pixel-accessor.h"
62#include "MagickCore/quantum-private.h"
63#include "MagickCore/static.h"
64#include "MagickCore/string_.h"
65#include "MagickCore/module.h"
cristy3ed852e2009-09-05 21:47:34 +000066#if defined(MAGICKCORE_JBIG_DELEGATE)
67#include "jbig.h"
68#endif
69
70/*
71 Forward declarations.
72*/
73#if defined(MAGICKCORE_JBIG_DELEGATE)
74static MagickBooleanType
75 WriteJBIGImage(const ImageInfo *,Image *);
76#endif
77
78#if defined(MAGICKCORE_JBIG_DELEGATE)
79/*
80%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81% %
82% %
83% %
84% R e a d J B I G I m a g e %
85% %
86% %
87% %
88%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89%
90% ReadJBIGImage() reads a JBIG image file and returns it. It
91% allocates the memory necessary for the new Image structure and returns a
92% pointer to the new image.
93%
94% The format of the ReadJBIGImage method is:
95%
96% Image *ReadJBIGImage(const ImageInfo *image_info,
97% ExceptionInfo *exception)
98%
99% A description of each parameter follows:
100%
101% o image_info: the image info.
102%
103% o exception: return any errors or warnings in this structure.
104%
105*/
106static Image *ReadJBIGImage(const ImageInfo *image_info,
107 ExceptionInfo *exception)
108{
109 Image
110 *image;
111
cristy50f1ebc2011-06-05 01:35:34 +0000112 MagickStatusType
cristy3ed852e2009-09-05 21:47:34 +0000113 status;
114
cristy4c08aed2011-07-01 19:47:50 +0000115 Quantum
116 index;
cristy3ed852e2009-09-05 21:47:34 +0000117
cristybb503372010-05-27 20:51:26 +0000118 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000119 x;
120
cristy4c08aed2011-07-01 19:47:50 +0000121 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000122 *q;
123
124 register unsigned char
125 *p;
126
127 ssize_t
cristy524222d2011-04-25 00:37:06 +0000128 length,
129 y;
cristy3ed852e2009-09-05 21:47:34 +0000130
131 struct jbg_dec_state
132 jbig_info;
133
134 unsigned char
135 bit,
136 *buffer,
137 byte;
138
139 /*
140 Open image file.
141 */
142 assert(image_info != (const ImageInfo *) NULL);
143 assert(image_info->signature == MagickSignature);
144 if (image_info->debug != MagickFalse)
145 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
146 image_info->filename);
147 assert(exception != (ExceptionInfo *) NULL);
148 assert(exception->signature == MagickSignature);
149 image=AcquireImage(image_info);
150 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
151 if (status == MagickFalse)
152 {
153 image=DestroyImageList(image);
154 return((Image *) NULL);
155 }
156 /*
157 Initialize JBIG toolkit.
158 */
159 jbg_dec_init(&jbig_info);
cristyf6fe0a12010-05-30 00:44:47 +0000160 jbg_dec_maxsize(&jbig_info,(unsigned long) image->columns,(unsigned long)
cristy3ed852e2009-09-05 21:47:34 +0000161 image->rows);
162 image->columns=jbg_dec_getwidth(&jbig_info);
163 image->rows=jbg_dec_getheight(&jbig_info);
164 image->depth=8;
165 image->storage_class=PseudoClass;
166 image->colors=2;
167 /*
168 Read JBIG file.
169 */
170 buffer=(unsigned char *) AcquireQuantumMemory(MagickMaxBufferExtent,
171 sizeof(*buffer));
172 if (buffer == (unsigned char *) NULL)
173 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
174 status=JBG_EAGAIN;
175 do
176 {
cristybb503372010-05-27 20:51:26 +0000177 length=(ssize_t) ReadBlob(image,MagickMaxBufferExtent,buffer);
cristy3ed852e2009-09-05 21:47:34 +0000178 if (length == 0)
179 break;
180 p=buffer;
cristy3ed852e2009-09-05 21:47:34 +0000181 while ((length > 0) && ((status == JBG_EAGAIN) || (status == JBG_EOK)))
182 {
183 size_t
184 count;
185
186 status=jbg_dec_in(&jbig_info,p,length,&count);
187 p+=count;
cristybb503372010-05-27 20:51:26 +0000188 length-=(ssize_t) count;
cristy3ed852e2009-09-05 21:47:34 +0000189 }
190 } while ((status == JBG_EAGAIN) || (status == JBG_EOK));
191 /*
192 Create colormap.
193 */
194 image->columns=jbg_dec_getwidth(&jbig_info);
195 image->rows=jbg_dec_getheight(&jbig_info);
cristy6d5e20f2011-04-25 13:48:54 +0000196 image->compression=JBIG2Compression;
cristy3ed852e2009-09-05 21:47:34 +0000197 if (AcquireImageColormap(image,2) == MagickFalse)
198 {
199 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
200 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
201 }
202 image->colormap[0].red=0;
203 image->colormap[0].green=0;
204 image->colormap[0].blue=0;
205 image->colormap[1].red=QuantumRange;
206 image->colormap[1].green=QuantumRange;
207 image->colormap[1].blue=QuantumRange;
208 image->x_resolution=300;
209 image->y_resolution=300;
210 if (image_info->ping != MagickFalse)
211 {
212 (void) CloseBlob(image);
213 return(GetFirstImageInList(image));
214 }
215 /*
216 Convert X bitmap image to pixel packets.
217 */
218 p=jbg_dec_getimage(&jbig_info,0);
cristybb503372010-05-27 20:51:26 +0000219 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000220 {
221 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000222 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000223 break;
cristy3ed852e2009-09-05 21:47:34 +0000224 bit=0;
225 byte=0;
cristybb503372010-05-27 20:51:26 +0000226 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000227 {
228 if (bit == 0)
229 byte=(*p++);
230 index=(byte & 0x80) ? 0 : 1;
231 bit++;
232 byte<<=1;
233 if (bit == 8)
234 bit=0;
cristy4c08aed2011-07-01 19:47:50 +0000235 SetPixelIndex(image,index,q);
236 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
237 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000238 }
239 if (SyncAuthenticPixels(image,exception) == MagickFalse)
240 break;
cristycee97112010-05-28 00:44:52 +0000241 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +0000242 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000243 if (status == MagickFalse)
244 break;
245 }
246 /*
247 Free scale resource.
248 */
249 jbg_dec_free(&jbig_info);
250 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
251 (void) CloseBlob(image);
252 return(GetFirstImageInList(image));
253}
254#endif
255
256/*
257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
258% %
259% %
260% %
261% R e g i s t e r J B I G I m a g e %
262% %
263% %
264% %
265%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
266%
267% RegisterJBIGImage() adds attributes for the JBIG image format to
268% the list of supported formats. The attributes include the image format
269% tag, a method to read and/or write the format, whether the format
270% supports the saving of more than one frame to the same file or blob,
271% whether the format supports native in-memory I/O, and a brief
272% description of the format.
273%
274% The format of the RegisterJBIGImage method is:
275%
cristybb503372010-05-27 20:51:26 +0000276% size_t RegisterJBIGImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000277%
278*/
cristybb503372010-05-27 20:51:26 +0000279ModuleExport size_t RegisterJBIGImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000280{
281#define JBIGDescription "Joint Bi-level Image experts Group interchange format"
282
283 char
284 version[MaxTextExtent];
285
286 MagickInfo
287 *entry;
288
289 *version='\0';
290#if defined(JBG_VERSION)
291 (void) CopyMagickString(version,JBG_VERSION,MaxTextExtent);
292#endif
293 entry=SetMagickInfo("BIE");
294#if defined(MAGICKCORE_JBIG_DELEGATE)
295 entry->decoder=(DecodeImageHandler *) ReadJBIGImage;
296 entry->encoder=(EncodeImageHandler *) WriteJBIGImage;
297#endif
298 entry->adjoin=MagickFalse;
299 entry->description=ConstantString(JBIGDescription);
300 if (*version != '\0')
301 entry->version=ConstantString(version);
302 entry->module=ConstantString("JBIG");
303 (void) RegisterMagickInfo(entry);
304 entry=SetMagickInfo("JBG");
305#if defined(MAGICKCORE_JBIG_DELEGATE)
306 entry->decoder=(DecodeImageHandler *) ReadJBIGImage;
307 entry->encoder=(EncodeImageHandler *) WriteJBIGImage;
308#endif
309 entry->description=ConstantString(JBIGDescription);
310 if (*version != '\0')
311 entry->version=ConstantString(version);
312 entry->module=ConstantString("JBIG");
313 (void) RegisterMagickInfo(entry);
314 entry=SetMagickInfo("JBIG");
315#if defined(MAGICKCORE_JBIG_DELEGATE)
316 entry->decoder=(DecodeImageHandler *) ReadJBIGImage;
317 entry->encoder=(EncodeImageHandler *) WriteJBIGImage;
318#endif
319 entry->description=ConstantString(JBIGDescription);
320 if (*version != '\0')
321 entry->version=ConstantString(version);
322 entry->module=ConstantString("JBIG");
323 (void) RegisterMagickInfo(entry);
324 return(MagickImageCoderSignature);
325}
326
327/*
328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
329% %
330% %
331% %
332% U n r e g i s t e r J B I G I m a g e %
333% %
334% %
335% %
336%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
337%
338% UnregisterJBIGImage() removes format registrations made by the
339% JBIG module from the list of supported formats.
340%
341% The format of the UnregisterJBIGImage method is:
342%
343% UnregisterJBIGImage(void)
344%
345*/
346ModuleExport void UnregisterJBIGImage(void)
347{
348 (void) UnregisterMagickInfo("BIE");
349 (void) UnregisterMagickInfo("JBG");
350 (void) UnregisterMagickInfo("JBIG");
351}
352
353#if defined(MAGICKCORE_JBIG_DELEGATE)
354/*
355%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
356% %
357% %
358% %
359% W r i t e J B I G I m a g e %
360% %
361% %
362% %
363%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
364%
365% WriteJBIGImage() writes an image in the JBIG encoded image format.
366%
367% The format of the WriteJBIGImage method is:
368%
369% MagickBooleanType WriteJBIGImage(const ImageInfo *image_info,Image *image)
370%
371% A description of each parameter follows.
372%
373% o image_info: the image info.
374%
375% o image: The image.
376%
377%
378*/
379
380static void JBIGEncode(unsigned char *pixels,size_t length,void *data)
381{
382 Image
383 *image;
384
385 image=(Image *) data;
386 (void) WriteBlob(image,length,pixels);
387}
388
389static MagickBooleanType WriteJBIGImage(const ImageInfo *image_info,
390 Image *image)
391{
392 double
393 version;
394
cristy3ed852e2009-09-05 21:47:34 +0000395 MagickBooleanType
396 status;
397
398 MagickOffsetType
399 scene;
400
cristy4c08aed2011-07-01 19:47:50 +0000401 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000402 *p;
403
cristybb503372010-05-27 20:51:26 +0000404 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000405 x;
406
407 register unsigned char
408 *q;
409
cristy524222d2011-04-25 00:37:06 +0000410 size_t
411 number_packets;
412
413 ssize_t
414 y;
415
cristy3ed852e2009-09-05 21:47:34 +0000416 struct jbg_enc_state
417 jbig_info;
418
419 unsigned char
420 bit,
421 byte,
422 *pixels;
423
cristy3ed852e2009-09-05 21:47:34 +0000424 /*
425 Open image file.
426 */
427 assert(image_info != (const ImageInfo *) NULL);
428 assert(image_info->signature == MagickSignature);
429 assert(image != (Image *) NULL);
430 assert(image->signature == MagickSignature);
431 if (image->debug != MagickFalse)
432 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
433 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
434 if (status == MagickFalse)
435 return(status);
cristyc1acd842011-05-19 23:05:47 +0000436 version=InterpretLocaleValue(JBG_VERSION,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000437 scene=0;
438 do
439 {
440 /*
441 Allocate pixel data.
442 */
cristy510d06a2011-07-06 23:43:54 +0000443 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000444 (void) TransformImageColorspace(image,RGBColorspace);
445 number_packets=(image->columns+7)/8;
446 pixels=(unsigned char *) AcquireQuantumMemory(number_packets,
447 image->rows*sizeof(*pixels));
448 if (pixels == (unsigned char *) NULL)
449 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
450 /*
451 Convert pixels to a bitmap.
452 */
453 (void) SetImageType(image,BilevelType);
454 q=pixels;
cristybb503372010-05-27 20:51:26 +0000455 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000456 {
457 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +0000458 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000459 break;
cristy3ed852e2009-09-05 21:47:34 +0000460 bit=0;
461 byte=0;
cristybb503372010-05-27 20:51:26 +0000462 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000463 {
464 byte<<=1;
cristy4c08aed2011-07-01 19:47:50 +0000465 if (GetPixelIntensity(image,p) < (QuantumRange/2.0))
cristy3ed852e2009-09-05 21:47:34 +0000466 byte|=0x01;
467 bit++;
468 if (bit == 8)
469 {
470 *q++=byte;
471 bit=0;
472 byte=0;
473 }
cristy4c08aed2011-07-01 19:47:50 +0000474 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000475 }
476 if (bit != 0)
477 *q++=byte << (8-bit);
478 if (image->previous == (Image *) NULL)
479 {
cristycee97112010-05-28 00:44:52 +0000480 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +0000481 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000482 if (status == MagickFalse)
483 break;
484 }
485 }
486 /*
487 Initialize JBIG info structure.
488 */
cristyf6fe0a12010-05-30 00:44:47 +0000489 jbg_enc_init(&jbig_info,(unsigned long) image->columns,(unsigned long)
490 image->rows,1,&pixels,(void (*)(unsigned char *,size_t,void *))
491 JBIGEncode,image);
cristy3ed852e2009-09-05 21:47:34 +0000492 if (image_info->scene != 0)
493 jbg_enc_layers(&jbig_info,(int) image_info->scene);
494 else
495 {
cristybb503372010-05-27 20:51:26 +0000496 size_t
cristy3ed852e2009-09-05 21:47:34 +0000497 x_resolution,
498 y_resolution;
499
500 x_resolution=640;
501 y_resolution=480;
cristy3ed852e2009-09-05 21:47:34 +0000502 if (image_info->density != (char *) NULL)
503 {
504 GeometryInfo
505 geometry_info;
506
507 MagickStatusType
508 flags;
509
510 flags=ParseGeometry(image_info->density,&geometry_info);
511 x_resolution=geometry_info.rho;
512 y_resolution=geometry_info.sigma;
513 if ((flags & SigmaValue) == 0)
514 y_resolution=x_resolution;
515 }
516 if (image->units == PixelsPerCentimeterResolution)
517 {
cristybb503372010-05-27 20:51:26 +0000518 x_resolution=(size_t) (100.0*2.54*x_resolution+0.5)/100.0;
519 y_resolution=(size_t) (100.0*2.54*y_resolution+0.5)/100.0;
cristy3ed852e2009-09-05 21:47:34 +0000520 }
cristyf6fe0a12010-05-30 00:44:47 +0000521 (void) jbg_enc_lrlmax(&jbig_info,(unsigned long) x_resolution,
522 (unsigned long) y_resolution);
cristy3ed852e2009-09-05 21:47:34 +0000523 }
524 (void) jbg_enc_lrange(&jbig_info,-1,-1);
525 jbg_enc_options(&jbig_info,JBG_ILEAVE | JBG_SMID,JBG_TPDON | JBG_TPBON |
526 JBG_DPON,version < 1.6 ? -1 : 0,-1,-1);
527 /*
528 Write JBIG image.
529 */
530 jbg_enc_out(&jbig_info);
531 jbg_enc_free(&jbig_info);
532 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
533 if (GetNextImageInList(image) == (Image *) NULL)
534 break;
535 image=SyncNextImageInList(image);
536 status=SetImageProgress(image,SaveImagesTag,scene++,
537 GetImageListLength(image));
538 if (status == MagickFalse)
539 break;
540 } while (image_info->adjoin != MagickFalse);
541 (void) CloseBlob(image);
542 return(MagickTrue);
543}
544#endif