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