SkImageDecoder_libgif.cpp revision 8552a21d334cbc7971b109f27d05aea9d7690e92
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,
35                          SkBitmap::Config pref, Mode mode);
36};
37
38static const uint8_t gStartingIterlaceYValue[] = {
39    0, 4, 2, 1
40};
41static const uint8_t gDeltaIterlaceYValue[] = {
42    8, 8, 4, 2
43};
44
45/*  Implement the GIF interlace algorithm in an iterator.
46    1) grab every 8th line beginning at 0
47    2) grab every 8th line beginning at 4
48    3) grab every 4th line beginning at 2
49    4) grab every 2nd line beginning at 1
50*/
51class GifInterlaceIter {
52public:
53    GifInterlaceIter(int height) : fHeight(height) {
54        fStartYPtr = gStartingIterlaceYValue;
55        fDeltaYPtr = gDeltaIterlaceYValue;
56
57        fCurrY = *fStartYPtr++;
58        fDeltaY = *fDeltaYPtr++;
59    }
60
61    int currY() const {
62        SkASSERT(fStartYPtr);
63        SkASSERT(fDeltaYPtr);
64        return fCurrY;
65    }
66
67    void next() {
68        SkASSERT(fStartYPtr);
69        SkASSERT(fDeltaYPtr);
70
71        int y = fCurrY + fDeltaY;
72        // We went from an if statement to a while loop so that we iterate
73        // through fStartYPtr until a valid row is found. This is so that images
74        // that are smaller than 5x5 will not trash memory.
75        while (y >= fHeight) {
76            if (gStartingIterlaceYValue +
77                    SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
78                // we done
79                SkDEBUGCODE(fStartYPtr = NULL;)
80                SkDEBUGCODE(fDeltaYPtr = NULL;)
81                y = 0;
82            } else {
83                y = *fStartYPtr++;
84                fDeltaY = *fDeltaYPtr++;
85            }
86        }
87        fCurrY = y;
88    }
89
90private:
91    const int fHeight;
92    int fCurrY;
93    int fDeltaY;
94    const uint8_t* fStartYPtr;
95    const uint8_t* fDeltaYPtr;
96};
97
98///////////////////////////////////////////////////////////////////////////////
99
100//#define GIF_STAMP       "GIF"    /* First chars in file - GIF stamp. */
101//#define GIF_STAMP_LEN   (sizeof(GIF_STAMP) - 1)
102
103static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
104                              int size) {
105    SkStream* stream = (SkStream*) fileType->UserData;
106    return (int) stream->read(out, size);
107}
108
109void CheckFreeExtension(SavedImage* Image) {
110    if (Image->ExtensionBlocks) {
111        FreeExtension(Image);
112    }
113}
114
115// return NULL on failure
116static const ColorMapObject* find_colormap(const GifFileType* gif) {
117    const ColorMapObject* cmap = gif->Image.ColorMap;
118    if (NULL == cmap) {
119        cmap = gif->SColorMap;
120    }
121    // some sanity checks
122    if ((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)
130static 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
148static 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
157bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm,
158                                 SkBitmap::Config prefConfig, Mode mode) {
159    GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
160    if (NULL == gif) {
161        return error_return(gif, *bm, "DGifOpen");
162    }
163
164    SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);
165
166    SavedImage temp_save;
167    temp_save.ExtensionBlocks=NULL;
168    temp_save.ExtensionBlockCount=0;
169    SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);
170
171    int width, height;
172    GifRecordType recType;
173    GifByteType *extData;
174
175    do {
176        if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
177            return error_return(gif, *bm, "DGifGetRecordType");
178        }
179
180        switch (recType) {
181        case IMAGE_DESC_RECORD_TYPE: {
182            if (DGifGetImageDesc(gif) == GIF_ERROR) {
183                return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE");
184            }
185
186            if (gif->ImageCount < 1) {    // sanity check
187                return error_return(gif, *bm, "ImageCount < 1");
188            }
189
190            width = gif->SWidth;
191            height = gif->SHeight;
192            if (width <= 0 || height <= 0 ||
193                !this->chooseFromOneChoice(SkBitmap::kIndex8_Config,
194                                           width, height)) {
195                return error_return(gif, *bm, "chooseFromOneChoice");
196            }
197
198            bm->setConfig(SkBitmap::kIndex8_Config, width, height);
199            if (SkImageDecoder::kDecodeBounds_Mode == mode)
200                return true;
201
202            SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
203            const GifImageDesc& desc = image->ImageDesc;
204
205            // check for valid descriptor
206            if (   (desc.Top | desc.Left) < 0 ||
207                    desc.Left + desc.Width > width ||
208                    desc.Top + desc.Height > height) {
209                return error_return(gif, *bm, "TopLeft");
210            }
211
212            // now we decode the colortable
213            int colorCount = 0;
214            {
215                const ColorMapObject* cmap = find_colormap(gif);
216                if (NULL == cmap) {
217                    return error_return(gif, *bm, "null cmap");
218                }
219
220                colorCount = cmap->ColorCount;
221                SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount));
222                SkPMColor* colorPtr = ctable->lockColors();
223                for (int index = 0; index < colorCount; index++)
224                    colorPtr[index] = SkPackARGB32(0xFF,
225                                                   cmap->Colors[index].Red,
226                                                   cmap->Colors[index].Green,
227                                                   cmap->Colors[index].Blue);
228
229                int transpIndex = find_transpIndex(temp_save, colorCount);
230                if (transpIndex < 0)
231                    ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
232                else
233                    colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor
234                ctable->unlockColors(true);
235
236                SkAutoUnref aurts(ctable);
237                if (!this->allocPixelRef(bm, ctable)) {
238                    return error_return(gif, *bm, "allocPixelRef");
239                }
240            }
241
242            SkAutoLockPixels alp(*bm);
243
244            // time to decode the scanlines
245            //
246            uint8_t*  scanline = bm->getAddr8(0, 0);
247            const int rowBytes = bm->rowBytes();
248            const int innerWidth = desc.Width;
249            const int innerHeight = desc.Height;
250
251            // abort if either inner dimension is <= 0
252            if (innerWidth <= 0 || innerHeight <= 0) {
253                return error_return(gif, *bm, "non-pos inner width/height");
254            }
255
256            // are we only a subset of the total bounds?
257            if ((desc.Top | desc.Left) > 0 ||
258                 innerWidth < width || innerHeight < height)
259            {
260                uint8_t fill = (uint8_t)gif->SBackGroundColor;
261                // check for valid fill index/color
262                if (fill >= (unsigned)colorCount) {
263                    fill = 0;
264                }
265                memset(scanline, gif->SBackGroundColor, bm->getSize());
266                // bump our starting address
267                scanline += desc.Top * rowBytes + desc.Left;
268            }
269
270            // now decode each scanline
271            if (gif->Image.Interlace)
272            {
273                GifInterlaceIter iter(innerHeight);
274                for (int y = 0; y < innerHeight; y++)
275                {
276                    uint8_t* row = scanline + iter.currY() * rowBytes;
277                    if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) {
278                        return error_return(gif, *bm, "interlace DGifGetLine");
279                    }
280                    iter.next();
281                }
282            }
283            else
284            {
285                // easy, non-interlace case
286                for (int y = 0; y < innerHeight; y++) {
287                    if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
288                        return error_return(gif, *bm, "DGifGetLine");
289                    }
290                    scanline += rowBytes;
291                }
292            }
293            goto DONE;
294            } break;
295
296        case EXTENSION_RECORD_TYPE:
297            if (DGifGetExtension(gif, &temp_save.Function,
298                                 &extData) == GIF_ERROR) {
299                return error_return(gif, *bm, "DGifGetExtension");
300            }
301
302            while (extData != NULL) {
303                /* Create an extension block with our data */
304                if (AddExtensionBlock(&temp_save, extData[0],
305                                      &extData[1]) == GIF_ERROR) {
306                    return error_return(gif, *bm, "AddExtensionBlock");
307                }
308                if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
309                    return error_return(gif, *bm, "DGifGetExtensionNext");
310                }
311                temp_save.Function = 0;
312            }
313            break;
314
315        case TERMINATE_RECORD_TYPE:
316            break;
317
318        default:	/* Should be trapped by DGifGetRecordType */
319            break;
320        }
321    } while (recType != TERMINATE_RECORD_TYPE);
322
323DONE:
324    return true;
325}
326
327///////////////////////////////////////////////////////////////////////////////
328
329#include "SkTRegistry.h"
330
331static SkImageDecoder* Factory(SkStream* stream) {
332    char buf[GIF_STAMP_LEN];
333    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
334        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
335                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
336                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
337            return SkNEW(SkGIFImageDecoder);
338        }
339    }
340    return NULL;
341}
342
343static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
344