• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2006 The Android Open Source Project
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 
10 #include "SkImageDecoder.h"
11 #include "SkColor.h"
12 #include "SkColorPriv.h"
13 #include "SkStream.h"
14 #include "SkTemplates.h"
15 #include "SkPackBits.h"
16 
17 #include "gif_lib.h"
18 
19 class SkGIFImageDecoder : public SkImageDecoder {
20 public:
getFormat() const21     virtual Format getFormat() const {
22         return kGIF_Format;
23     }
24 
25 protected:
26     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
27 };
28 
29 static const uint8_t gStartingIterlaceYValue[] = {
30     0, 4, 2, 1
31 };
32 static const uint8_t gDeltaIterlaceYValue[] = {
33     8, 8, 4, 2
34 };
35 
36 /*  Implement the GIF interlace algorithm in an iterator.
37     1) grab every 8th line beginning at 0
38     2) grab every 8th line beginning at 4
39     3) grab every 4th line beginning at 2
40     4) grab every 2nd line beginning at 1
41 */
42 class GifInterlaceIter {
43 public:
GifInterlaceIter(int height)44     GifInterlaceIter(int height) : fHeight(height) {
45         fStartYPtr = gStartingIterlaceYValue;
46         fDeltaYPtr = gDeltaIterlaceYValue;
47 
48         fCurrY = *fStartYPtr++;
49         fDeltaY = *fDeltaYPtr++;
50     }
51 
currY() const52     int currY() const {
53         SkASSERT(fStartYPtr);
54         SkASSERT(fDeltaYPtr);
55         return fCurrY;
56     }
57 
next()58     void next() {
59         SkASSERT(fStartYPtr);
60         SkASSERT(fDeltaYPtr);
61 
62         int y = fCurrY + fDeltaY;
63         // We went from an if statement to a while loop so that we iterate
64         // through fStartYPtr until a valid row is found. This is so that images
65         // that are smaller than 5x5 will not trash memory.
66         while (y >= fHeight) {
67             if (gStartingIterlaceYValue +
68                     SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
69                 // we done
70                 SkDEBUGCODE(fStartYPtr = NULL;)
71                 SkDEBUGCODE(fDeltaYPtr = NULL;)
72                 y = 0;
73             } else {
74                 y = *fStartYPtr++;
75                 fDeltaY = *fDeltaYPtr++;
76             }
77         }
78         fCurrY = y;
79     }
80 
81 private:
82     const int fHeight;
83     int fCurrY;
84     int fDeltaY;
85     const uint8_t* fStartYPtr;
86     const uint8_t* fDeltaYPtr;
87 };
88 
89 ///////////////////////////////////////////////////////////////////////////////
90 
91 //#define GIF_STAMP       "GIF"    /* First chars in file - GIF stamp. */
92 //#define GIF_STAMP_LEN   (sizeof(GIF_STAMP) - 1)
93 
DecodeCallBackProc(GifFileType * fileType,GifByteType * out,int size)94 static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
95                               int size) {
96     SkStream* stream = (SkStream*) fileType->UserData;
97     return (int) stream->read(out, size);
98 }
99 
CheckFreeExtension(SavedImage * Image)100 void CheckFreeExtension(SavedImage* Image) {
101     if (Image->ExtensionBlocks) {
102 #if GIFLIB_MAJOR < 5
103         FreeExtension(Image);
104 #else
105         GifFreeExtensions(&Image->ExtensionBlockCount, &Image->ExtensionBlocks);
106 #endif
107     }
108 }
109 
110 // return NULL on failure
find_colormap(const GifFileType * gif)111 static const ColorMapObject* find_colormap(const GifFileType* gif) {
112     const ColorMapObject* cmap = gif->Image.ColorMap;
113     if (NULL == cmap) {
114         cmap = gif->SColorMap;
115     }
116 
117     if (NULL == cmap) {
118         // no colormap found
119         return NULL;
120     }
121     // some sanity checks
122     if (cmap && ((unsigned)cmap->ColorCount > 256 ||
123                  cmap->ColorCount != (1 << cmap->BitsPerPixel))) {
124         cmap = NULL;
125     }
126     return cmap;
127 }
128 
129 // return -1 if not found (i.e. we're completely opaque)
find_transpIndex(const SavedImage & image,int colorCount)130 static int find_transpIndex(const SavedImage& image, int colorCount) {
131     int transpIndex = -1;
132     for (int i = 0; i < image.ExtensionBlockCount; ++i) {
133         const ExtensionBlock* eb = image.ExtensionBlocks + i;
134         if (eb->Function == 0xF9 && eb->ByteCount == 4) {
135             if (eb->Bytes[0] & 1) {
136                 transpIndex = (unsigned char)eb->Bytes[3];
137                 // check for valid transpIndex
138                 if (transpIndex >= colorCount) {
139                     transpIndex = -1;
140                 }
141                 break;
142             }
143         }
144     }
145     return transpIndex;
146 }
147 
error_return(GifFileType * gif,const SkBitmap & bm,const char msg[])148 static bool error_return(GifFileType* gif, const SkBitmap& bm,
149                          const char msg[]) {
150 #if 0
151     SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n",
152              msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable());
153 #endif
154     return false;
155 }
156 
onDecode(SkStream * sk_stream,SkBitmap * bm,Mode mode)157 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
158 #if GIFLIB_MAJOR < 5
159     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
160 #else
161     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);
162 #endif
163     if (NULL == gif) {
164         return error_return(gif, *bm, "DGifOpen");
165     }
166 
167     SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);
168 
169     SavedImage temp_save;
170     temp_save.ExtensionBlocks=NULL;
171     temp_save.ExtensionBlockCount=0;
172     SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);
173 
174     int width, height;
175     GifRecordType recType;
176     GifByteType *extData;
177 #if GIFLIB_MAJOR >= 5
178     int extFunction;
179 #endif
180     int transpIndex = -1;   // -1 means we don't have it (yet)
181 
182     do {
183         if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
184             return error_return(gif, *bm, "DGifGetRecordType");
185         }
186 
187         switch (recType) {
188         case IMAGE_DESC_RECORD_TYPE: {
189             if (DGifGetImageDesc(gif) == GIF_ERROR) {
190                 return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE");
191             }
192 
193             if (gif->ImageCount < 1) {    // sanity check
194                 return error_return(gif, *bm, "ImageCount < 1");
195             }
196 
197             width = gif->SWidth;
198             height = gif->SHeight;
199             if (width <= 0 || height <= 0 ||
200                 !this->chooseFromOneChoice(SkBitmap::kIndex8_Config,
201                                            width, height)) {
202                 return error_return(gif, *bm, "chooseFromOneChoice");
203             }
204 
205             if (SkImageDecoder::kDecodeBounds_Mode == mode) {
206                 bm->setConfig(SkBitmap::kIndex8_Config, width, height);
207                 return true;
208             }
209 #ifdef SK_BUILD_FOR_ANDROID
210             // No Bitmap reuse supported for this format
211             if (!bm->isNull()) {
212                 return false;
213             }
214 #endif
215 
216             bm->setConfig(SkBitmap::kIndex8_Config, width, height);
217             SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
218             const GifImageDesc& desc = image->ImageDesc;
219 
220             // check for valid descriptor
221             if (   (desc.Top | desc.Left) < 0 ||
222                     desc.Left + desc.Width > width ||
223                     desc.Top + desc.Height > height) {
224                 return error_return(gif, *bm, "TopLeft");
225             }
226 
227             // now we decode the colortable
228             int colorCount = 0;
229             {
230                 const ColorMapObject* cmap = find_colormap(gif);
231                 if (NULL == cmap) {
232                     return error_return(gif, *bm, "null cmap");
233                 }
234 
235                 colorCount = cmap->ColorCount;
236                 SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount));
237                 SkPMColor* colorPtr = ctable->lockColors();
238                 for (int index = 0; index < colorCount; index++)
239                     colorPtr[index] = SkPackARGB32(0xFF,
240                                                    cmap->Colors[index].Red,
241                                                    cmap->Colors[index].Green,
242                                                    cmap->Colors[index].Blue);
243 
244                 transpIndex = find_transpIndex(temp_save, colorCount);
245                 if (transpIndex < 0)
246                     ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
247                 else
248                     colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor
249                 ctable->unlockColors(true);
250 
251                 SkAutoUnref aurts(ctable);
252                 if (!this->allocPixelRef(bm, ctable)) {
253                     return error_return(gif, *bm, "allocPixelRef");
254                 }
255             }
256 
257             SkAutoLockPixels alp(*bm);
258 
259             // time to decode the scanlines
260             //
261             uint8_t*  scanline = bm->getAddr8(0, 0);
262             const int rowBytes = bm->rowBytes();
263             const int innerWidth = desc.Width;
264             const int innerHeight = desc.Height;
265 
266             // abort if either inner dimension is <= 0
267             if (innerWidth <= 0 || innerHeight <= 0) {
268                 return error_return(gif, *bm, "non-pos inner width/height");
269             }
270 
271             // are we only a subset of the total bounds?
272             if ((desc.Top | desc.Left) > 0 ||
273                  innerWidth < width || innerHeight < height)
274             {
275                 int fill;
276                 if (transpIndex >= 0) {
277                     fill = transpIndex;
278                 } else {
279                     fill = gif->SBackGroundColor;
280                 }
281                 // check for valid fill index/color
282                 if (static_cast<unsigned>(fill) >=
283                         static_cast<unsigned>(colorCount)) {
284                     fill = 0;
285                 }
286                 memset(scanline, fill, bm->getSize());
287                 // bump our starting address
288                 scanline += desc.Top * rowBytes + desc.Left;
289             }
290 
291             // now decode each scanline
292             if (gif->Image.Interlace)
293             {
294                 GifInterlaceIter iter(innerHeight);
295                 for (int y = 0; y < innerHeight; y++)
296                 {
297                     uint8_t* row = scanline + iter.currY() * rowBytes;
298                     if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) {
299                         return error_return(gif, *bm, "interlace DGifGetLine");
300                     }
301                     iter.next();
302                 }
303             }
304             else
305             {
306                 // easy, non-interlace case
307                 for (int y = 0; y < innerHeight; y++) {
308                     if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
309                         return error_return(gif, *bm, "DGifGetLine");
310                     }
311                     scanline += rowBytes;
312                 }
313             }
314             goto DONE;
315             } break;
316 
317         case EXTENSION_RECORD_TYPE:
318 #if GIFLIB_MAJOR < 5
319             if (DGifGetExtension(gif, &temp_save.Function,
320                                  &extData) == GIF_ERROR) {
321 #else
322             if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
323 #endif
324                 return error_return(gif, *bm, "DGifGetExtension");
325             }
326 
327             while (extData != NULL) {
328                 /* Create an extension block with our data */
329 #if GIFLIB_MAJOR < 5
330                 if (AddExtensionBlock(&temp_save, extData[0],
331                                       &extData[1]) == GIF_ERROR) {
332 #else
333                 if (GifAddExtensionBlock(&gif->ExtensionBlockCount,
334                                          &gif->ExtensionBlocks,
335                                          extFunction,
336                                          extData[0],
337                                          &extData[1]) == GIF_ERROR) {
338 #endif
339                     return error_return(gif, *bm, "AddExtensionBlock");
340                 }
341                 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
342                     return error_return(gif, *bm, "DGifGetExtensionNext");
343                 }
344 #if GIFLIB_MAJOR < 5
345                 temp_save.Function = 0;
346 #endif
347             }
348             break;
349 
350         case TERMINATE_RECORD_TYPE:
351             break;
352 
353         default:	/* Should be trapped by DGifGetRecordType */
354             break;
355         }
356     } while (recType != TERMINATE_RECORD_TYPE);
357 
358 DONE:
359     return true;
360 }
361 
362 ///////////////////////////////////////////////////////////////////////////////
363 DEFINE_DECODER_CREATOR(GIFImageDecoder);
364 ///////////////////////////////////////////////////////////////////////////////
365 
366 #include "SkTRegistry.h"
367 
368 static SkImageDecoder* sk_libgif_dfactory(SkStream* stream) {
369     char buf[GIF_STAMP_LEN];
370     if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
371         if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
372                 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
373                 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
374             return SkNEW(SkGIFImageDecoder);
375         }
376     }
377     return NULL;
378 }
379 
380 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libgif_dfactory);
381