1 /*
2 % Copyright (C) 2003-2020 GraphicsMagick Group
3 % Copyright (C) 2002 ImageMagick Studio
4 %
5 % This program is covered by multiple licenses, which are described in
6 % Copyright.txt. You should have received a copy of Copyright.txt with this
7 % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
8 %
9 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10 % %
11 % %
12 % CCC U U TTTTT %
13 % C U U T %
14 % C U U T %
15 % C U U T %
16 % CCC UUU T %
17 % %
18 % %
19 % Read DR Halo Image Format. %
20 % %
21 % %
22 % Software Design %
23 % Jaroslav Fojtik %
24 % June 2000 - 2007 %
25 % %
26 % %
27 % %
28 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
29 %
30 %
31 */
32
33 /*
34 Include declarations.
35 */
36 #include "magick/studio.h"
37 #include "magick/analyze.h"
38 #include "magick/blob.h"
39 #include "magick/color.h"
40 #include "magick/colormap.h"
41 #include "magick/magick.h"
42 #include "magick/pixel_cache.h"
43 #include "magick/utility.h"
44
45 typedef struct
46 {
47 unsigned Width;
48 unsigned Height;
49 unsigned Reserved;
50 }CUTHeader; /*Dr Halo*/
51 typedef struct
52 {
53 char FileId[2];
54 unsigned Version;
55 unsigned Size;
56 char FileType;
57 char SubType;
58 unsigned BoardID;
59 unsigned GraphicsMode;
60 unsigned MaxIndex;
61 unsigned MaxRed;
62 unsigned MaxGreen;
63 unsigned MaxBlue;
64 char PaletteId[20];
65 }CUTPalHeader;
66
67
InsertRow(unsigned char * p,long y,Image * image)68 static void InsertRow(unsigned char *p,long y,Image *image)
69 {
70 int bit; long x;
71 register PixelPacket *q;
72 IndexPacket index;
73 register IndexPacket *indexes;
74
75
76 switch (image->depth)
77 {
78 case 1: /* Convert bitmap scanline. */
79 {
80 q=SetImagePixels(image,0,y,image->columns,1);
81 if (q == (PixelPacket *) NULL)
82 break;
83 indexes=AccessMutableIndexes(image);
84 for (x=0; x < ((long) image->columns-7); x+=8)
85 {
86 for (bit=0; bit < 8; bit++)
87 {
88 index=((*p) & (0x80U >> bit) ? 0x01U : 0x00U);
89 indexes[x+bit]=index;
90 *q++=image->colormap[index];
91 }
92 p++;
93 }
94 if ((image->columns % 8) != 0)
95 {
96 for (bit=0; bit < (long) (image->columns % 8); bit++)
97 {
98 index=((*p) & (0x80 >> bit) ? 0x01U : 0x00U);
99 indexes[x+bit]=index;
100 *q++=image->colormap[index];
101 }
102 p++;
103 }
104 if (!SyncImagePixels(image))
105 break;
106 /* if (image->previous == (Image *) NULL)
107 if (QuantumTick(y,image->rows))
108 ProgressMonitor(LoadImageText,image->rows-y-1,image->rows);*/
109 break;
110 }
111 case 2: /* Convert PseudoColor scanline. */
112 {
113 q=SetImagePixels(image,0,y,image->columns,1);
114 if (q == (PixelPacket *) NULL)
115 break;
116 indexes=AccessMutableIndexes(image);
117 for (x=0; x < ((long) image->columns-1); x+=2)
118 {
119 index=(IndexPacket) ((*p >> 6U) & 0x3U);
120 VerifyColormapIndex(image,index);
121 indexes[x]=index;
122 *q++=image->colormap[index];
123 index=(IndexPacket) ((*p >> 4U) & 0x3U);
124 VerifyColormapIndex(image,index);
125 indexes[x]=index;
126 *q++=image->colormap[index];
127 index=(IndexPacket) ((*p >> 2U) & 0x3U);
128 VerifyColormapIndex(image,index);
129 indexes[x]=index;
130 *q++=image->colormap[index];
131 index=(IndexPacket) ((*p) & 0x3U);
132 VerifyColormapIndex(image,index);
133 indexes[x+1]=index;
134 *q++=image->colormap[index];
135 p++;
136 }
137 if ((image->columns % 4) != 0)
138 {
139 index=(IndexPacket) ((*p >> 6U) & 0x3U);
140 VerifyColormapIndex(image,index);
141 indexes[x]=index;
142 *q++=image->colormap[index];
143 if ((image->columns % 4) >= 1)
144
145 {
146 index=(IndexPacket) ((*p >> 4U) & 0x3U);
147 VerifyColormapIndex(image,index);
148 indexes[x]=index;
149 *q++=image->colormap[index];
150 if ((image->columns % 4) >= 2U)
151
152 {
153 index=(IndexPacket) ((*p >> 2U) & 0x3U);
154 VerifyColormapIndex(image,index);
155 indexes[x]=index;
156 *q++=image->colormap[index];
157 }
158 }
159 p++;
160 }
161 if (!SyncImagePixels(image))
162 break;
163 /* if (image->previous == (Image *) NULL)
164 if (QuantumTick(y,image->rows))
165 ProgressMonitor(LoadImageText,image->rows-y-1,image->rows);*/
166 break;
167 }
168
169 case 4: /* Convert PseudoColor scanline. */
170 {
171 q=SetImagePixels(image,0,y,image->columns,1);
172 if (q == (PixelPacket *) NULL)
173 break;
174 indexes=AccessMutableIndexes(image);
175 for (x=0; x < ((long) image->columns-1); x+=2)
176 {
177 index=(IndexPacket) ((*p >> 4U) & 0xfU);
178 VerifyColormapIndex(image,index);
179 indexes[x]=index;
180 *q++=image->colormap[index];
181 index=(IndexPacket) ((*p) & 0xfU);
182 VerifyColormapIndex(image,index);
183 indexes[x+1]=index;
184 *q++=image->colormap[index];
185 p++;
186 }
187 if ((image->columns % 2) != 0)
188 {
189 index=(IndexPacket) ((*p >> 4U) & 0xfU);
190 VerifyColormapIndex(image,index);
191 indexes[x]=index;
192 *q++=image->colormap[index];
193 p++;
194 }
195 if (!SyncImagePixels(image))
196 break;
197 /* if (image->previous == (Image *) NULL)
198 if (QuantumTick(y,image->rows))
199 ProgressMonitor(LoadImageText,image->rows-y-1,image->rows);*/
200 break;
201 }
202 case 8: /* Convert PseudoColor scanline. */
203 {
204 q=SetImagePixels(image,0,y,image->columns,1);
205 if (q == (PixelPacket *) NULL) break;
206 indexes=AccessMutableIndexes(image);
207
208 for (x=0; x < (long) image->columns; x++)
209 {
210 index=(IndexPacket) (*p);
211 VerifyColormapIndex(image,index);
212 indexes[x]=index;
213 *q++=image->colormap[index];
214 p++;
215 }
216 if (!SyncImagePixels(image))
217 break;
218 /* if (image->previous == (Image *) NULL)
219 if (QuantumTick(y,image->rows))
220 ProgressMonitor(LoadImageText,image->rows-y-1,image->rows);*/
221 }
222 break;
223
224 }
225 }
226
GetCutColors(Image * image)227 static unsigned int GetCutColors(Image *image)
228 {
229 unsigned long
230 x,
231 y;
232
233 PixelPacket
234 *q;
235
236 Quantum
237 MaxColor,
238 ScaleCharToQuantum16;
239
240 /* Compute the number of colors in Grayed R[i]=G[i]=B[i] image */
241 ScaleCharToQuantum16=ScaleCharToQuantum(16U);
242 MaxColor=0U;
243 for (y=0; y < image->rows; y++)
244 {
245
246 q=SetImagePixels(image,0,y,image->columns,1);
247 if (q == (PixelPacket *) NULL)
248 break;
249 for (x=image->columns; x != 0; x--)
250 {
251 if (MaxColor < q->red)
252 MaxColor=q->red;
253 if (MaxColor >= ScaleCharToQuantum16)
254 return(255U);
255 q++;
256 }
257 }
258 if (MaxColor < ScaleCharToQuantum(2))
259 MaxColor=2U;
260 else if (MaxColor < ScaleCharToQuantum(16U))
261 MaxColor=16U;
262 return (MaxColor);
263 }
264
265 /*
266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
267 % %
268 % %
269 % %
270 % R e a d C U T I m a g e %
271 % %
272 % %
273 % %
274 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
275 %
276 % Method ReadCUTImage reads an CUT X image file and returns it. It
277 % allocates the memory necessary for the new Image structure and returns a
278 % pointer to the new image.
279 %
280 % The format of the ReadCUTImage method is:
281 %
282 % Image *ReadCUTImage(const ImageInfo *image_info,ExceptionInfo *exception)
283 %
284 % A description of each parameter follows:
285 %
286 % o image: Method ReadCUTImage returns a pointer to the image after
287 % reading. A null image is returned if there is a memory shortage or if
288 % the image cannot be read.
289 %
290 % o image_info: Specifies a pointer to a ImageInfo structure.
291 %
292 % o exception: return any errors or warnings in this structure.
293 %
294 %
295 */
296 #define ThrowCUTReaderException(code_,reason_,image_) \
297 { \
298 if (palette != (Image *) NULL) \
299 DestroyImage(palette); \
300 if (clone_info != (ImageInfo *) NULL) \
301 DestroyImageInfo(clone_info); \
302 ThrowReaderException(code_,reason_,image_); \
303 }
ReadCUTImage(const ImageInfo * image_info,ExceptionInfo * exception)304 static Image *ReadCUTImage(const ImageInfo *image_info,ExceptionInfo *exception)
305 {
306 Image *image,*palette = (Image *) NULL;
307 ImageInfo *clone_info = (ImageInfo *) NULL;
308 unsigned int status;
309 unsigned long EncodedByte;
310 unsigned char RunCount,RunValue,RunCountMasked;
311 CUTHeader Header;
312 CUTPalHeader PalHeader;
313 long i,j;
314 long ldblk;
315 unsigned char *BImgBuff=NULL,*ptrB;
316 PixelPacket *q;
317
318 /*
319 Open image file.
320 */
321 assert(image_info != (const ImageInfo *) NULL);
322 assert(image_info->signature == MagickSignature);
323 assert(exception != (ExceptionInfo *) NULL);
324 assert(exception->signature == MagickSignature);
325 image=AllocateImage(image_info);
326 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
327 if (status == False)
328 ThrowReaderException(FileOpenError,UnableToOpenFile,image);
329 /*
330 Read CUT image.
331 */
332 Header.Width=ReadBlobLSBShort(image);
333 Header.Height=ReadBlobLSBShort(image);
334 Header.Reserved=ReadBlobLSBShort(image);
335
336 if (Header.Width==0 || Header.Height==0 || Header.Reserved!=0 ||
337 (Header.Width > INT_MAX) || (Header.Height > INT_MAX) )
338 CUT_KO: ThrowCUTReaderException(CorruptImageError,ImproperImageHeader,image);
339
340 /*---This code checks first line of image---*/
341 EncodedByte=ReadBlobLSBShort(image);
342 RunCount=ReadBlobByte(image);
343 RunCountMasked=RunCount & 0x7F;
344 ldblk=0;
345 while (RunCountMasked != 0) /*end of line?*/
346 {
347 i=1;
348 if (RunCount<0x80) i=RunCountMasked;
349 (void) SeekBlob(image,TellBlob(image)+i,SEEK_SET);
350 if (EOFBlob(image)) goto CUT_KO; /*wrong data*/
351 EncodedByte-=i+1;
352 ldblk+=RunCountMasked;
353
354 RunCount=ReadBlobByte(image);
355 if (EOFBlob(image)) goto CUT_KO; /*wrong data: unexpected eof in line*/
356 RunCountMasked=RunCount & 0x7F;
357 }
358 if (EncodedByte!=1) goto CUT_KO; /*wrong data: size incorrect*/
359 i=0; /*guess a number of bit planes*/
360 if (ldblk==(int) Header.Width) i=8;
361 if (2*ldblk==(int) Header.Width) i=4;
362 if (8*ldblk==(int) Header.Width) i=1;
363 if (i==0) goto CUT_KO; /*wrong data: incorrect bit planes*/
364
365 image->columns=Header.Width;
366 image->rows=Header.Height;
367 image->depth=i;
368 image->colors=1l >> i;
369
370 /* If ping is true, then only set image size and colors without reading any image data. */
371 if (image_info->ping) goto Finish;
372
373 if (CheckImagePixelLimits(image, exception) != MagickPass)
374 ThrowCUTReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
375
376 /* ----- Do something with palette ----- */
377 if ((clone_info=CloneImageInfo(image_info)) == NULL) goto NoPalette;
378
379 i=(long) strlen(clone_info->filename);
380 j=i;
381 while (--i>0)
382 {
383 if (clone_info->filename[i]=='.')
384 {
385 break;
386 }
387 if (clone_info->filename[i]=='/' || clone_info->filename[i]=='\\' ||
388 clone_info->filename[i]==':' )
389 {
390 i=j;
391 break;
392 }
393 }
394
395 if (i <= 0)
396 goto NoPalette;
397
398 (void) strlcpy(clone_info->filename+i,".PAL",sizeof(clone_info->filename)-i);
399 if ((clone_info->file=fopen(clone_info->filename,"rb"))==NULL)
400 {
401 (void) strlcpy(clone_info->filename+i,".pal",sizeof(clone_info->filename)-i);
402 if ((clone_info->file=fopen(clone_info->filename,"rb"))==NULL)
403 {
404 clone_info->filename[i]=0;
405 if ((clone_info->file=fopen(clone_info->filename,"rb"))==NULL)
406 {
407 DestroyImageInfo(clone_info);
408 clone_info=NULL;
409 goto NoPalette;
410 }
411 }
412 }
413
414 if ( (palette=AllocateImage(clone_info))==NULL ) goto NoPalette;
415 status=OpenBlob(clone_info,palette,ReadBinaryBlobMode,exception);
416 if (status == False)
417 {
418 ErasePalette:
419 DestroyImage(palette);
420 palette=NULL;
421 goto NoPalette;
422 }
423
424
425 if (palette!=NULL)
426 {
427 (void) ReadBlob(palette,2,PalHeader.FileId);
428 if (strncmp(PalHeader.FileId,"AH",2)) goto ErasePalette;
429 PalHeader.Version=ReadBlobLSBShort(palette);
430 PalHeader.Size=ReadBlobLSBShort(palette);
431 PalHeader.FileType=ReadBlobByte(palette);
432 PalHeader.SubType=ReadBlobByte(palette);
433 PalHeader.BoardID=ReadBlobLSBShort(palette);
434 PalHeader.GraphicsMode=ReadBlobLSBShort(palette);
435 PalHeader.MaxIndex=ReadBlobLSBShort(palette);
436 PalHeader.MaxRed=ReadBlobLSBShort(palette);
437 PalHeader.MaxGreen=ReadBlobLSBShort(palette);
438 PalHeader.MaxBlue=ReadBlobLSBShort(palette);
439 (void) ReadBlob(palette,20,PalHeader.PaletteId);
440
441 if (EOFBlob(image))
442 ThrowCUTReaderException(CorruptImageError,UnexpectedEndOfFile,image);
443
444 if (PalHeader.MaxIndex<1) goto ErasePalette;
445 image->colors=PalHeader.MaxIndex+1;
446 if (!AllocateImageColormap(image,image->colors)) goto NoMemory;
447
448 if (PalHeader.MaxRed==0) PalHeader.MaxRed=MaxRGB; /*avoid division by 0*/
449 if (PalHeader.MaxGreen==0) PalHeader.MaxGreen=MaxRGB;
450 if (PalHeader.MaxBlue==0) PalHeader.MaxBlue=MaxRGB;
451
452 for (i=0;i<=(int) PalHeader.MaxIndex;i++)
453 {
454 /*this may be wrong- I don't know why is palette such strange*/
455 j=(long) TellBlob(palette);
456 if ((j % 512)>512-6)
457 {
458 j=((j / 512)+1)*512;
459 (void) SeekBlob(palette,j,SEEK_SET);
460 }
461 image->colormap[i].red=ReadBlobLSBShort(palette);
462 if (MaxRGB!=PalHeader.MaxRed)
463 {
464 image->colormap[i].red=(Quantum)
465 (((double)image->colormap[i].red*MaxRGB+(PalHeader.MaxRed>>1))/PalHeader.MaxRed+0.5);
466 }
467 image->colormap[i].green=ReadBlobLSBShort(palette);
468 if (MaxRGB!=PalHeader.MaxGreen)
469 {
470 image->colormap[i].green=(Quantum)
471 (((double)image->colormap[i].green*MaxRGB+(PalHeader.MaxGreen>>1))/PalHeader.MaxGreen+0.5);
472 }
473 image->colormap[i].blue=ReadBlobLSBShort(palette);
474 if (MaxRGB!=PalHeader.MaxBlue)
475 {
476 image->colormap[i].blue=(Quantum)
477 (((double)image->colormap[i].blue*MaxRGB+(PalHeader.MaxBlue>>1))/PalHeader.MaxBlue+0.5);
478 }
479
480 }
481 if (EOFBlob(image))
482 ThrowCUTReaderException(CorruptImageError,UnexpectedEndOfFile,image);
483 }
484
485
486
487 NoPalette:
488 if (palette==NULL)
489 {
490
491 image->colors=256;
492 if (!AllocateImageColormap(image,image->colors))
493 {
494 NoMemory:
495 if (clone_info != NULL)
496 {
497 DestroyImageInfo(clone_info);
498 clone_info=(ImageInfo *) NULL;
499 }
500 ThrowCUTReaderException(ResourceLimitError,MemoryAllocationFailed,image);
501 }
502
503 for (i=0; i < (long)image->colors; i++)
504 {
505 image->colormap[i].red=ScaleCharToQuantum(i);
506 image->colormap[i].green=ScaleCharToQuantum(i);
507 image->colormap[i].blue=ScaleCharToQuantum(i);
508 }
509 }
510
511
512 /* ----- Load RLE compressed raster ----- */
513 BImgBuff=MagickAllocateResourceLimitedMemory(unsigned char *,(size_t) (ldblk)); /*Ldblk was set in the check phase*/
514 if (BImgBuff==NULL) goto NoMemory;
515
516 (void) SeekBlob(image,6 /*sizeof(Header)*/,SEEK_SET);
517 for (i=0;i<(int) Header.Height;i++)
518 {
519 EncodedByte=ReadBlobLSBShort(image);
520
521 ptrB=BImgBuff;
522 j=ldblk;
523
524 RunCount=ReadBlobByte(image);
525 RunCountMasked=RunCount & 0x7F;
526
527 while (RunCountMasked!=0)
528 {
529 if (RunCountMasked>j)
530 { /*Wrong Data*/
531 RunCountMasked=(unsigned char) j;
532 if (j==0)
533 {
534 break;
535 }
536 }
537
538 if (RunCount>0x80)
539 {
540 RunValue=ReadBlobByte(image);
541 (void) memset(ptrB,RunValue,RunCountMasked);
542 }
543 else {
544 (void) ReadBlob(image,RunCountMasked,ptrB);
545 }
546
547 ptrB+=RunCountMasked;
548 j-=RunCountMasked;
549
550 if (EOFBlob(image)) goto Finish; /* wrong data: unexpected eof in line */
551 RunCount=ReadBlobByte(image);
552 RunCountMasked=RunCount & 0x7F;
553 }
554
555 InsertRow(BImgBuff,i,image);
556 }
557
558
559 /*detect monochrome image*/
560
561 if (palette==NULL)
562 { /*attempt to detect binary (black&white) images*/
563 if ((image->storage_class == PseudoClass) && IsGrayImage(image,&image->exception))
564 {
565 if (GetCutColors(image)==2)
566 {
567 for (i=0; i < (long)image->colors; i++)
568 {
569 register Quantum
570 sample;
571 sample=ScaleCharToQuantum(i);
572 if (image->colormap[i].red!=sample) goto Finish;
573 if (image->colormap[i].green!=sample) goto Finish;
574 if (image->colormap[i].blue!=sample) goto Finish;
575 }
576
577 image->colormap[1].red=image->colormap[1].green=image->colormap[1].blue=MaxRGB;
578 for (i=0; i < (long)image->rows; i++)
579 {
580 q=SetImagePixels(image,0,i,image->columns,1);
581 if (q == (PixelPacket *) NULL)
582 break;
583 for (j=0; j < (long)image->columns; j++)
584 {
585 if (q->red==ScaleCharToQuantum(1))
586 {
587 q->red=q->green=q->blue=MaxRGB;
588 }
589 q++;
590 }
591 if (!SyncImagePixels(image)) goto Finish;
592 }
593 }
594 }
595 }
596
597 Finish:
598 if (BImgBuff!=NULL) MagickFreeResourceLimitedMemory(BImgBuff);
599 if (palette!=NULL)
600 {
601 DestroyImage(palette);
602 palette=(Image *) NULL;
603 }
604 if (clone_info!=NULL)
605 {
606 DestroyImageInfo(clone_info);
607 clone_info=(ImageInfo *) NULL;
608 }
609 if (EOFBlob(image))
610 ThrowCUTReaderException(CorruptImageError,UnexpectedEndOfFile,image);
611 CloseBlob(image);
612 StopTimer(&image->timer);
613 return(image);
614 }
615
616 /*
617 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
618 % %
619 % %
620 % %
621 % R e g i s t e r C U T I m a g e %
622 % %
623 % %
624 % %
625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
626 %
627 % Method RegisterCUTImage adds attributes for the CUT image format to
628 % the list of supported formats. The attributes include the image format
629 % tag, a method to read and/or write the format, whether the format
630 % supports the saving of more than one frame to the same file or blob,
631 % whether the format supports native in-memory I/O, and a brief
632 % description of the format.
633 %
634 % The format of the RegisterCUTImage method is:
635 %
636 % RegisterCUTImage(void)
637 %
638 */
RegisterCUTImage(void)639 ModuleExport void RegisterCUTImage(void)
640 {
641 MagickInfo
642 *entry;
643
644 entry=SetMagickInfo("CUT");
645 entry->decoder=(DecoderHandler) ReadCUTImage;
646 entry->seekable_stream=True;
647 entry->description="DR Halo";
648 entry->module="CUT";
649 (void) RegisterMagickInfo(entry);
650 }
651
652 /*
653 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
654 % %
655 % %
656 % %
657 % U n r e g i s t e r C U T I m a g e %
658 % %
659 % %
660 % %
661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
662 %
663 % Method UnregisterCUTImage removes format registrations made by the
664 % CUT module from the list of supported formats.
665 %
666 % The format of the UnregisterCUTImage method is:
667 %
668 % UnregisterCUTImage(void)
669 %
670 */
UnregisterCUTImage(void)671 ModuleExport void UnregisterCUTImage(void)
672 {
673 (void) UnregisterMagickInfo("CUT");
674 }
675