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 "SkMovie.h"
19 #include "SkColor.h"
20 #include "SkColorPriv.h"
21 #include "SkStream.h"
22 #include "SkTemplates.h"
23
24 #include "gif_lib.h"
25
26 class SkGIFMovie : public SkMovie {
27 public:
28 SkGIFMovie(SkStream* stream);
29 virtual ~SkGIFMovie();
30
31 protected:
32 virtual bool onGetInfo(Info*);
33 virtual bool onSetTime(SkMSec);
34 virtual bool onGetBitmap(SkBitmap*);
35
36 private:
37 GifFileType* fGIF;
38 SavedImage* fCurrSavedImage;
39 };
40
Decode(GifFileType * fileType,GifByteType * out,int size)41 static int Decode(GifFileType* fileType, GifByteType* out, int size) {
42 SkStream* stream = (SkStream*) fileType->UserData;
43 return (int) stream->read(out, size);
44 }
45
SkGIFMovie(SkStream * stream)46 SkGIFMovie::SkGIFMovie(SkStream* stream)
47 {
48 fGIF = DGifOpen( stream, Decode );
49 if (NULL == fGIF)
50 return;
51
52 if (DGifSlurp(fGIF) != GIF_OK)
53 {
54 DGifCloseFile(fGIF);
55 fGIF = NULL;
56 }
57 fCurrSavedImage = NULL;
58 }
59
~SkGIFMovie()60 SkGIFMovie::~SkGIFMovie()
61 {
62 if (fGIF)
63 DGifCloseFile(fGIF);
64 }
65
savedimage_duration(const SavedImage * image)66 static SkMSec savedimage_duration(const SavedImage* image)
67 {
68 for (int j = 0; j < image->ExtensionBlockCount; j++)
69 {
70 if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
71 {
72 int size = image->ExtensionBlocks[j].ByteCount;
73 SkASSERT(size >= 4);
74 const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
75 return ((b[2] << 8) | b[1]) * 10;
76 }
77 }
78 return 0;
79 }
80
onGetInfo(Info * info)81 bool SkGIFMovie::onGetInfo(Info* info)
82 {
83 if (NULL == fGIF)
84 return false;
85
86 SkMSec dur = 0;
87 for (int i = 0; i < fGIF->ImageCount; i++)
88 dur += savedimage_duration(&fGIF->SavedImages[i]);
89
90 info->fDuration = dur;
91 info->fWidth = fGIF->SWidth;
92 info->fHeight = fGIF->SHeight;
93 info->fIsOpaque = false; // how to compute?
94 return true;
95 }
96
onSetTime(SkMSec time)97 bool SkGIFMovie::onSetTime(SkMSec time)
98 {
99 if (NULL == fGIF)
100 return false;
101
102 SkMSec dur = 0;
103 for (int i = 0; i < fGIF->ImageCount; i++)
104 {
105 dur += savedimage_duration(&fGIF->SavedImages[i]);
106 if (dur >= time)
107 {
108 SavedImage* prev = fCurrSavedImage;
109 fCurrSavedImage = &fGIF->SavedImages[i];
110 return prev != fCurrSavedImage;
111 }
112 }
113 fCurrSavedImage = &fGIF->SavedImages[fGIF->ImageCount - 1];
114 return true;
115 }
116
onGetBitmap(SkBitmap * bm)117 bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
118 {
119 GifFileType* gif = fGIF;
120 if (NULL == gif)
121 return false;
122
123 // should we check for the Image cmap or the global (SColorMap) first?
124 ColorMapObject* cmap = gif->SColorMap;
125 if (cmap == NULL)
126 cmap = gif->Image.ColorMap;
127
128 if (cmap == NULL || gif->ImageCount < 1 || cmap->ColorCount != (1 << cmap->BitsPerPixel))
129 {
130 SkASSERT(!"bad colortable setup");
131 return false;
132 }
133
134 const int width = gif->SWidth;
135 const int height = gif->SHeight;
136 if (width <= 0 || height <= 0) {
137 return false;
138 }
139
140 SavedImage* gif_image = fCurrSavedImage;
141 SkBitmap::Config config = SkBitmap::kIndex8_Config;
142
143 SkColorTable* colorTable = SkNEW_ARGS(SkColorTable, (cmap->ColorCount));
144 SkAutoUnref aur(colorTable);
145
146 bm->setConfig(config, width, height, 0);
147 if (!bm->allocPixels(colorTable)) {
148 return false;
149 }
150
151 int transparent = -1;
152 for (int i = 0; i < gif_image->ExtensionBlockCount; ++i) {
153 ExtensionBlock* eb = gif_image->ExtensionBlocks + i;
154 if (eb->Function == 0xF9 &&
155 eb->ByteCount == 4) {
156 bool has_transparency = ((eb->Bytes[0] & 1) == 1);
157 if (has_transparency) {
158 transparent = (unsigned char)eb->Bytes[3];
159 }
160 }
161 }
162
163 SkPMColor* colorPtr = colorTable->lockColors();
164
165 if (transparent >= 0)
166 memset(colorPtr, 0, cmap->ColorCount * 4);
167 else
168 colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
169
170 for (int index = 0; index < cmap->ColorCount; index++)
171 {
172 if (transparent != index)
173 colorPtr[index] = SkPackARGB32(0xFF, cmap->Colors[index].Red,
174 cmap->Colors[index].Green, cmap->Colors[index].Blue);
175 }
176 colorTable->unlockColors(true);
177
178 unsigned char* in = (unsigned char*)gif_image->RasterBits;
179 unsigned char* out = bm->getAddr8(0, 0);
180 if (gif->Image.Interlace) {
181
182 // deinterlace
183 int row;
184 // group 1 - every 8th row, starting with row 0
185 for (row = 0; row < height; row += 8) {
186 memcpy(out + width * row, in, width);
187 in += width;
188 }
189
190 // group 2 - every 8th row, starting with row 4
191 for (row = 4; row < height; row += 8) {
192 memcpy(out + width * row, in, width);
193 in += width;
194 }
195
196 // group 3 - every 4th row, starting with row 2
197 for (row = 2; row < height; row += 4) {
198 memcpy(out + width * row, in, width);
199 in += width;
200 }
201
202 for (row = 1; row < height; row += 2) {
203 memcpy(out + width * row, in, width);
204 in += width;
205 }
206
207 } else {
208 memcpy(out, in, width * height);
209 }
210 return true;
211 }
212
213 ///////////////////////////////////////////////////////////////////////////////
214
215 #include "SkTRegistry.h"
216
Factory(SkStream * stream)217 SkMovie* Factory(SkStream* stream) {
218 char buf[GIF_STAMP_LEN];
219 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
220 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 ||
221 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
222 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
223 // must rewind here, since our construct wants to re-read the data
224 stream->rewind();
225 return SkNEW_ARGS(SkGIFMovie, (stream));
226 }
227 }
228 return NULL;
229 }
230
231 static SkTRegistry<SkMovie*, SkStream*> gReg(Factory);
232