Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Side by Side Diff: src/codec/SkCodec_libgif.cpp

Issue 1022673011: Creating a new wrapper for gif decoder (Closed) Base URL: https://skia.googlesource.com/skia.git@ico-real
Patch Set: Second set of fixes to gif codec Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 static void free_extension(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 int32_t find_trans_index(const SavedImage& image, uint32_t colorCount) {
99 // If there is a transparent index specified, it will be contained in an
100 // extension block.
101 for (int32_t i = 0; i < image.ExtensionBlockCount; i++) {
102 // Get an extension block
103 const ExtensionBlock& extBlock = image.ExtensionBlocks[i];
104
105 // Specifically, we need to check for a graphics control extension,
106 // which may contain transparency information. Also, note that a valid
107 // graphics control extension is always four bytes. The fourth byte
108 // is the transparent index (if it exists), so we need at least four
109 // bytes.
110 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function &&
111 extBlock.ByteCount >= 4) {
112
113 // Check the transparent color flag which indicates whether a
114 // transparent index exists. It is the least significant bit of
115 // the first byte of the extension block.
116 if (1 == (extBlock.Bytes[0] & 1)) {
117
118 // Use uint32_t to prevent sign extending
119 uint32_t transIndex = extBlock.Bytes[3];
120 if (transIndex < colorCount) {
121 return transIndex;
122 }
123 }
124 }
125 }
126
127 // Flag indicating that a valid index was not found
128 return -1;
129 }
130
131 /*
132 * Assumes IsGif was called and returned true
133 * Creates a gif decoder
134 * Reads enough of the stream to determine the image format
135 */
136 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) {
137 // Read gif header, logical screen descriptor, and global color table
138 SkAutoTCallIProc<GifFileType, CloseGif> gif(open_gif(stream));
139
140 if (NULL == gif) {
141 gif_error("DGifOpen failed.\n");
142 return NULL;
143 }
144
145 // Get fields from header
146 const int32_t width = gif->SWidth;
147 const int32_t height = gif->SHeight;
148 if (width <= 0 || height <= 0) {
149 gif_error("Invalid dimensions.\n");
150 return NULL;
151 }
152
153 // Return the codec
154 // kIndex is the most natural color type for gifs, so we set this as
155 // the default.
156 // Many gifs specify a color table index for transparent pixels. Every
157 // other pixel is guaranteed to be opaque. Despite this, because of the
158 // possiblity of transparent pixels, we cannot assume that the image is
159 // opaque. However, we can at least mark it as kPremul, since pixels will
160 // either have a 0xFF alpha component or be completely zeroed.
161 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height,
162 kIndex_8_SkColorType, kPremul_SkAlphaType);
163 return SkNEW_ARGS(SkGifCodec, (imageInfo, stream, gif.detach()));
164 }
165
166 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream,
167 GifFileType* gif)
168 : INHERITED(srcInfo, stream)
169 , fGif(gif)
170 {}
171
172 /*
173 * Checks if the conversion between the input image and the requested output
174 * image has been implemented
175 */
176 static bool conversion_possible(const SkImageInfo& dst,
177 const SkImageInfo& src) {
178 // Ensure that the profile type is unchanged
179 if (dst.profileType() != src.profileType()) {
180 return false;
181 }
182
183 // Check for supported color and alpha types
184 switch (dst.colorType()) {
185 case kN32_SkColorType:
186 return src.alphaType() == dst.alphaType() ||
187 (kPremul_SkAlphaType == dst.alphaType() &&
188 kUnpremul_SkAlphaType == src.alphaType());
189 default:
190 return false;
191 }
192 }
193
194 /*
195 * Initiates the gif decode
196 */
197 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
198 void* dst, size_t dstRowBytes,
199 const Options& opts, SkPMColor*, int*) {
200 // Check for valid input parameters
201 if (!this->rewindIfNeeded()) {
202 return kCouldNotRewind;
203 }
204 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
205 return gif_error("Scaling not supported.\n", kInvalidScale);
206 }
207 if (!conversion_possible(dstInfo, this->getInfo())) {
208 return gif_error("Cannot convert input type to output type.\n",
209 kInvalidConversion);
210 }
211
212 // Use this as a container to hold information about any gif extension
213 // blocks. This generally stores transparency and animation instructions.
214 SavedImage saveExt;
215 SkAutoTCallVProc<SavedImage, free_extension> autoFreeExt(&saveExt);
216 saveExt.ExtensionBlocks = NULL;
217 saveExt.ExtensionBlockCount = 0;
218 GifByteType* extData;
219 #if GIFLIB_MAJOR >= 5
220 int32_t extFunction;
221 #endif
222
223 // We will loop over components of gif images until we find an image. Once
224 // we find an image, we will decode and return it. While many gif files
225 // contain more than one image, we will simply decode the first image.
226 const int32_t width = dstInfo.width();
227 const int32_t height = dstInfo.height();
228 GifRecordType recordType = UNDEFINED_RECORD_TYPE;
229 while (TERMINATE_RECORD_TYPE != recordType) {
230 // Get the current record type
231 if (GIF_ERROR == DGifGetRecordType(fGif, &recordType)) {
232 return gif_error("DGifGetRecordType failed.\n", kInvalidInput);
233 }
234
235 switch (recordType) {
236 case IMAGE_DESC_RECORD_TYPE: {
237 // Read the image descriptor
238 if (GIF_ERROR == DGifGetImageDesc(fGif)) {
239 return gif_error("DGifGetImageDesc failed.\n",
240 kInvalidInput);
241 }
242
243 // If reading the image descriptor is successful, the image
244 // count will be incremented
245 SkASSERT(fGif->ImageCount >= 1);
246 SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1];
247
248 // Process the descriptor
249 const GifImageDesc& desc = image->ImageDesc;
250 int32_t imageLeft = desc.Left;
251 int32_t imageTop = desc.Top;
252 int32_t innerWidth = desc.Width;
253 int32_t innerHeight = desc.Height;
254 // Fail on non-positive dimensions
255 if (innerWidth <= 0 || innerHeight <= 0) {
256 return gif_error("Invalid dimensions for inner image.\n",
257 kInvalidInput);
258 }
259 // Treat the following cases as warnings and try to fix
260 if (innerWidth > width) {
261 gif_warning("Inner image too wide, shrinking.\n");
262 innerWidth = width;
263 imageLeft = 0;
264 } else if (imageLeft + innerWidth > width) {
265 gif_warning("Shifting inner image to left to fit.\n");
266 imageLeft = width - innerWidth;
267 } else if (imageLeft < 0) {
268 gif_warning("Shifting image to right to fit\n");
269 imageLeft = 0;
270 }
271 if (innerHeight > height) {
272 gif_warning("Inner image too tall, shrinking.\n");
273 innerHeight = height;
274 imageTop = 0;
275 } else if (imageTop + innerHeight > height) {
276 gif_warning("Shifting inner image up to fit.\n");
277 imageTop = height - innerHeight;
278 } else if (imageTop < 0) {
279 gif_warning("Shifting image down to fit\n");
280 imageTop = 0;
281 }
282
283 // Set up the color table
284 uint32_t colorCount = 0;
285 // Allocate maximum storage to deal with invalid indices safely
286 const uint32_t maxColors = 256;
287 SkPMColor colorTable[maxColors];
288 ColorMapObject* colorMap = fGif->Image.ColorMap;
289 // If there is no local color table, use the global color table
290 if (NULL == colorMap) {
291 colorMap = fGif->SColorMap;
292 }
293 if (NULL != colorMap) {
294 colorCount = colorMap->ColorCount;
295 SkASSERT(colorCount ==
296 (unsigned) (1 << (colorMap->BitsPerPixel)));
297 SkASSERT(colorCount <= 256);
298 for (uint32_t i = 0; i < colorCount; i++) {
299 colorTable[i] = SkPackARGB32(0xFF,
300 colorMap->Colors[i].Red,
301 colorMap->Colors[i].Green,
302 colorMap->Colors[i].Blue);
303 }
304 }
305
306 // This is used to fill unspecified pixels in the image data.
307 uint32_t fillIndex = fGif->SBackGroundColor;
308 bool fillBackground = true;
309 ZeroInitialized zeroInit = opts.fZeroInitialized;
310
311 // Gifs have the option to specify the color at a single
312 // index of the color table as transparent.
313 {
314 int32_t transIndex = find_trans_index(saveExt, colorCount);
315
316 // If the background is already zeroed and we have a valid
317 // transparent index, we do not need to fill the background.
318 if (transIndex >= 0) {
319 colorTable[transIndex] = SK_ColorTRANSPARENT;
320 // If there is a transparent index, we also use this as
321 // the fill index.
322 fillIndex = transIndex;
323 fillBackground = (kYes_ZeroInitialized != zeroInit);
324 } else if (fillIndex >= colorCount) {
325 // If the fill index is invalid, we default to 0. This
326 // behavior is unspecified but matches SkImageDecoder.
327 fillIndex = 0;
328 }
329 }
330
331 // Fill in the color table for indices greater than color count.
332 // This allows for predictable, safe behavior.
333 for (uint32_t i = colorCount; i < maxColors; i++) {
334 colorTable[i] = colorTable[fillIndex];
335 }
336
337 // Check if image is only a subset of the image frame
338 SkAutoTDelete<SkSwizzler> swizzler(NULL);
339 if (innerWidth < width || innerHeight < height) {
340
341 // Modify the destination info
342 const SkImageInfo subsetDstInfo =
343 dstInfo.makeWH(innerWidth, innerHeight);
344
345 // Fill the destination with the fill color
346 // FIXME: This may not be the behavior that we want for
347 // animated gifs where we draw on top of the
348 // previous frame.
349 SkColorType dstColorType = dstInfo.colorType();
350 if (fillBackground) {
351 switch (dstColorType) {
352 case kN32_SkColorType:
353 sk_memset32((SkPMColor*) dst,
354 colorTable[fillIndex],
355 dstRowBytes * height / sizeof(SkPMColor));
356 break;
357 default:
358 SkASSERT(false);
359 break;
360 }
361 }
362
363 // Modify the dst pointer
364 const int32_t dstBytesPerPixel =
365 SkColorTypeBytesPerPixel(dstColorType);
366 void* subsetDst = SkTAddOffset<void*>(dst,
367 dstRowBytes * imageTop +
368 dstBytesPerPixel * imageLeft);
369
370 // Create the subset swizzler
371 swizzler.reset(SkSwizzler::CreateSwizzler(
372 SkSwizzler::kIndex, colorTable, subsetDstInfo,
373 subsetDst, dstRowBytes, zeroInit));
374 } else {
375 // Create the fully dimensional swizzler
376 swizzler.reset(SkSwizzler::CreateSwizzler(
377 SkSwizzler::kIndex, colorTable, dstInfo, dst,
378 dstRowBytes, zeroInit));
379 }
380
381 // Stores output from dgiflib and input to the swizzler
382 SkAutoTDeleteArray<uint8_t>
383 buffer(SkNEW_ARRAY(uint8_t, innerWidth));
384
385 // Check the interlace flag and iterate over rows of the input
386 if (fGif->Image.Interlace) {
387 // In interlace mode, the rows of input are rearranged in
388 // the output image. We use an iterator to take care of
389 // the rearranging.
390 SkGifInterlaceIter iter(innerHeight);
391 for (int32_t y = 0; y < innerHeight; y++) {
392 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(),
393 innerWidth)) {
394 // Recover from error by filling remainder of image
395 if (fillBackground) {
396 memset(buffer.get(), fillIndex, innerWidth);
397 for (; y < innerHeight; y++) {
398 swizzler->next(buffer.get(), iter.nextY());
399 }
400 }
401 return gif_error(SkStringPrintf(
402 "Could not decode line %d of %d.\n",
403 y, height - 1).c_str(), kIncompleteInput);
404 }
405 swizzler->next(buffer.get(), iter.nextY());
406 }
407 } else {
408 // Standard mode
409 for (int32_t y = 0; y < innerHeight; y++) {
410 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(),
411 innerWidth)) {
412 if (fillBackground) {
413 SkPMColor* dstPtr = (SkPMColor*) SkTAddOffset
414 <void*>(dst, y * dstRowBytes);
415 sk_memset32(dstPtr, colorTable[fillIndex],
416 (height - y) * dstRowBytes
417 / sizeof(SkPMColor));
418 }
419 return gif_error(SkStringPrintf(
420 "Could not decode line %d of %d.\n",
421 y, height - 1).c_str(), kIncompleteInput);
422 }
423 swizzler->next(buffer.get());
424 }
425 }
426
427 // FIXME: Gif files may have multiple images stored in a single
428 // file. This is most commonly used to enable
429 // animations. Since we are leaving animated gifs as a
430 // TODO, we will return kSuccess after decoding the
431 // first image in the file. This is the same behavior
432 // as SkImageDecoder_libgif.
433 //
434 // Most times this works pretty well, but sometimes it
435 // doesn't. For example, I have an animated test image
436 // where the first image in the file is 1x1, but the
437 // subsequent images are meaningful. This currently
438 // displays the 1x1 image, which is not ideal. Right
439 // now I am leaving this as an issue that will be
440 // addressed when we implement animated gifs.
441 //
442 // It is also possible (not explicitly disallowed in the
443 // specification) that gif files provide multiple
444 // images in a single file that are all meant to be
445 // displayed in the same frame together. I will
446 // currently leave this unimplemented until I find a
447 // test case that expects this behavior.
448 return kSuccess;
449 }
450
451 // Extensions are used to specify special properties of the image
452 // such as transparency or animation.
453 case EXTENSION_RECORD_TYPE:
454 // Read extension data
455 #if GIFLIB_MAJOR < 5
456 if (GIF_ERROR ==
457 DGifGetExtension(fGif, &saveExt.Function, &extData)) {
458 #else
459 if (GIF_ERROR ==
460 DGifGetExtension(fGif, &extFunction, &extData)) {
461 #endif
462 return gif_error("Could not get extension.\n",
463 kIncompleteInput);
464 }
465
466 // Create an extension block with our data
467 while (NULL != extData) {
468 // Add a single block
469 #if GIFLIB_MAJOR < 5
470 if (GIF_ERROR == AddExtensionBlock(&saveExt, extData[0],
471 &extData[1])) {
472 #else
473 if (GIF_ERROR ==
474 GifAddExtensionBlock(&saveExt.ExtensionBlockCount,
475 &saveExt.ExtensionBlocks, extFunction, extData[0],
476 &extData[1])) {
477 #endif
478 return gif_error("Could not add extension block.\n",
479 kIncompleteInput);
480 }
481 // Move to the next block
482 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) {
483 return gif_error("Could not get next extension.\n",
484 kIncompleteInput);
485 }
486 #if GIFLIB_MAJOR < 5
487 saveExt.Function = 0;
488 #endif
489 }
490 break;
491
492 // Signals the end of the gif file
493 case TERMINATE_RECORD_TYPE:
494 break;
495
496 default:
497 break;
498 }
499 }
500
501 return gif_error("Could not find any images to decode in gif file.\n",
502 kInvalidInput);
503 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698