/src/graphicsmagick/coders/rle.c
Line | Count | Source |
1 | | /* |
2 | | % Copyright (C) 2003-2025 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % Copyright 1991-1999 E. I. du Pont de Nemours and Company |
5 | | % |
6 | | % This program is covered by multiple licenses, which are described in |
7 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
8 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
9 | | % |
10 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
11 | | % % |
12 | | % % |
13 | | % % |
14 | | % RRRR L EEEEE % |
15 | | % R R L E % |
16 | | % RRRR L EEE % |
17 | | % R R L E % |
18 | | % R R LLLLL EEEEE % |
19 | | % % |
20 | | % % |
21 | | % Read URT RLE Image Format. % |
22 | | % % |
23 | | % % |
24 | | % Software Design % |
25 | | % John Cristy % |
26 | | % July 1992 % |
27 | | % % |
28 | | % % |
29 | | % % |
30 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
31 | | % |
32 | | % |
33 | | */ |
34 | | |
35 | | /* |
36 | | Include declarations. |
37 | | */ |
38 | | #include "magick/studio.h" |
39 | | #include "magick/attribute.h" |
40 | | #include "magick/blob.h" |
41 | | #include "magick/colormap.h" |
42 | | #include "magick/log.h" |
43 | | #include "magick/magick.h" |
44 | | #include "magick/monitor.h" |
45 | | #include "magick/pixel_cache.h" |
46 | | #include "magick/utility.h" |
47 | | |
48 | | typedef struct _RLE_Header |
49 | | { |
50 | | magick_uint8_t Magic[2]; /* Magic number */ |
51 | | magick_uint16_t Xpos; /* Lower left x of image */ |
52 | | magick_uint16_t Ypos; /* Lower left y of image */ |
53 | | magick_uint16_t XSize; /* Image width */ |
54 | | magick_uint16_t YSize; /* Image height */ |
55 | | magick_uint8_t Flags; /* Misc flags */ |
56 | | magick_uint8_t Ncolors; /* Number of colors */ |
57 | | magick_uint8_t Pixelbits; /* Number of bits per channel */ |
58 | | magick_uint8_t Ncmap; /* Number of color channels in palette */ |
59 | | magick_uint8_t Cmaplen; /* Colormap length */ |
60 | | |
61 | | } RLE_HEADER; |
62 | | |
63 | | /* If this flag is set, the image rectangle should first be cleared to |
64 | | the background color (q.v.) before reading the scanline data. */ |
65 | 56.8k | #define ClearFirstFlag 0x01 |
66 | | |
67 | | /* If this flag is set, no background color is supplied, and the |
68 | | ClearFirst flag should be ignored. */ |
69 | 160k | #define NoBackgroundFlag 0x02 |
70 | | |
71 | | /* This flag indicates the presence of an "alpha" channel. The alpha |
72 | | channel is used by image compositing software to correctly blend |
73 | | anti-aliased edges. It is stored as channel -1 (255). */ |
74 | 235k | #define AlphaFlag 0x04 |
75 | | |
76 | | /* If this flag is set, comments are present in the variable part of |
77 | | the header, immediately following the color map. */ |
78 | 89.9k | #define CommentsFlag 0x08 |
79 | | |
80 | | /* RLE operators */ |
81 | 5.95M | #define SkipLinesOp 0x01 |
82 | 61.8k | #define SetColorOp 0x02 |
83 | 58.9k | #define SkipPixelsOp 0x03 |
84 | 30.9k | #define ByteDataOp 0x05 |
85 | 18.0k | #define RunDataOp 0x06 |
86 | 11.5M | #define EOFOp 0x07 |
87 | | |
88 | | static void LogRLEHeader(const RLE_HEADER* rle_header) |
89 | 147k | { |
90 | 147k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
91 | 147k | "RLE Header\n" |
92 | 147k | " Magic: \\%03o\\%03o\n" |
93 | 147k | " Xpos: %u\n" |
94 | 147k | " Ypos: %u\n" |
95 | 147k | " XSize: %u\n" |
96 | 147k | " YSize: %u\n" |
97 | 147k | " Flags: 0x%02x (%u,%u,%u,%u,%u,%u,%u,%u)\n" |
98 | 147k | " Ncolors: %u\n" |
99 | 147k | " Pixelbits: %u\n" |
100 | 147k | " Ncmap: %u\n" |
101 | 147k | " Cmaplen: %u", |
102 | 147k | rle_header->Magic[0], |
103 | 147k | rle_header->Magic[1], |
104 | 147k | rle_header->Xpos, |
105 | 147k | rle_header->Ypos, |
106 | 147k | rle_header->XSize, |
107 | 147k | rle_header->YSize, |
108 | 147k | rle_header->Flags, |
109 | 147k | (rle_header->Flags >> 7) & 0x01, |
110 | 147k | (rle_header->Flags >> 6) & 0x01, |
111 | 147k | (rle_header->Flags >> 5) & 0x01, |
112 | 147k | (rle_header->Flags >> 4) & 0x01, |
113 | 147k | (rle_header->Flags >> 3) & 0x01, |
114 | 147k | (rle_header->Flags >> 2) & 0x01, |
115 | 147k | (rle_header->Flags >> 1) & 0x01, |
116 | 147k | (rle_header->Flags >> 0) & 0x01, |
117 | 147k | rle_header->Ncolors, |
118 | 147k | rle_header->Pixelbits, |
119 | 147k | rle_header->Ncmap, |
120 | 147k | rle_header->Cmaplen); |
121 | 147k | } |
122 | | |
123 | | /* |
124 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
125 | | % % |
126 | | % % |
127 | | % % |
128 | | % I s R L E % |
129 | | % % |
130 | | % % |
131 | | % % |
132 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
133 | | % |
134 | | % Method IsRLE returns True if the image format type, identified by the |
135 | | % magick string, is RLE. |
136 | | % |
137 | | % The format of the ReadRLEImage method is: |
138 | | % |
139 | | % unsigned int IsRLE(const unsigned char *magick,const size_t length) |
140 | | % |
141 | | % A description of each parameter follows: |
142 | | % |
143 | | % o status: Method IsRLE returns True if the image format type is RLE. |
144 | | % |
145 | | % o magick: This string is generally the first few bytes of an image file |
146 | | % or blob. |
147 | | % |
148 | | % o length: Specifies the length of the magick string. |
149 | | % |
150 | | % |
151 | | */ |
152 | | static unsigned int IsRLE(const unsigned char *magick,const size_t length) |
153 | 0 | { |
154 | 0 | if (length < 2) |
155 | 0 | return(False); |
156 | 0 | if (memcmp(magick,"\122\314",2) == 0) |
157 | 0 | return(True); |
158 | 0 | return(False); |
159 | 0 | } |
160 | | |
161 | | /* |
162 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
163 | | % % |
164 | | % % |
165 | | % % |
166 | | % R e a d R L E I m a g e % |
167 | | % % |
168 | | % % |
169 | | % % |
170 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
171 | | % |
172 | | % Method ReadRLEImage reads a run-length encoded Utah Raster Toolkit |
173 | | % image file and returns it. It allocates the memory necessary for the new |
174 | | % Image structure and returns a pointer to the new image. |
175 | | % |
176 | | % The format of the ReadRLEImage method is: |
177 | | % |
178 | | % Image *ReadRLEImage(const ImageInfo *image_info,ExceptionInfo *exception) |
179 | | % |
180 | | % A description of each parameter follows: |
181 | | % |
182 | | % o image: Method ReadRLEImage returns a pointer to the image after |
183 | | % reading. A null image is returned if there is a memory shortage or |
184 | | % if the image cannot be read. |
185 | | % |
186 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
187 | | % |
188 | | % o exception: return any errors or warnings in this structure. |
189 | | % |
190 | | % |
191 | | */ |
192 | 139k | #define ThrowRLEReaderException(code_,reason_,image_) \ |
193 | 143k | do { \ |
194 | 143k | MagickFreeResourceLimitedMemory(colormap); \ |
195 | 143k | MagickFreeResourceLimitedMemory(rle_pixels); \ |
196 | 143k | ThrowReaderException(code_,reason_,image_); \ |
197 | 0 | } while (0); |
198 | | static Image *ReadRLEImage(const ImageInfo *image_info,ExceptionInfo *exception) |
199 | 153k | { |
200 | 153k | RLE_HEADER |
201 | 153k | rle_header; |
202 | | |
203 | 153k | Image |
204 | 153k | *image; |
205 | | |
206 | 153k | int |
207 | 153k | b, |
208 | 153k | opcode, |
209 | 153k | operand, |
210 | 153k | pixel, |
211 | 153k | status; |
212 | | |
213 | 153k | size_t |
214 | 153k | colormap_entries; |
215 | | |
216 | 153k | unsigned int |
217 | 153k | index; |
218 | | |
219 | 153k | unsigned long |
220 | 153k | y; |
221 | | |
222 | 153k | register IndexPacket |
223 | 153k | *indexes; |
224 | | |
225 | 153k | register unsigned long |
226 | 153k | x; |
227 | | |
228 | 153k | register PixelPacket |
229 | 153k | *q; |
230 | | |
231 | 153k | register unsigned int |
232 | 153k | i; |
233 | | |
234 | 153k | register unsigned char |
235 | 153k | *p; |
236 | | |
237 | 153k | size_t |
238 | 153k | count, |
239 | 153k | map_length, |
240 | 153k | number_pixels, |
241 | 153k | offset, |
242 | 153k | rle_bytes; |
243 | | |
244 | 153k | unsigned char |
245 | 153k | background_color[256], |
246 | 153k | *colormap = (unsigned char *) NULL, |
247 | 153k | plane, |
248 | 153k | *rle_pixels = (unsigned char *) NULL; |
249 | | |
250 | 153k | unsigned int |
251 | 153k | number_colormaps, |
252 | 153k | number_planes; |
253 | | |
254 | 153k | magick_off_t |
255 | 153k | file_size; |
256 | | |
257 | | /* |
258 | | Open image file. |
259 | | */ |
260 | 153k | assert(image_info != (const ImageInfo *) NULL); |
261 | 153k | assert(image_info->signature == MagickSignature); |
262 | 153k | assert(exception != (ExceptionInfo *) NULL); |
263 | 153k | assert(exception->signature == MagickSignature); |
264 | 153k | (void) memset(&rle_header,0,sizeof(rle_header)); |
265 | 153k | image=AllocateImage(image_info); |
266 | 153k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
267 | 153k | if (status == False) |
268 | 153k | ThrowRLEReaderException(FileOpenError,UnableToOpenFile,image); |
269 | | /* |
270 | | Determine if this is a RLE file. |
271 | | */ |
272 | 153k | count=ReadBlob(image,2,(char *) &rle_header.Magic); |
273 | 153k | if ((count != 2) || (memcmp(&rle_header.Magic,"\122\314",2) != 0)) |
274 | 153k | ThrowRLEReaderException(CorruptImageError,ImproperImageHeader,image); |
275 | 153k | file_size=GetBlobSize(image); |
276 | | /* |
277 | | Read image header. |
278 | | */ |
279 | 153k | rle_header.Xpos=ReadBlobLSBShort(image); |
280 | 153k | rle_header.Ypos=ReadBlobLSBShort(image); |
281 | 153k | rle_header.XSize=ReadBlobLSBShort(image); |
282 | 153k | rle_header.YSize=ReadBlobLSBShort(image); |
283 | 153k | rle_header.Flags=ReadBlobByte(image); |
284 | 153k | b=ReadBlobByte(image); |
285 | 153k | if (EOF == b) |
286 | 150k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
287 | 150k | if (b < 0) |
288 | 150k | ThrowRLEReaderException(CorruptImageError,ImproperImageHeader,image); |
289 | 150k | rle_header.Ncolors=(magick_uint8_t) b; |
290 | 150k | b=ReadBlobByte(image); |
291 | 150k | if (EOF == b) |
292 | 150k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
293 | 150k | if (b < 0) |
294 | 150k | ThrowRLEReaderException(CorruptImageError,ImproperImageHeader,image); |
295 | 150k | rle_header.Pixelbits=(magick_uint8_t) b; |
296 | 150k | b=ReadBlobByte(image); |
297 | 150k | if (EOF == b) |
298 | 147k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
299 | 147k | if (b < 0) |
300 | 147k | ThrowRLEReaderException(CorruptImageError,ImproperImageHeader,image); |
301 | 147k | rle_header.Ncmap=(magick_uint8_t) b; |
302 | 147k | b=ReadBlobByte(image); |
303 | 147k | if (EOF == b) |
304 | 147k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
305 | 147k | if (b < 0) |
306 | 147k | ThrowRLEReaderException(CorruptImageError,ImproperImageHeader,image); |
307 | 147k | rle_header.Cmaplen=(magick_uint8_t) b; |
308 | 147k | if (EOFBlob(image)) |
309 | 147k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
310 | | |
311 | 147k | LogRLEHeader(&rle_header); |
312 | | |
313 | 147k | if ((rle_header.Ncolors == 0) || |
314 | 139k | (rle_header.Ncolors == 2) || |
315 | 130k | ((rle_header.Flags & AlphaFlag) && |
316 | 39.8k | ((rle_header.Ncolors > 254) || (rle_header.Ncolors < 3))) || |
317 | 128k | (rle_header.Pixelbits != 8)) |
318 | 121k | ThrowRLEReaderException(CoderError,DataEncodingSchemeIsNotSupported,image); |
319 | | |
320 | | /* X/Y size may not be zero and may not exceed 32768 */ |
321 | 121k | if ((rle_header.XSize == 0) || (rle_header.XSize >= 32768) || |
322 | 113k | (rle_header.YSize == 0) || (rle_header.YSize >= 32768)) |
323 | 112k | ThrowRLEReaderException(CorruptImageError,ImproperImageHeader,image); |
324 | | |
325 | 112k | if (rle_header.Cmaplen > 8) |
326 | 104k | ThrowRLEReaderException(CorruptImageError,ImproperImageHeader,image); |
327 | | |
328 | 104k | image->columns=rle_header.XSize; |
329 | 104k | image->rows=rle_header.YSize; |
330 | 104k | image->matte=rle_header.Flags & AlphaFlag; |
331 | 104k | number_planes=rle_header.Ncolors; |
332 | 104k | number_colormaps=rle_header.Ncmap; |
333 | 104k | map_length=(size_t) 1 << rle_header.Cmaplen; |
334 | | |
335 | 104k | (void) memset(background_color,0,sizeof(background_color)); |
336 | 104k | if (rle_header.Flags & NoBackgroundFlag) |
337 | 16.9k | { |
338 | | /* |
339 | | No background color-- initialize to black. |
340 | | */ |
341 | 133k | for (i=0; i < number_planes; i++) |
342 | 116k | background_color[i]=0; |
343 | 16.9k | (void) ReadBlobByte(image); |
344 | 16.9k | } |
345 | 87.9k | else |
346 | 87.9k | { |
347 | | /* |
348 | | Initialize background color. |
349 | | */ |
350 | 87.9k | p=background_color; |
351 | 626k | for (i=0; i < number_planes; i++) |
352 | 538k | *p++=ReadBlobByte(image); |
353 | 87.9k | } |
354 | 104k | if ((number_planes & 0x01) == 0) |
355 | 10.7k | (void) ReadBlobByte(image); |
356 | | |
357 | 104k | if (EOFBlob(image)) |
358 | 103k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
359 | | |
360 | 103k | if (image->matte) |
361 | 31.8k | number_planes++; |
362 | | |
363 | | /* |
364 | | Rationalize pixels with file size |
365 | | */ |
366 | 103k | if ((file_size == 0) || |
367 | 103k | ((((double) image->columns*image->rows*number_planes* |
368 | 103k | rle_header.Pixelbits/8)/file_size) > 254.0)) |
369 | 12.9k | ThrowRLEReaderException(CorruptImageError,InsufficientImageDataInFile, |
370 | 103k | image); |
371 | | |
372 | 90.9k | if ((double) number_colormaps*map_length > file_size) |
373 | 453 | ThrowRLEReaderException(CorruptImageError,InsufficientImageDataInFile, |
374 | 90.9k | image); |
375 | | |
376 | | /* |
377 | | Cap the number of planes at 4 since we don't support more than that. |
378 | | */ |
379 | 90.4k | if (number_planes > 4) |
380 | 24.0k | number_planes=4; |
381 | | |
382 | 90.4k | colormap_entries=0; |
383 | 90.4k | if (number_colormaps != 0) |
384 | 67.1k | { |
385 | | /* |
386 | | Read image colormaps. Color map values are stored as 16 bit |
387 | | quantities, left justified in the word. |
388 | | */ |
389 | 67.1k | colormap=MagickAllocateResourceLimitedArray(unsigned char *,number_colormaps, |
390 | 67.1k | map_length); |
391 | 67.1k | if (colormap == (unsigned char *) NULL) |
392 | 0 | ThrowRLEReaderException(ResourceLimitError,MemoryAllocationFailed, |
393 | 67.1k | image); |
394 | 67.1k | p=colormap; /* unsigned char * */ |
395 | 557k | for (i=0; i < number_colormaps; i++) |
396 | 4.73M | for (x=0; x < map_length; x++) |
397 | 4.24M | { |
398 | 4.24M | if (EOFBlob(image)) |
399 | 529 | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile, |
400 | 4.24M | image); |
401 | 4.24M | *p++=(ReadBlobLSBShort(image) >> 8); |
402 | 4.24M | } |
403 | 66.5k | colormap_entries=MagickArraySize(number_colormaps,map_length); |
404 | 66.5k | } |
405 | 89.9k | if (rle_header.Flags & CommentsFlag) |
406 | 20.9k | { |
407 | 20.9k | char |
408 | 20.9k | *comment; |
409 | | |
410 | 20.9k | unsigned int |
411 | 20.9k | length; |
412 | | |
413 | | /* |
414 | | Read image comment. |
415 | | |
416 | | The comment block contains any number of null-terminated |
417 | | text strings. These strings will conventionally be of the |
418 | | form "name=value", allowing for easy retrieval of specific |
419 | | information. However, there is no restriction that a given |
420 | | name appear only once, and a comment may contain an |
421 | | arbitrary string. |
422 | | */ |
423 | 20.9k | length=ReadBlobLSBShort(image); |
424 | 20.9k | comment=MagickAllocateResourceLimitedMemory(char *,(size_t) length+1); |
425 | 20.9k | if (comment == (char *) NULL) |
426 | 0 | { |
427 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
428 | 0 | "Failed to allocate %u bytes for comment", |
429 | 0 | length+1); |
430 | 0 | ThrowRLEReaderException(ResourceLimitError,MemoryAllocationFailed, |
431 | 0 | image); |
432 | 0 | } |
433 | 20.9k | if (ReadBlob(image,length,comment) != length) |
434 | 3.61k | { |
435 | 3.61k | MagickFreeResourceLimitedMemory(comment); |
436 | 3.61k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
437 | 0 | } |
438 | 17.3k | comment[length]='\0'; |
439 | | /* |
440 | | Delimit multiple comments with '\n' so they fit in one string. |
441 | | */ |
442 | 17.3k | if (length) |
443 | 1.52M | for (i=0; i < length-1; i++) |
444 | 1.52M | { |
445 | 1.52M | if (comment[i]=='\0') |
446 | 349k | comment[i]='\n'; |
447 | 1.52M | } |
448 | 17.3k | (void) SetImageAttribute(image,"comment",comment); |
449 | 17.3k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
450 | 17.3k | "Comment: '%s'", comment); |
451 | 17.3k | MagickFreeResourceLimitedMemory(comment); |
452 | 17.3k | if ((length & 0x01) == 0) |
453 | 11.6k | (void) ReadBlobByte(image); |
454 | 17.3k | } |
455 | | |
456 | 86.3k | if (EOFBlob(image)) |
457 | 84.3k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
458 | | |
459 | 84.3k | if (image_info->ping) |
460 | 0 | { |
461 | 0 | MagickFreeResourceLimitedMemory(colormap); |
462 | 0 | CloseBlob(image); |
463 | 0 | StopTimer(&image->timer); |
464 | 0 | return(image); |
465 | 0 | } |
466 | | |
467 | 84.3k | if (CheckImagePixelLimits(image, exception) != MagickPass) |
468 | 56.8k | ThrowRLEReaderException(ResourceLimitError,ImagePixelLimitExceeded,image); |
469 | | |
470 | | /* |
471 | | Allocate RLE pixels. |
472 | | */ |
473 | 56.8k | number_pixels=MagickArraySize(image->columns,image->rows); |
474 | 56.8k | rle_bytes=MagickArraySize(number_pixels,number_planes); |
475 | 56.8k | if ((number_pixels == 0) || (rle_bytes == 0)) |
476 | 56.8k | ThrowRLEReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
477 | 56.8k | rle_pixels=MagickAllocateResourceLimitedArray(unsigned char *,number_pixels, |
478 | 56.8k | number_planes); |
479 | 56.8k | if (rle_pixels == (unsigned char *) NULL) |
480 | 56.8k | ThrowRLEReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
481 | 56.8k | (void) memset(rle_pixels,0,rle_bytes); |
482 | 56.8k | if ((rle_header.Flags & ClearFirstFlag) && |
483 | 56.0k | !(rle_header.Flags & NoBackgroundFlag)) |
484 | 55.9k | { |
485 | 55.9k | int |
486 | 55.9k | j; |
487 | | |
488 | | /* |
489 | | Set background color. |
490 | | */ |
491 | 55.9k | p=rle_pixels; |
492 | 4.10M | for (i=0; i < number_pixels; i++) |
493 | 4.04M | { |
494 | 4.04M | if (!image->matte) |
495 | 8.85M | for (j=0; j < (int) number_planes; j++) |
496 | 4.82M | *p++=background_color[j]; |
497 | 18.9k | else |
498 | 18.9k | { |
499 | 75.9k | for (j=0; j < ((int) number_planes-1); j++) |
500 | 56.9k | *p++=background_color[j]; |
501 | 18.9k | *p++=0; /* initialize matte channel */ |
502 | 18.9k | } |
503 | 4.04M | } |
504 | 55.9k | } |
505 | | /* |
506 | | Read runlength-encoded image. |
507 | | */ |
508 | 56.8k | plane=0; |
509 | 56.8k | x=0; |
510 | 56.8k | y=0; |
511 | 56.8k | opcode=ReadBlobByte(image); |
512 | 56.8k | if (opcode == EOF) |
513 | 51.5k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
514 | 51.5k | do |
515 | 11.6M | { |
516 | 11.6M | switch (opcode & 0x3f) |
517 | 11.6M | { |
518 | 5.95M | case SkipLinesOp: |
519 | 5.95M | { |
520 | 5.95M | operand=ReadBlobByte(image); |
521 | 5.95M | if (operand == EOF) |
522 | 5.95M | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
523 | 5.95M | if (opcode & 0x40) |
524 | 5.86M | { |
525 | 5.86M | operand=ReadBlobLSBShort(image); |
526 | 5.86M | if (EOFBlob(image)) |
527 | 5.86M | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
528 | 5.86M | } |
529 | 5.95M | x=0; |
530 | 5.95M | y+=operand; |
531 | 5.95M | break; |
532 | 5.95M | } |
533 | 61.8k | case SetColorOp: |
534 | 61.8k | { |
535 | 61.8k | operand=ReadBlobByte(image); |
536 | 61.8k | if (operand == EOF) |
537 | 61.2k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
538 | 61.2k | plane=(unsigned char) operand; |
539 | 61.2k | if (plane == 255) |
540 | 12.2k | plane=(unsigned char) (number_planes-1); |
541 | 61.2k | x=0; |
542 | 61.2k | break; |
543 | 61.8k | } |
544 | 58.9k | case SkipPixelsOp: |
545 | 58.9k | { |
546 | 58.9k | operand=ReadBlobByte(image); |
547 | 58.9k | if (operand == EOF) |
548 | 56.9k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
549 | 56.9k | if (opcode & 0x40) |
550 | 29.0k | { |
551 | 29.0k | operand=ReadBlobLSBShort(image); |
552 | 29.0k | if (EOFBlob(image)) |
553 | 28.7k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
554 | 28.7k | } |
555 | 56.6k | x+=operand; |
556 | 56.6k | break; |
557 | 56.9k | } |
558 | 30.9k | case ByteDataOp: |
559 | 30.9k | { |
560 | 30.9k | operand=ReadBlobByte(image); |
561 | 30.9k | if (operand == EOF) |
562 | 28.9k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
563 | 28.9k | if (opcode & 0x40) |
564 | 7.14k | { |
565 | 7.14k | operand=ReadBlobLSBShort(image); |
566 | 7.14k | if (EOFBlob(image)) |
567 | 6.06k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
568 | 6.06k | } |
569 | 27.8k | offset=(((size_t) image->rows-y-1)*image->columns*number_planes)+x*(size_t) number_planes+plane; |
570 | 27.8k | operand++; |
571 | 27.8k | if ((SIZE_MAX - (size_t) rle_pixels) < offset) |
572 | 24.0k | ThrowRLEReaderException(CorruptImageError,UnableToRunlengthDecodeImage,image); |
573 | 24.0k | p=rle_pixels+offset; |
574 | 158k | for (i=0; i < (unsigned int) operand; i++) |
575 | 138k | { |
576 | 138k | pixel=ReadBlobByte(image); |
577 | 138k | if (pixel == EOF) |
578 | 137k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
579 | 137k | if ((p >= rle_pixels) && (p < rle_pixels+rle_bytes)) |
580 | 134k | *p=(unsigned char) pixel; |
581 | 3.76k | else |
582 | 134k | ThrowRLEReaderException(CorruptImageError,UnableToRunlengthDecodeImage,image); |
583 | 134k | p+=number_planes; |
584 | 134k | } |
585 | 19.2k | if (operand & 0x01) |
586 | 16.0k | (void) ReadBlobByte(image); |
587 | 19.2k | x+=operand; |
588 | 19.2k | break; |
589 | 24.0k | } |
590 | 18.0k | case RunDataOp: |
591 | 18.0k | { |
592 | 18.0k | operand=ReadBlobByte(image); |
593 | 18.0k | if (operand == EOF) |
594 | 17.1k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
595 | 17.1k | if (opcode & 0x40) |
596 | 8.62k | { |
597 | 8.62k | operand=ReadBlobLSBShort(image); |
598 | 8.62k | if (EOFBlob(image)) |
599 | 7.21k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
600 | 7.21k | } |
601 | 15.7k | pixel=ReadBlobByte(image); |
602 | 15.7k | if (pixel == EOF) |
603 | 13.9k | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
604 | 13.9k | (void) ReadBlobByte(image); |
605 | 13.9k | operand++; |
606 | 13.9k | offset=(((size_t) image->rows-y-1)*image->columns*number_planes)+x*(size_t) number_planes+plane; |
607 | 13.9k | if ((SIZE_MAX - (size_t) rle_pixels) < offset) |
608 | 12.6k | ThrowRLEReaderException(CorruptImageError,UnableToRunlengthDecodeImage,image); |
609 | 12.6k | p=rle_pixels+offset; |
610 | 221k | for (i=0; i < (unsigned int) operand; i++) |
611 | 214k | { |
612 | 214k | if ((p >= rle_pixels) && (p < rle_pixels+rle_bytes)) |
613 | 208k | *p=pixel; |
614 | 6.13k | else |
615 | 208k | ThrowRLEReaderException(CorruptImageError,UnableToRunlengthDecodeImage,image); |
616 | 208k | p+=number_planes; |
617 | 208k | } |
618 | 6.51k | x+=operand; |
619 | 6.51k | break; |
620 | 12.6k | } |
621 | 5.50M | default: |
622 | 5.50M | break; |
623 | 11.6M | } |
624 | 11.6M | opcode=ReadBlobByte(image); |
625 | 11.6M | if (opcode == EOF) |
626 | 11.5M | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
627 | 11.5M | } while (((opcode & 0x3f) != EOFOp) && (opcode != EOF)); |
628 | 9.96k | if (number_colormaps != 0) |
629 | 8.18k | { |
630 | 8.18k | unsigned int |
631 | 8.18k | mask; |
632 | | |
633 | | /* |
634 | | Apply colormap affineation to image. |
635 | | */ |
636 | 8.18k | mask=(unsigned int) (map_length-1); |
637 | 8.18k | p=rle_pixels; |
638 | 8.18k | if (number_colormaps == 1) |
639 | 5.97M | for (i=0; i < number_pixels; i++) |
640 | 5.97M | { |
641 | 5.97M | index=*p & mask; |
642 | 5.97M | VerifyColormapIndexWithColors(image,index,colormap_entries); |
643 | 5.97M | *p=colormap[index]; |
644 | 5.97M | p++; |
645 | 5.97M | } |
646 | 5.68k | else |
647 | 5.68k | if ((number_planes >= 3) && (number_colormaps >= 3)) |
648 | 229k | for (i=0; i < number_pixels; i++) |
649 | 963k | for (x=0; x < number_planes; x++) |
650 | 735k | { |
651 | 735k | index=x*(unsigned int) map_length+(*p & mask); |
652 | 735k | VerifyColormapIndexWithColors(image,index,colormap_entries); |
653 | 735k | *p=colormap[index]; |
654 | 735k | p++; |
655 | 735k | } |
656 | 8.18k | } |
657 | | /* |
658 | | Initialize image structure. |
659 | | */ |
660 | 9.96k | if (number_planes >= 3) |
661 | 905 | { |
662 | | /* |
663 | | Convert raster image to DirectClass pixel packets. |
664 | | */ |
665 | 905 | p=rle_pixels; |
666 | 37.2k | for (y=0; y < image->rows; y++) |
667 | 36.3k | { |
668 | 36.3k | q=SetImagePixels(image,0,y,image->columns,1); |
669 | 36.3k | if (q == (PixelPacket *) NULL) |
670 | 0 | break; |
671 | 358k | for (x=0; x < image->columns; x++) |
672 | 322k | { |
673 | 322k | q->red=ScaleCharToQuantum(*p++); |
674 | 322k | q->green=ScaleCharToQuantum(*p++); |
675 | 322k | q->blue=ScaleCharToQuantum(*p++); |
676 | 322k | if (image->matte) |
677 | 85.3k | q->opacity=(Quantum) (MaxRGB-ScaleCharToQuantum(*p++)); |
678 | 322k | q++; |
679 | 322k | } |
680 | 36.3k | if (!SyncImagePixels(image)) |
681 | 0 | break; |
682 | 36.3k | if (image->previous == (Image *) NULL) |
683 | 36.3k | if (QuantumTick(y,image->rows)) |
684 | 9.90k | if (!MagickMonitorFormatted(y,image->rows,exception, |
685 | 9.90k | LoadImageText,image->filename, |
686 | 9.90k | image->columns,image->rows)) |
687 | 0 | break; |
688 | 36.3k | } |
689 | 905 | } |
690 | 9.05k | else |
691 | 9.05k | { |
692 | | /* |
693 | | Create colormap. |
694 | | */ |
695 | 9.05k | if (number_colormaps == 0) |
696 | 1.66k | map_length=256; |
697 | 9.05k | if (((unsigned long) map_length != map_length) || |
698 | 9.05k | (!AllocateImageColormap(image,(unsigned long) map_length))) |
699 | 0 | ThrowRLEReaderException(ResourceLimitError,MemoryAllocationFailed, |
700 | 9.05k | image); |
701 | 9.05k | p=colormap; |
702 | 9.05k | if (number_colormaps == 1) |
703 | 13.7k | for (i=0; i < image->colors; i++) |
704 | 11.2k | { |
705 | | /* |
706 | | Pseudocolor. |
707 | | */ |
708 | 11.2k | image->colormap[i].red=ScaleCharToQuantum(i); |
709 | 11.2k | image->colormap[i].green=ScaleCharToQuantum(i); |
710 | 11.2k | image->colormap[i].blue=ScaleCharToQuantum(i); |
711 | 11.2k | } |
712 | 6.64k | else |
713 | 6.64k | if (number_colormaps > 1) |
714 | 36.6k | for (i=0; i < image->colors; i++) |
715 | 31.7k | { |
716 | 31.7k | image->colormap[i].red=ScaleCharToQuantum(*p); |
717 | 31.7k | image->colormap[i].green=ScaleCharToQuantum(*(p+map_length)); |
718 | 31.7k | if (number_colormaps > 2) |
719 | 1.75k | image->colormap[i].blue=ScaleCharToQuantum(*(p+map_length*2)); |
720 | 29.9k | else |
721 | 29.9k | image->colormap[i].blue=0U; |
722 | 31.7k | p++; |
723 | 31.7k | } |
724 | 9.05k | p=rle_pixels; |
725 | 9.05k | if (!image->matte) |
726 | 9.05k | { |
727 | | /* |
728 | | Convert raster image to PseudoClass pixel packets. |
729 | | */ |
730 | 121k | for (y=0; y < image->rows; y++) |
731 | 112k | { |
732 | 112k | q=SetImagePixels(image,0,y,image->columns,1); |
733 | 112k | if (q == (PixelPacket *) NULL) |
734 | 0 | break; |
735 | 112k | indexes=AccessMutableIndexes(image); |
736 | 25.7M | for (x=0; x < image->columns; x++) |
737 | 25.6M | indexes[x]=(*p++); |
738 | 112k | if (!SyncImagePixels(image)) |
739 | 0 | break; |
740 | 112k | if (image->previous == (Image *) NULL) |
741 | 112k | if (QuantumTick(y,image->rows)) |
742 | 27.2k | if (!MagickMonitorFormatted(y,image->rows,exception, |
743 | 27.2k | LoadImageText,image->filename, |
744 | 27.2k | image->columns,image->rows)) |
745 | 0 | break; |
746 | 112k | } |
747 | 9.05k | (void) SyncImage(image); |
748 | 9.05k | } |
749 | 0 | else |
750 | 0 | { |
751 | | /* |
752 | | Image has a matte channel-- promote to DirectClass. |
753 | | */ |
754 | 0 | for (y=0; y < image->rows; y++) |
755 | 0 | { |
756 | 0 | q=SetImagePixels(image,0,y,image->columns,1); |
757 | 0 | if (q == (PixelPacket *) NULL) |
758 | 0 | break; |
759 | 0 | for (x=0; x < image->columns; x++) |
760 | 0 | { |
761 | 0 | index=*p++; |
762 | 0 | VerifyColormapIndex(image,index); |
763 | 0 | q->red=image->colormap[index].red; |
764 | 0 | index=*p++; |
765 | 0 | VerifyColormapIndex(image,index); |
766 | 0 | q->green=image->colormap[index].green; |
767 | 0 | index=*p++; |
768 | 0 | VerifyColormapIndex(image,index); |
769 | 0 | q->blue=image->colormap[index].blue; |
770 | 0 | q->opacity=(Quantum) (MaxRGB-ScaleCharToQuantum(*p++)); |
771 | 0 | q++; |
772 | 0 | } |
773 | 0 | if (!SyncImagePixels(image)) |
774 | 0 | break; |
775 | 0 | if (image->previous == (Image *) NULL) |
776 | 0 | if (QuantumTick(y,image->rows)) |
777 | 0 | if (!MagickMonitorFormatted(y,image->rows,exception, |
778 | 0 | LoadImageText, |
779 | 0 | image->filename, |
780 | 0 | image->columns,image->rows)) |
781 | 0 | break; |
782 | 0 | } |
783 | 0 | MagickFreeMemory(image->colormap); |
784 | 0 | image->colormap=(PixelPacket *) NULL; |
785 | 0 | image->storage_class=DirectClass; |
786 | 0 | image->colors=0; |
787 | 0 | } |
788 | 9.05k | } |
789 | 9.96k | MagickFreeResourceLimitedMemory(colormap); |
790 | 9.96k | MagickFreeResourceLimitedMemory(rle_pixels); |
791 | 9.96k | if (EOFBlob(image)) |
792 | 0 | { |
793 | 0 | ThrowRLEReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
794 | 0 | } |
795 | 9.96k | CloseBlob(image); |
796 | 9.96k | StopTimer(&image->timer); |
797 | 9.96k | return(image); |
798 | 9.96k | } |
799 | | |
800 | | /* |
801 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
802 | | % % |
803 | | % % |
804 | | % % |
805 | | % R e g i s t e r R L E I m a g e % |
806 | | % % |
807 | | % % |
808 | | % % |
809 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
810 | | % |
811 | | % Method RegisterRLEImage adds attributes for the RLE image format to |
812 | | % the list of supported formats. The attributes include the image format |
813 | | % tag, a method to read and/or write the format, whether the format |
814 | | % supports the saving of more than one frame to the same file or blob, |
815 | | % whether the format supports native in-memory I/O, and a brief |
816 | | % description of the format. |
817 | | % |
818 | | % The format of the RegisterRLEImage method is: |
819 | | % |
820 | | % RegisterRLEImage(void) |
821 | | % |
822 | | */ |
823 | | ModuleExport void RegisterRLEImage(void) |
824 | 5 | { |
825 | 5 | MagickInfo |
826 | 5 | *entry; |
827 | | |
828 | 5 | entry=SetMagickInfo("RLE"); |
829 | 5 | entry->decoder=(DecoderHandler) ReadRLEImage; |
830 | 5 | entry->magick=(MagickHandler) IsRLE; |
831 | 5 | entry->adjoin=False; |
832 | 5 | entry->description="Utah Run length encoded image"; |
833 | 5 | entry->module="RLE"; |
834 | 5 | entry->coder_class=UnstableCoderClass; |
835 | 5 | (void) RegisterMagickInfo(entry); |
836 | 5 | } |
837 | | |
838 | | /* |
839 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
840 | | % % |
841 | | % % |
842 | | % % |
843 | | % U n r e g i s t e r R L E I m a g e % |
844 | | % % |
845 | | % % |
846 | | % % |
847 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
848 | | % |
849 | | % Method UnregisterRLEImage removes format registrations made by the |
850 | | % RLE module from the list of supported formats. |
851 | | % |
852 | | % The format of the UnregisterRLEImage method is: |
853 | | % |
854 | | % UnregisterRLEImage(void) |
855 | | % |
856 | | */ |
857 | | ModuleExport void UnregisterRLEImage(void) |
858 | 0 | { |
859 | 0 | (void) UnregisterMagickInfo("RLE"); |
860 | 0 | } |