OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2015 Google Inc. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license that can be | |
5 * found in the LICENSE file. | |
6 */ | |
7 | |
8 #include "SkCodec_libgif.h" | |
9 #include "SkCodecPriv.h" | |
10 #include "SkColorPriv.h" | |
11 #include "SkColorTable.h" | |
12 #include "SkGifInterlaceIter.h" | |
13 #include "SkStream.h" | |
14 #include "SkSwizzler.h" | |
15 #include "SkUtils.h" | |
16 | |
17 /* | |
18 * Checks the start of the stream to see if the image is a gif | |
19 */ | |
20 bool SkGifCodec::IsGif(SkStream* stream) { | |
21 char buf[GIF_STAMP_LEN]; | |
22 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { | |
23 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || | |
24 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || | |
25 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { | |
26 return true; | |
27 } | |
28 } | |
29 return false; | |
30 } | |
31 | |
32 /* | |
33 * Warning reporting function | |
34 */ | |
35 static void gif_warning(const char* msg) { | |
36 SkCodecPrintf("Gif Warning: %s\n", msg); | |
37 } | |
38 | |
39 /* | |
40 * Error function | |
41 */ | |
42 static SkCodec::Result gif_error(const char* msg, | |
43 SkCodec::Result result = SkCodec::kInvalidInput) { | |
44 SkCodecPrintf("Gif Error: %s\n", msg); | |
45 return result; | |
46 } | |
47 | |
48 | |
49 /* | |
50 * Read function that will be passed to gif_lib | |
51 */ | |
52 static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, | |
53 int32_t size) { | |
54 SkStream* stream = (SkStream*) fileType->UserData; | |
55 return (int32_t) stream->read(out, size); | |
56 } | |
57 | |
58 /* | |
59 * Open the gif file | |
60 */ | |
61 static GifFileType* open_gif(SkStream* stream) { | |
62 #if GIFLIB_MAJOR < 5 | |
63 return DGifOpen(stream, read_bytes_callback); | |
64 #else | |
65 return DGifOpen(stream, read_bytes_callback, NULL); | |
66 #endif | |
67 } | |
68 | |
69 /* | |
70 * This function cleans up the gif object after the decode completes | |
71 * It is used in a SkAutoTCallIProc template | |
72 */ | |
73 int32_t SkGifCodec::CloseGif(GifFileType* gif) { | |
74 #if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0) | |
75 return DGifCloseFile(gif); | |
76 #else | |
77 return DGifCloseFile(gif, NULL); | |
78 #endif | |
79 } | |
80 | |
81 /* | |
82 * This function free extension data that has been saved to assist the image | |
83 * decoder | |
84 */ | |
85 void SkGifCodec::FreeExtension(SavedImage* image) { | |
86 if (NULL != image->ExtensionBlocks) { | |
87 #if GIFLIB_MAJOR < 5 | |
88 FreeExtension(image); | |
89 #else | |
90 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks); | |
91 #endif | |
92 } | |
93 } | |
94 | |
95 /* | |
96 * Check if a there is an index of the color table for a transparent pixel | |
97 */ | |
98 static uint32_t find_trans_index(const SavedImage& image) { | |
99 // If there is a transparent index specified, it will be contained in an | |
100 // extension block. We will loop through extension blocks in reverse order | |
101 // to check the most recent extension blocks first. | |
102 for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) { | |
103 // Get an extension block | |
104 const ExtensionBlock& extBlock = image.ExtensionBlocks[i]; | |
105 | |
106 // Specifically, we need to check for a graphics control extension, | |
107 // which may contain transparency information. Also, note that a valid | |
108 // graphics control extension is always four bytes. The fourth byte | |
109 // is the transparent index (if it exists), so we need at least four | |
110 // bytes. | |
111 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && | |
112 extBlock.ByteCount >= 4) { | |
113 | |
114 // Check the transparent color flag which indicates whether a | |
115 // transparent index exists. It is the least significant bit of | |
116 // the first byte of the extension block. | |
117 if (1 == (extBlock.Bytes[0] & 1)) { | |
118 | |
119 // Use uint32_t to prevent sign extending | |
120 return extBlock.Bytes[3]; | |
121 } | |
122 | |
123 // There should only be one graphics control extension for the image frame | |
124 break; | |
125 } | |
126 } | |
127 | |
128 // Flag indicating that a valid index was not found | |
129 return (uint32_t) -1; | |
scroggo
2015/03/31 20:39:11
Why not return a signed type?
msarett
2015/03/31 21:10:56
It's my personal preference for indices to be unsi
| |
130 } | |
131 | |
132 /* | |
133 * Assumes IsGif was called and returned true | |
134 * Creates a gif decoder | |
135 * Reads enough of the stream to determine the image format | |
136 */ | |
137 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { | |
138 // Read gif header, logical screen descriptor, and global color table | |
139 SkAutoTCallIProc<GifFileType, CloseGif> gif(open_gif(stream)); | |
140 | |
141 if (NULL == gif) { | |
142 gif_error("DGifOpen failed.\n"); | |
143 return NULL; | |
144 } | |
145 | |
146 // Get fields from header | |
147 const int32_t width = gif->SWidth; | |
148 const int32_t height = gif->SHeight; | |
149 if (width <= 0 || height <= 0) { | |
150 gif_error("Invalid dimensions.\n"); | |
151 return NULL; | |
152 } | |
153 | |
154 // Return the codec | |
155 // kIndex is the most natural color type for gifs, so we set this as | |
156 // the default. | |
157 // Many gifs specify a color table index for transparent pixels. Every | |
158 // other pixel is guaranteed to be opaque. Despite this, because of the | |
159 // possiblity of transparent pixels, we cannot assume that the image is | |
160 // opaque. We have the option to set the alpha type as kPremul or | |
161 // kUnpremul. Both are valid since the alpha component will always be | |
162 // 0xFF or the entire 32-bit pixel will be set to zero. We prefer | |
163 // kPremul because we support kPremul, and it is more efficient to | |
164 // use kPremul directly even when kUnpremul is supported. | |
165 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, | |
166 kIndex_8_SkColorType, kPremul_SkAlphaType); | |
167 return SkNEW_ARGS(SkGifCodec, (imageInfo, stream, gif.detach())); | |
168 } | |
169 | |
170 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, | |
171 GifFileType* gif) | |
172 : INHERITED(srcInfo, stream) | |
173 , fGif(gif) | |
174 {} | |
175 | |
176 /* | |
177 * Checks if the conversion between the input image and the requested output | |
178 * image has been implemented | |
179 */ | |
180 static bool conversion_possible(const SkImageInfo& dst, | |
181 const SkImageInfo& src) { | |
182 // Ensure that the profile type is unchanged | |
183 if (dst.profileType() != src.profileType()) { | |
184 return false; | |
185 } | |
186 | |
187 // Check for supported color and alpha types | |
188 switch (dst.colorType()) { | |
189 case kN32_SkColorType: | |
190 return kPremul_SkAlphaType == dst.alphaType() || | |
191 kUnpremul_SkAlphaType == dst.alphaType(); | |
192 default: | |
193 return false; | |
194 } | |
195 } | |
196 | |
197 /* | |
198 * Initiates the gif decode | |
199 */ | |
200 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, | |
201 void* dst, size_t dstRowBytes, | |
202 const Options& opts, SkPMColor*, int*) { | |
203 // Check for valid input parameters | |
204 if (!this->rewindIfNeeded()) { | |
205 return kCouldNotRewind; | |
206 } | |
207 if (dstInfo.dimensions() != this->getInfo().dimensions()) { | |
208 return gif_error("Scaling not supported.\n", kInvalidScale); | |
209 } | |
210 if (!conversion_possible(dstInfo, this->getInfo())) { | |
211 return gif_error("Cannot convert input type to output type.\n", | |
212 kInvalidConversion); | |
213 } | |
214 | |
215 // Use this as a container to hold information about any gif extension | |
216 // blocks. This generally stores transparency and animation instructions. | |
217 SavedImage saveExt; | |
218 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); | |
219 saveExt.ExtensionBlocks = NULL; | |
220 saveExt.ExtensionBlockCount = 0; | |
221 GifByteType* extData; | |
222 #if GIFLIB_MAJOR >= 5 | |
223 int32_t extFunction; | |
224 #endif | |
225 | |
226 // We will loop over components of gif images until we find an image. Once | |
227 // we find an image, we will decode and return it. While many gif files | |
228 // contain more than one image, we will simply decode the first image. | |
229 const int32_t width = dstInfo.width(); | |
230 const int32_t height = dstInfo.height(); | |
231 GifRecordType recordType = UNDEFINED_RECORD_TYPE; | |
232 while (TERMINATE_RECORD_TYPE != recordType) { | |
scroggo
2015/03/31 20:39:11
nit: It seems like this could be a do while loop.
msarett
2015/03/31 21:10:56
Done.
| |
233 // Get the current record type | |
234 if (GIF_ERROR == DGifGetRecordType(fGif, &recordType)) { | |
235 return gif_error("DGifGetRecordType failed.\n", kInvalidInput); | |
236 } | |
237 | |
238 switch (recordType) { | |
239 case IMAGE_DESC_RECORD_TYPE: { | |
240 // Read the image descriptor | |
241 if (GIF_ERROR == DGifGetImageDesc(fGif)) { | |
242 return gif_error("DGifGetImageDesc failed.\n", | |
243 kInvalidInput); | |
244 } | |
245 | |
246 // If reading the image descriptor is successful, the image | |
247 // count will be incremented | |
248 SkASSERT(fGif->ImageCount >= 1); | |
249 SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1]; | |
250 | |
251 // Process the descriptor | |
252 const GifImageDesc& desc = image->ImageDesc; | |
253 int32_t imageLeft = desc.Left; | |
254 int32_t imageTop = desc.Top; | |
255 int32_t innerWidth = desc.Width; | |
256 int32_t innerHeight = desc.Height; | |
257 // Fail on non-positive dimensions | |
258 if (innerWidth <= 0 || innerHeight <= 0) { | |
259 return gif_error("Invalid dimensions for inner image.\n", | |
260 kInvalidInput); | |
261 } | |
262 // Treat the following cases as warnings and try to fix | |
263 if (innerWidth > width) { | |
264 gif_warning("Inner image too wide, shrinking.\n"); | |
265 innerWidth = width; | |
266 imageLeft = 0; | |
267 } else if (imageLeft + innerWidth > width) { | |
268 gif_warning("Shifting inner image to left to fit.\n"); | |
269 imageLeft = width - innerWidth; | |
270 } else if (imageLeft < 0) { | |
271 gif_warning("Shifting image to right to fit\n"); | |
272 imageLeft = 0; | |
273 } | |
274 if (innerHeight > height) { | |
275 gif_warning("Inner image too tall, shrinking.\n"); | |
276 innerHeight = height; | |
277 imageTop = 0; | |
278 } else if (imageTop + innerHeight > height) { | |
279 gif_warning("Shifting inner image up to fit.\n"); | |
280 imageTop = height - innerHeight; | |
281 } else if (imageTop < 0) { | |
282 gif_warning("Shifting image down to fit\n"); | |
283 imageTop = 0; | |
284 } | |
285 | |
286 // Set up the color table | |
287 uint32_t colorCount = 0; | |
288 // Allocate maximum storage to deal with invalid indices safely | |
289 const uint32_t maxColors = 256; | |
290 SkPMColor colorTable[maxColors]; | |
291 ColorMapObject* colorMap = fGif->Image.ColorMap; | |
292 // If there is no local color table, use the global color table | |
293 if (NULL == colorMap) { | |
294 colorMap = fGif->SColorMap; | |
295 } | |
296 if (NULL != colorMap) { | |
297 colorCount = colorMap->ColorCount; | |
298 SkASSERT(colorCount == | |
299 (unsigned) (1 << (colorMap->BitsPerPixel))); | |
300 SkASSERT(colorCount <= 256); | |
301 for (uint32_t i = 0; i < colorCount; i++) { | |
302 colorTable[i] = SkPackARGB32(0xFF, | |
303 colorMap->Colors[i].Red, | |
304 colorMap->Colors[i].Green, | |
305 colorMap->Colors[i].Blue); | |
306 } | |
307 } | |
308 | |
309 // This is used to fill unspecified pixels in the image data. | |
310 uint32_t fillIndex = fGif->SBackGroundColor; | |
311 bool fillBackground = true; | |
312 ZeroInitialized zeroInit = opts.fZeroInitialized; | |
313 | |
314 // Gifs have the option to specify the color at a single | |
315 // index of the color table as transparent. | |
316 { | |
317 uint32_t transIndex = find_trans_index(saveExt); | |
318 | |
319 // If the background is already zeroed and we have a valid | |
320 // transparent index, we do not need to fill the background. | |
321 if (transIndex < colorCount) { | |
scroggo
2015/03/31 20:39:11
This seems a little sneaky:
It seems that find_tr
msarett
2015/03/31 21:10:56
Done.
| |
322 colorTable[transIndex] = SK_ColorTRANSPARENT; | |
323 // If there is a transparent index, we also use this as | |
324 // the fill index. | |
325 fillIndex = transIndex; | |
326 fillBackground = (kYes_ZeroInitialized != zeroInit); | |
327 } else if (fillIndex >= colorCount) { | |
328 // If the fill index is invalid, we default to 0. This | |
329 // behavior is unspecified but matches SkImageDecoder. | |
330 fillIndex = 0; | |
331 } | |
332 } | |
333 | |
334 // Fill in the color table for indices greater than color count. | |
335 // This allows for predictable, safe behavior. | |
336 for (uint32_t i = colorCount; i < maxColors; i++) { | |
337 colorTable[i] = colorTable[fillIndex]; | |
338 } | |
339 | |
340 // Check if image is only a subset of the image frame | |
341 SkAutoTDelete<SkSwizzler> swizzler(NULL); | |
342 if (innerWidth < width || innerHeight < height) { | |
343 | |
344 // Modify the destination info | |
345 const SkImageInfo subsetDstInfo = | |
346 dstInfo.makeWH(innerWidth, innerHeight); | |
347 | |
348 // Fill the destination with the fill color | |
349 // FIXME: This may not be the behavior that we want for | |
350 // animated gifs where we draw on top of the | |
351 // previous frame. | |
352 SkColorType dstColorType = dstInfo.colorType(); | |
353 if (fillBackground) { | |
354 switch (dstColorType) { | |
355 case kN32_SkColorType: | |
356 sk_memset32((SkPMColor*) dst, | |
357 colorTable[fillIndex], | |
358 ((int) dstRowBytes) * height | |
359 / sizeof(SkPMColor)); | |
360 break; | |
361 default: | |
362 SkASSERT(false); | |
363 break; | |
364 } | |
365 } | |
366 | |
367 // Modify the dst pointer | |
368 const int32_t dstBytesPerPixel = | |
369 SkColorTypeBytesPerPixel(dstColorType); | |
370 void* subsetDst = SkTAddOffset<void*>(dst, | |
371 dstRowBytes * imageTop + | |
372 dstBytesPerPixel * imageLeft); | |
373 | |
374 // Create the subset swizzler | |
375 swizzler.reset(SkSwizzler::CreateSwizzler( | |
376 SkSwizzler::kIndex, colorTable, subsetDstInfo, | |
377 subsetDst, dstRowBytes, zeroInit)); | |
378 } else { | |
379 // Create the fully dimensional swizzler | |
380 swizzler.reset(SkSwizzler::CreateSwizzler( | |
381 SkSwizzler::kIndex, colorTable, dstInfo, dst, | |
382 dstRowBytes, zeroInit)); | |
383 } | |
384 | |
385 // Stores output from dgiflib and input to the swizzler | |
386 SkAutoTDeleteArray<uint8_t> | |
387 buffer(SkNEW_ARRAY(uint8_t, innerWidth)); | |
388 | |
389 // Check the interlace flag and iterate over rows of the input | |
390 if (fGif->Image.Interlace) { | |
391 // In interlace mode, the rows of input are rearranged in | |
392 // the output image. We use an iterator to take care of | |
393 // the rearranging. | |
394 SkGifInterlaceIter iter(innerHeight); | |
395 for (int32_t y = 0; y < innerHeight; y++) { | |
396 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), | |
397 innerWidth)) { | |
398 // Recover from error by filling remainder of image | |
399 if (fillBackground) { | |
400 memset(buffer.get(), fillIndex, innerWidth); | |
401 for (; y < innerHeight; y++) { | |
402 swizzler->next(buffer.get(), iter.nextY()); | |
403 } | |
404 } | |
405 return gif_error(SkStringPrintf( | |
406 "Could not decode line %d of %d.\n", | |
407 y, height - 1).c_str(), kIncompleteInput); | |
408 } | |
409 swizzler->next(buffer.get(), iter.nextY()); | |
410 } | |
411 } else { | |
412 // Standard mode | |
413 for (int32_t y = 0; y < innerHeight; y++) { | |
414 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), | |
415 innerWidth)) { | |
416 if (fillBackground) { | |
417 SkPMColor* dstPtr = (SkPMColor*) SkTAddOffset | |
418 <void*>(dst, y * dstRowBytes); | |
419 sk_memset32(dstPtr, colorTable[fillIndex], | |
420 (height - y) * ((int) dstRowBytes) | |
421 / sizeof(SkPMColor)); | |
422 } | |
423 return gif_error(SkStringPrintf( | |
424 "Could not decode line %d of %d.\n", | |
425 y, height - 1).c_str(), kIncompleteInput); | |
426 } | |
427 swizzler->next(buffer.get()); | |
428 } | |
429 } | |
430 | |
431 // FIXME: Gif files may have multiple images stored in a single | |
432 // file. This is most commonly used to enable | |
433 // animations. Since we are leaving animated gifs as a | |
434 // TODO, we will return kSuccess after decoding the | |
435 // first image in the file. This is the same behavior | |
436 // as SkImageDecoder_libgif. | |
437 // | |
438 // Most times this works pretty well, but sometimes it | |
439 // doesn't. For example, I have an animated test image | |
440 // where the first image in the file is 1x1, but the | |
441 // subsequent images are meaningful. This currently | |
442 // displays the 1x1 image, which is not ideal. Right | |
443 // now I am leaving this as an issue that will be | |
444 // addressed when we implement animated gifs. | |
445 // | |
446 // It is also possible (not explicitly disallowed in the | |
447 // specification) that gif files provide multiple | |
448 // images in a single file that are all meant to be | |
449 // displayed in the same frame together. I will | |
450 // currently leave this unimplemented until I find a | |
451 // test case that expects this behavior. | |
452 return kSuccess; | |
453 } | |
454 | |
455 // Extensions are used to specify special properties of the image | |
456 // such as transparency or animation. | |
457 case EXTENSION_RECORD_TYPE: | |
458 // Read extension data | |
459 #if GIFLIB_MAJOR < 5 | |
460 if (GIF_ERROR == | |
461 DGifGetExtension(fGif, &saveExt.Function, &extData)) { | |
462 #else | |
463 if (GIF_ERROR == | |
464 DGifGetExtension(fGif, &extFunction, &extData)) { | |
465 #endif | |
466 return gif_error("Could not get extension.\n", | |
467 kIncompleteInput); | |
468 } | |
469 | |
470 // Create an extension block with our data | |
471 while (NULL != extData) { | |
472 // Add a single block | |
473 #if GIFLIB_MAJOR < 5 | |
474 if (GIF_ERROR == AddExtensionBlock(&saveExt, extData[0], | |
475 &extData[1])) { | |
476 #else | |
477 if (GIF_ERROR == | |
478 GifAddExtensionBlock(&saveExt.ExtensionBlockCount, | |
479 &saveExt.ExtensionBlocks, extFunction, extData[0], | |
480 &extData[1])) { | |
481 #endif | |
482 return gif_error("Could not add extension block.\n", | |
483 kIncompleteInput); | |
484 } | |
485 // Move to the next block | |
486 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) { | |
487 return gif_error("Could not get next extension.\n", | |
488 kIncompleteInput); | |
489 } | |
490 #if GIFLIB_MAJOR < 5 | |
491 saveExt.Function = 0; | |
492 #endif | |
493 } | |
494 break; | |
495 | |
496 // Signals the end of the gif file | |
497 case TERMINATE_RECORD_TYPE: | |
498 break; | |
499 | |
500 default: | |
501 // giflib returns an error code if the record type is not known. | |
502 // We should catch this error immediately. | |
503 SkASSERT(false); | |
504 break; | |
505 } | |
506 } | |
507 | |
508 return gif_error("Could not find any images to decode in gif file.\n", | |
509 kInvalidInput); | |
510 } | |
OLD | NEW |