OLD | NEW |
| (Empty) |
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 | |
27 class SkGIFImageDecoder : public SkImageDecoder { | |
28 public: | |
29 virtual Format getFormat() const { | |
30 return kGIF_Format; | |
31 } | |
32 | |
33 protected: | |
34 virtual bool onDecode(SkStream* stream, SkBitmap* bm, | |
35 SkBitmap::Config pref, Mode mode); | |
36 }; | |
37 | |
38 static const uint8_t gStartingIterlaceYValue[] = { | |
39 0, 4, 2, 1 | |
40 }; | |
41 static 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 */ | |
51 class GifInterlaceIter { | |
52 public: | |
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 | |
90 private: | |
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 | |
103 static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out, | |
104 int size) { | |
105 SkStream* stream = (SkStream*) fileType->UserData; | |
106 return (int) stream->read(out, size); | |
107 } | |
108 | |
109 void CheckFreeExtension(SavedImage* Image) { | |
110 if (Image->ExtensionBlocks) { | |
111 FreeExtension(Image); | |
112 } | |
113 } | |
114 | |
115 // return NULL on failure | |
116 static const ColorMapObject* find_colormap(const GifFileType* gif) { | |
117 const ColorMapObject* cmap = gif->SColorMap; | |
118 if (NULL == cmap) { | |
119 cmap = gif->Image.ColorMap; | |
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) | |
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 | |
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 | |
157 bool 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::kColorsA
reOpaque_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 | |
323 DONE: | |
324 return true; | |
325 } | |
326 | |
327 /////////////////////////////////////////////////////////////////////////////// | |
328 | |
329 SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream* stream) { | |
330 char buf[GIF_STAMP_LEN]; | |
331 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { | |
332 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || | |
333 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || | |
334 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { | |
335 return SkNEW(SkGIFImageDecoder); | |
336 } | |
337 } | |
338 return NULL; | |
339 } | |
340 | |
OLD | NEW |