• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2006 The Android Open Source Project
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 "SkColor.h"
9 #include "SkColorPriv.h"
10 #include "SkColorTable.h"
11 #include "SkImageDecoder.h"
12 #include "SkRTConf.h"
13 #include "SkScaledBitmapSampler.h"
14 #include "SkStream.h"
15 #include "SkTemplates.h"
16 #include "SkUtils.h"
17 
18 #include "gif_lib.h"
19 
20 class SkGIFImageDecoder : public SkImageDecoder {
21 public:
getFormat() const22     virtual Format getFormat() const SK_OVERRIDE {
23         return kGIF_Format;
24     }
25 
26 protected:
27     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
28 
29 private:
30     typedef SkImageDecoder INHERITED;
31 };
32 
33 static const uint8_t gStartingIterlaceYValue[] = {
34     0, 4, 2, 1
35 };
36 static const uint8_t gDeltaIterlaceYValue[] = {
37     8, 8, 4, 2
38 };
39 
40 SK_CONF_DECLARE(bool, c_suppressGIFImageDecoderWarnings,
41                 "images.gif.suppressDecoderWarnings", true,
42                 "Suppress GIF warnings and errors when calling image decode "
43                 "functions.");
44 
45 
46 /*  Implement the GIF interlace algorithm in an iterator.
47     1) grab every 8th line beginning at 0
48     2) grab every 8th line beginning at 4
49     3) grab every 4th line beginning at 2
50     4) grab every 2nd line beginning at 1
51 */
52 class GifInterlaceIter {
53 public:
GifInterlaceIter(int height)54     GifInterlaceIter(int height) : fHeight(height) {
55         fStartYPtr = gStartingIterlaceYValue;
56         fDeltaYPtr = gDeltaIterlaceYValue;
57 
58         fCurrY = *fStartYPtr++;
59         fDeltaY = *fDeltaYPtr++;
60     }
61 
currY() const62     int currY() const {
63         SkASSERT(fStartYPtr);
64         SkASSERT(fDeltaYPtr);
65         return fCurrY;
66     }
67 
next()68     void next() {
69         SkASSERT(fStartYPtr);
70         SkASSERT(fDeltaYPtr);
71 
72         int y = fCurrY + fDeltaY;
73         // We went from an if statement to a while loop so that we iterate
74         // through fStartYPtr until a valid row is found. This is so that images
75         // that are smaller than 5x5 will not trash memory.
76         while (y >= fHeight) {
77             if (gStartingIterlaceYValue +
78                     SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
79                 // we done
80                 SkDEBUGCODE(fStartYPtr = NULL;)
81                 SkDEBUGCODE(fDeltaYPtr = NULL;)
82                 y = 0;
83             } else {
84                 y = *fStartYPtr++;
85                 fDeltaY = *fDeltaYPtr++;
86             }
87         }
88         fCurrY = y;
89     }
90 
91 private:
92     const int fHeight;
93     int fCurrY;
94     int fDeltaY;
95     const uint8_t* fStartYPtr;
96     const uint8_t* fDeltaYPtr;
97 };
98 
99 ///////////////////////////////////////////////////////////////////////////////
100 
DecodeCallBackProc(GifFileType * fileType,GifByteType * out,int size)101 static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
102                               int size) {
103     SkStream* stream = (SkStream*) fileType->UserData;
104     return (int) stream->read(out, size);
105 }
106 
CheckFreeExtension(SavedImage * Image)107 void CheckFreeExtension(SavedImage* Image) {
108     if (Image->ExtensionBlocks) {
109 #if GIFLIB_MAJOR < 5
110         FreeExtension(Image);
111 #else
112         GifFreeExtensions(&Image->ExtensionBlockCount, &Image->ExtensionBlocks);
113 #endif
114     }
115 }
116 
117 // return NULL on failure
find_colormap(const GifFileType * gif)118 static const ColorMapObject* find_colormap(const GifFileType* gif) {
119     const ColorMapObject* cmap = gif->Image.ColorMap;
120     if (NULL == cmap) {
121         cmap = gif->SColorMap;
122     }
123 
124     if (NULL == cmap) {
125         // no colormap found
126         return NULL;
127     }
128     // some sanity checks
129     if (cmap && ((unsigned)cmap->ColorCount > 256 ||
130                  cmap->ColorCount != (1 << cmap->BitsPerPixel))) {
131         cmap = NULL;
132     }
133     return cmap;
134 }
135 
136 // return -1 if not found (i.e. we're completely opaque)
find_transpIndex(const SavedImage & image,int colorCount)137 static int find_transpIndex(const SavedImage& image, int colorCount) {
138     int transpIndex = -1;
139     for (int i = 0; i < image.ExtensionBlockCount; ++i) {
140         const ExtensionBlock* eb = image.ExtensionBlocks + i;
141         if (eb->Function == 0xF9 && eb->ByteCount == 4) {
142             if (eb->Bytes[0] & 1) {
143                 transpIndex = (unsigned char)eb->Bytes[3];
144                 // check for valid transpIndex
145                 if (transpIndex >= colorCount) {
146                     transpIndex = -1;
147                 }
148                 break;
149             }
150         }
151     }
152     return transpIndex;
153 }
154 
error_return(const SkBitmap & bm,const char msg[])155 static bool error_return(const SkBitmap& bm, const char msg[]) {
156     if (!c_suppressGIFImageDecoderWarnings) {
157         SkDebugf("libgif error [%s] bitmap [%d %d] pixels %p colortable %p\n",
158                  msg, bm.width(), bm.height(), bm.getPixels(),
159                  bm.getColorTable());
160     }
161     return false;
162 }
gif_warning(const SkBitmap & bm,const char msg[])163 static void gif_warning(const SkBitmap& bm, const char msg[]) {
164     if (!c_suppressGIFImageDecoderWarnings) {
165         SkDebugf("libgif warning [%s] bitmap [%d %d] pixels %p colortable %p\n",
166                  msg, bm.width(), bm.height(), bm.getPixels(),
167                  bm.getColorTable());
168     }
169 }
170 
171 /**
172  *  Skip rows in the source gif image.
173  *  @param gif Source image.
174  *  @param dst Scratch output needed by gif library call. Must be >= width bytes.
175  *  @param width Bytes per row in the source image.
176  *  @param rowsToSkip Number of rows to skip.
177  *  @return True on success, false on GIF_ERROR.
178  */
skip_src_rows(GifFileType * gif,uint8_t * dst,int width,int rowsToSkip)179 static bool skip_src_rows(GifFileType* gif, uint8_t* dst, int width, int rowsToSkip) {
180     for (int i = 0; i < rowsToSkip; i++) {
181         if (DGifGetLine(gif, dst, width) == GIF_ERROR) {
182             return false;
183         }
184     }
185     return true;
186 }
187 
onDecode(SkStream * sk_stream,SkBitmap * bm,Mode mode)188 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
189 #if GIFLIB_MAJOR < 5
190     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
191 #else
192     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);
193 #endif
194     if (NULL == gif) {
195         return error_return(*bm, "DGifOpen");
196     }
197 
198     SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);
199 
200     SavedImage temp_save;
201     temp_save.ExtensionBlocks=NULL;
202     temp_save.ExtensionBlockCount=0;
203     SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);
204 
205     int width, height;
206     GifRecordType recType;
207     GifByteType *extData;
208 #if GIFLIB_MAJOR >= 5
209     int extFunction;
210 #endif
211     int transpIndex = -1;   // -1 means we don't have it (yet)
212     int fillIndex = gif->SBackGroundColor;
213 
214     do {
215         if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
216             return error_return(*bm, "DGifGetRecordType");
217         }
218 
219         switch (recType) {
220         case IMAGE_DESC_RECORD_TYPE: {
221             if (DGifGetImageDesc(gif) == GIF_ERROR) {
222                 return error_return(*bm, "IMAGE_DESC_RECORD_TYPE");
223             }
224 
225             if (gif->ImageCount < 1) {    // sanity check
226                 return error_return(*bm, "ImageCount < 1");
227             }
228 
229             width = gif->SWidth;
230             height = gif->SHeight;
231 
232             SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
233             const GifImageDesc& desc = image->ImageDesc;
234 
235             int imageLeft = desc.Left;
236             int imageTop = desc.Top;
237             const int innerWidth = desc.Width;
238             const int innerHeight = desc.Height;
239             if (innerWidth <= 0 || innerHeight <= 0) {
240                 return error_return(*bm, "invalid dimensions");
241             }
242 
243             // check for valid descriptor
244             if (innerWidth > width) {
245                 gif_warning(*bm, "image too wide, expanding output to size");
246                 width = innerWidth;
247                 imageLeft = 0;
248             } else if (imageLeft + innerWidth > width) {
249                 gif_warning(*bm, "shifting image left to fit");
250                 imageLeft = width - innerWidth;
251             } else if (imageLeft < 0) {
252                 gif_warning(*bm, "shifting image right to fit");
253                 imageLeft = 0;
254             }
255 
256 
257             if (innerHeight > height) {
258                 gif_warning(*bm, "image too tall,  expanding output to size");
259                 height = innerHeight;
260                 imageTop = 0;
261             } else if (imageTop + innerHeight > height) {
262                 gif_warning(*bm, "shifting image up to fit");
263                 imageTop = height - innerHeight;
264             } else if (imageTop < 0) {
265                 gif_warning(*bm, "shifting image down to fit");
266                 imageTop = 0;
267             }
268 
269             // FIXME: We could give the caller a choice of images or configs.
270             if (!this->chooseFromOneChoice(SkBitmap::kIndex8_Config, width, height)) {
271                 return error_return(*bm, "chooseFromOneChoice");
272             }
273 
274             SkScaledBitmapSampler sampler(width, height, this->getSampleSize());
275 
276             bm->setConfig(SkBitmap::kIndex8_Config, sampler.scaledWidth(),
277                           sampler.scaledHeight());
278 
279             if (SkImageDecoder::kDecodeBounds_Mode == mode) {
280                 return true;
281             }
282 
283 
284             // now we decode the colortable
285             int colorCount = 0;
286             {
287                 // Declare colorPtr here for scope.
288                 SkPMColor colorPtr[256]; // storage for worst-case
289                 const ColorMapObject* cmap = find_colormap(gif);
290                 SkAlphaType alphaType = kOpaque_SkAlphaType;
291                 if (cmap != NULL) {
292                     colorCount = cmap->ColorCount;
293                     if (colorCount > 256) {
294                         colorCount = 256;  // our kIndex8 can't support more
295                     }
296                     for (int index = 0; index < colorCount; index++) {
297                         colorPtr[index] = SkPackARGB32(0xFF,
298                                                        cmap->Colors[index].Red,
299                                                        cmap->Colors[index].Green,
300                                                        cmap->Colors[index].Blue);
301                     }
302                 } else {
303                     // find_colormap() returned NULL.  Some (rare, broken)
304                     // GIFs don't have a color table, so we force one.
305                     gif_warning(*bm, "missing colormap");
306                     colorCount = 256;
307                     sk_memset32(colorPtr, SK_ColorWHITE, colorCount);
308                 }
309                 transpIndex = find_transpIndex(temp_save, colorCount);
310                 if (transpIndex >= 0) {
311                     colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a transparent SkPMColor
312                     alphaType = kPremul_SkAlphaType;
313                     fillIndex = transpIndex;
314                 } else if (fillIndex >= colorCount) {
315                     // gif->SBackGroundColor should be less than colorCount.
316                     fillIndex = 0;  // If not, fix it.
317                 }
318 
319                 SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable,
320                                                   (colorPtr, colorCount,
321                                                    alphaType)));
322                 if (!this->allocPixelRef(bm, ctable)) {
323                     return error_return(*bm, "allocPixelRef");
324                 }
325             }
326 
327             // abort if either inner dimension is <= 0
328             if (innerWidth <= 0 || innerHeight <= 0) {
329                 return error_return(*bm, "non-pos inner width/height");
330             }
331 
332             SkAutoLockPixels alp(*bm);
333 
334             SkAutoMalloc storage(innerWidth);
335             uint8_t* scanline = (uint8_t*) storage.get();
336 
337             // GIF has an option to store the scanlines of an image, plus a larger background,
338             // filled by a fill color. In this case, we will use a subset of the larger bitmap
339             // for sampling.
340             SkBitmap subset;
341             SkBitmap* workingBitmap;
342             // are we only a subset of the total bounds?
343             if ((imageTop | imageLeft) > 0 ||
344                  innerWidth < width || innerHeight < height) {
345                 // Fill the background.
346                 memset(bm->getPixels(), fillIndex, bm->getSize());
347 
348                 // Create a subset of the bitmap.
349                 SkIRect subsetRect(SkIRect::MakeXYWH(imageLeft / sampler.srcDX(),
350                                                      imageTop / sampler.srcDY(),
351                                                      innerWidth / sampler.srcDX(),
352                                                      innerHeight / sampler.srcDY()));
353                 if (!bm->extractSubset(&subset, subsetRect)) {
354                     return error_return(*bm, "Extract failed.");
355                 }
356                 // Update the sampler. We'll now be only sampling into the subset.
357                 sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->getSampleSize());
358                 workingBitmap = &subset;
359             } else {
360                 workingBitmap = bm;
361             }
362 
363             // bm is already locked, but if we had to take a subset, it must be locked also,
364             // so that getPixels() will point to its pixels.
365             SkAutoLockPixels alpWorking(*workingBitmap);
366 
367             if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *this)) {
368                 return error_return(*bm, "Sampler failed to begin.");
369             }
370 
371             // now decode each scanline
372             if (gif->Image.Interlace) {
373                 // Iterate over the height of the source data. The sampler will
374                 // take care of skipping unneeded rows.
375                 GifInterlaceIter iter(innerHeight);
376                 for (int y = 0; y < innerHeight; y++) {
377                     if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
378                         gif_warning(*bm, "interlace DGifGetLine");
379                         memset(scanline, fillIndex, innerWidth);
380                         for (; y < innerHeight; y++) {
381                             sampler.sampleInterlaced(scanline, iter.currY());
382                             iter.next();
383                         }
384                         return true;
385                     }
386                     sampler.sampleInterlaced(scanline, iter.currY());
387                     iter.next();
388                 }
389             } else {
390                 // easy, non-interlace case
391                 const int outHeight = workingBitmap->height();
392                 skip_src_rows(gif, scanline, innerWidth, sampler.srcY0());
393                 for (int y = 0; y < outHeight; y++) {
394                     if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
395                         gif_warning(*bm, "DGifGetLine");
396                         memset(scanline, fillIndex, innerWidth);
397                         for (; y < outHeight; y++) {
398                             sampler.next(scanline);
399                         }
400                         return true;
401                     }
402                     // scanline now contains the raw data. Sample it.
403                     sampler.next(scanline);
404                     if (y < outHeight - 1) {
405                         skip_src_rows(gif, scanline, innerWidth, sampler.srcDY() - 1);
406                     }
407                 }
408                 // skip the rest of the rows (if any)
409                 int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() + 1;
410                 SkASSERT(read <= innerHeight);
411                 skip_src_rows(gif, scanline, innerWidth, innerHeight - read);
412             }
413             return true;
414             } break;
415 
416         case EXTENSION_RECORD_TYPE:
417 #if GIFLIB_MAJOR < 5
418             if (DGifGetExtension(gif, &temp_save.Function,
419                                  &extData) == GIF_ERROR) {
420 #else
421             if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
422 #endif
423                 return error_return(*bm, "DGifGetExtension");
424             }
425 
426             while (extData != NULL) {
427                 /* Create an extension block with our data */
428 #if GIFLIB_MAJOR < 5
429                 if (AddExtensionBlock(&temp_save, extData[0],
430                                       &extData[1]) == GIF_ERROR) {
431 #else
432                 if (GifAddExtensionBlock(&gif->ExtensionBlockCount,
433                                          &gif->ExtensionBlocks,
434                                          extFunction,
435                                          extData[0],
436                                          &extData[1]) == GIF_ERROR) {
437 #endif
438                     return error_return(*bm, "AddExtensionBlock");
439                 }
440                 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
441                     return error_return(*bm, "DGifGetExtensionNext");
442                 }
443 #if GIFLIB_MAJOR < 5
444                 temp_save.Function = 0;
445 #endif
446             }
447             break;
448 
449         case TERMINATE_RECORD_TYPE:
450             break;
451 
452         default:    /* Should be trapped by DGifGetRecordType */
453             break;
454         }
455     } while (recType != TERMINATE_RECORD_TYPE);
456 
457     return true;
458 }
459 
460 ///////////////////////////////////////////////////////////////////////////////
461 DEFINE_DECODER_CREATOR(GIFImageDecoder);
462 ///////////////////////////////////////////////////////////////////////////////
463 
464 static bool is_gif(SkStreamRewindable* stream) {
465     char buf[GIF_STAMP_LEN];
466     if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
467         if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
468                 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
469                 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
470             return true;
471         }
472     }
473     return false;
474 }
475 
476 static SkImageDecoder* sk_libgif_dfactory(SkStreamRewindable* stream) {
477     if (is_gif(stream)) {
478         return SkNEW(SkGIFImageDecoder);
479     }
480     return NULL;
481 }
482 
483 static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory);
484 
485 static SkImageDecoder::Format get_format_gif(SkStreamRewindable* stream) {
486     if (is_gif(stream)) {
487         return SkImageDecoder::kGIF_Format;
488     }
489     return SkImageDecoder::kUnknown_Format;
490 }
491 
492 static SkImageDecoder_FormatReg gFormatReg(get_format_gif);
493