OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2006 The Android Open Source Project | 2 * Copyright 2006 The Android Open Source Project |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 | |
9 #include "SkColor.h" | 8 #include "SkColor.h" |
10 #include "SkColorPriv.h" | 9 #include "SkColorPriv.h" |
11 #include "SkColorTable.h" | 10 #include "SkColorTable.h" |
12 #include "SkImageDecoder.h" | 11 #include "SkImageDecoder.h" |
| 12 #include "SkRTConf.h" |
13 #include "SkScaledBitmapSampler.h" | 13 #include "SkScaledBitmapSampler.h" |
14 #include "SkStream.h" | 14 #include "SkStream.h" |
15 #include "SkTemplates.h" | 15 #include "SkTemplates.h" |
| 16 #include "SkUtils.h" |
16 | 17 |
17 #include "gif_lib.h" | 18 #include "gif_lib.h" |
18 | 19 |
19 class SkGIFImageDecoder : public SkImageDecoder { | 20 class SkGIFImageDecoder : public SkImageDecoder { |
20 public: | 21 public: |
21 virtual Format getFormat() const SK_OVERRIDE { | 22 virtual Format getFormat() const SK_OVERRIDE { |
22 return kGIF_Format; | 23 return kGIF_Format; |
23 } | 24 } |
24 | 25 |
25 protected: | 26 protected: |
26 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE
; | 27 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE
; |
27 | 28 |
28 private: | 29 private: |
29 typedef SkImageDecoder INHERITED; | 30 typedef SkImageDecoder INHERITED; |
30 }; | 31 }; |
31 | 32 |
32 static const uint8_t gStartingIterlaceYValue[] = { | 33 static const uint8_t gStartingIterlaceYValue[] = { |
33 0, 4, 2, 1 | 34 0, 4, 2, 1 |
34 }; | 35 }; |
35 static const uint8_t gDeltaIterlaceYValue[] = { | 36 static const uint8_t gDeltaIterlaceYValue[] = { |
36 8, 8, 4, 2 | 37 8, 8, 4, 2 |
37 }; | 38 }; |
38 | 39 |
| 40 SK_CONF_DECLARE(bool, c_suppressGIFImageDecoderWarnings, |
| 41 "images.gif.suppressDecoderWarnings", true, |
| 42 "Suppress GIF warnings and errors when calling image decode " |
| 43 "functions."); |
| 44 |
| 45 |
39 /* Implement the GIF interlace algorithm in an iterator. | 46 /* Implement the GIF interlace algorithm in an iterator. |
40 1) grab every 8th line beginning at 0 | 47 1) grab every 8th line beginning at 0 |
41 2) grab every 8th line beginning at 4 | 48 2) grab every 8th line beginning at 4 |
42 3) grab every 4th line beginning at 2 | 49 3) grab every 4th line beginning at 2 |
43 4) grab every 2nd line beginning at 1 | 50 4) grab every 2nd line beginning at 1 |
44 */ | 51 */ |
45 class GifInterlaceIter { | 52 class GifInterlaceIter { |
46 public: | 53 public: |
47 GifInterlaceIter(int height) : fHeight(height) { | 54 GifInterlaceIter(int height) : fHeight(height) { |
48 fStartYPtr = gStartingIterlaceYValue; | 55 fStartYPtr = gStartingIterlaceYValue; |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
138 if (transpIndex >= colorCount) { | 145 if (transpIndex >= colorCount) { |
139 transpIndex = -1; | 146 transpIndex = -1; |
140 } | 147 } |
141 break; | 148 break; |
142 } | 149 } |
143 } | 150 } |
144 } | 151 } |
145 return transpIndex; | 152 return transpIndex; |
146 } | 153 } |
147 | 154 |
148 static bool error_return(GifFileType* gif, const SkBitmap& bm, | 155 static bool error_return(const SkBitmap& bm, const char msg[]) { |
149 const char msg[]) { | 156 if (!c_suppressGIFImageDecoderWarnings) { |
150 #if 0 | 157 SkDebugf("libgif error [%s] bitmap [%d %d] pixels %p colortable %p\n", |
151 SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n", | 158 msg, bm.width(), bm.height(), bm.getPixels(), |
152 msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable()); | 159 bm.getColorTable()); |
153 #endif | 160 } |
154 return false; | 161 return false; |
155 } | 162 } |
| 163 static void gif_warning(const SkBitmap& bm, const char msg[]) { |
| 164 if (!c_suppressGIFImageDecoderWarnings) { |
| 165 SkDebugf("libgif warning [%s] bitmap [%d %d] pixels %p colortable %p\n", |
| 166 msg, bm.width(), bm.height(), bm.getPixels(), |
| 167 bm.getColorTable()); |
| 168 } |
| 169 } |
156 | 170 |
157 /** | 171 /** |
158 * Skip rows in the source gif image. | 172 * Skip rows in the source gif image. |
159 * @param gif Source image. | 173 * @param gif Source image. |
160 * @param dst Scratch output needed by gif library call. Must be >= width bytes
. | 174 * @param dst Scratch output needed by gif library call. Must be >= width bytes
. |
161 * @param width Bytes per row in the source image. | 175 * @param width Bytes per row in the source image. |
162 * @param rowsToSkip Number of rows to skip. | 176 * @param rowsToSkip Number of rows to skip. |
163 * @return True on success, false on GIF_ERROR. | 177 * @return True on success, false on GIF_ERROR. |
164 */ | 178 */ |
165 static bool skip_src_rows(GifFileType* gif, uint8_t* dst, int width, int rowsToS
kip) { | 179 static bool skip_src_rows(GifFileType* gif, uint8_t* dst, int width, int rowsToS
kip) { |
166 for (int i = 0; i < rowsToSkip; i++) { | 180 for (int i = 0; i < rowsToSkip; i++) { |
167 if (DGifGetLine(gif, dst, width) == GIF_ERROR) { | 181 if (DGifGetLine(gif, dst, width) == GIF_ERROR) { |
168 return false; | 182 return false; |
169 } | 183 } |
170 } | 184 } |
171 return true; | 185 return true; |
172 } | 186 } |
173 | 187 |
174 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { | 188 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { |
175 #if GIFLIB_MAJOR < 5 | 189 #if GIFLIB_MAJOR < 5 |
176 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); | 190 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); |
177 #else | 191 #else |
178 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL); | 192 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL); |
179 #endif | 193 #endif |
180 if (NULL == gif) { | 194 if (NULL == gif) { |
181 return error_return(gif, *bm, "DGifOpen"); | 195 return error_return(*bm, "DGifOpen"); |
182 } | 196 } |
183 | 197 |
184 SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif); | 198 SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif); |
185 | 199 |
186 SavedImage temp_save; | 200 SavedImage temp_save; |
187 temp_save.ExtensionBlocks=NULL; | 201 temp_save.ExtensionBlocks=NULL; |
188 temp_save.ExtensionBlockCount=0; | 202 temp_save.ExtensionBlockCount=0; |
189 SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save); | 203 SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save); |
190 | 204 |
191 int width, height; | 205 int width, height; |
192 GifRecordType recType; | 206 GifRecordType recType; |
193 GifByteType *extData; | 207 GifByteType *extData; |
194 #if GIFLIB_MAJOR >= 5 | 208 #if GIFLIB_MAJOR >= 5 |
195 int extFunction; | 209 int extFunction; |
196 #endif | 210 #endif |
197 int transpIndex = -1; // -1 means we don't have it (yet) | 211 int transpIndex = -1; // -1 means we don't have it (yet) |
| 212 int fillIndex = gif->SBackGroundColor; |
198 | 213 |
199 do { | 214 do { |
200 if (DGifGetRecordType(gif, &recType) == GIF_ERROR) { | 215 if (DGifGetRecordType(gif, &recType) == GIF_ERROR) { |
201 return error_return(gif, *bm, "DGifGetRecordType"); | 216 return error_return(*bm, "DGifGetRecordType"); |
202 } | 217 } |
203 | 218 |
204 switch (recType) { | 219 switch (recType) { |
205 case IMAGE_DESC_RECORD_TYPE: { | 220 case IMAGE_DESC_RECORD_TYPE: { |
206 if (DGifGetImageDesc(gif) == GIF_ERROR) { | 221 if (DGifGetImageDesc(gif) == GIF_ERROR) { |
207 return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE"); | 222 return error_return(*bm, "IMAGE_DESC_RECORD_TYPE"); |
208 } | 223 } |
209 | 224 |
210 if (gif->ImageCount < 1) { // sanity check | 225 if (gif->ImageCount < 1) { // sanity check |
211 return error_return(gif, *bm, "ImageCount < 1"); | 226 return error_return(*bm, "ImageCount < 1"); |
212 } | 227 } |
213 | 228 |
214 width = gif->SWidth; | 229 width = gif->SWidth; |
215 height = gif->SHeight; | 230 height = gif->SHeight; |
216 if (width <= 0 || height <= 0) { | 231 |
217 return error_return(gif, *bm, "invalid dimensions"); | 232 SavedImage* image = &gif->SavedImages[gif->ImageCount-1]; |
| 233 const GifImageDesc& desc = image->ImageDesc; |
| 234 |
| 235 int imageLeft = desc.Left; |
| 236 int imageTop = desc.Top; |
| 237 const int innerWidth = desc.Width; |
| 238 const int innerHeight = desc.Height; |
| 239 if (innerWidth <= 0 || innerHeight <= 0) { |
| 240 return error_return(*bm, "invalid dimensions"); |
| 241 } |
| 242 |
| 243 // check for valid descriptor |
| 244 if (innerWidth > width) { |
| 245 gif_warning(*bm, "image too wide, expanding output to size"); |
| 246 width = innerWidth; |
| 247 imageLeft = 0; |
| 248 } else if (imageLeft + innerWidth > width) { |
| 249 gif_warning(*bm, "shifting image left to fit"); |
| 250 imageLeft = width - innerWidth; |
| 251 } else if (imageLeft < 0) { |
| 252 gif_warning(*bm, "shifting image right to fit"); |
| 253 imageLeft = 0; |
| 254 } |
| 255 |
| 256 |
| 257 if (innerHeight > height) { |
| 258 gif_warning(*bm, "image too tall, expanding output to size"); |
| 259 height = innerHeight; |
| 260 imageTop = 0; |
| 261 } else if (imageTop + innerHeight > height) { |
| 262 gif_warning(*bm, "shifting image up to fit"); |
| 263 imageTop = height - innerHeight; |
| 264 } else if (imageTop < 0) { |
| 265 gif_warning(*bm, "shifting image down to fit"); |
| 266 imageTop = 0; |
218 } | 267 } |
219 | 268 |
220 // FIXME: We could give the caller a choice of images or configs. | 269 // FIXME: We could give the caller a choice of images or configs. |
221 if (!this->chooseFromOneChoice(SkBitmap::kIndex8_Config, width, heig
ht)) { | 270 if (!this->chooseFromOneChoice(SkBitmap::kIndex8_Config, width, heig
ht)) { |
222 return error_return(gif, *bm, "chooseFromOneChoice"); | 271 return error_return(*bm, "chooseFromOneChoice"); |
223 } | 272 } |
224 | 273 |
225 SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); | 274 SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); |
226 | 275 |
227 bm->setConfig(SkBitmap::kIndex8_Config, sampler.scaledWidth(), | 276 bm->setConfig(SkBitmap::kIndex8_Config, sampler.scaledWidth(), |
228 sampler.scaledHeight()); | 277 sampler.scaledHeight()); |
229 | 278 |
230 if (SkImageDecoder::kDecodeBounds_Mode == mode) { | 279 if (SkImageDecoder::kDecodeBounds_Mode == mode) { |
231 return true; | 280 return true; |
232 } | 281 } |
233 | 282 |
234 SavedImage* image = &gif->SavedImages[gif->ImageCount-1]; | |
235 const GifImageDesc& desc = image->ImageDesc; | |
236 | |
237 // check for valid descriptor | |
238 if ( (desc.Top | desc.Left) < 0 || | |
239 desc.Left + desc.Width > width || | |
240 desc.Top + desc.Height > height) { | |
241 return error_return(gif, *bm, "TopLeft"); | |
242 } | |
243 | 283 |
244 // now we decode the colortable | 284 // now we decode the colortable |
245 int colorCount = 0; | 285 int colorCount = 0; |
246 { | 286 { |
| 287 // Declare colorPtr here for scope. |
| 288 SkPMColor colorPtr[256]; // storage for worst-case |
247 const ColorMapObject* cmap = find_colormap(gif); | 289 const ColorMapObject* cmap = find_colormap(gif); |
248 if (NULL == cmap) { | 290 SkAlphaType alphaType = kOpaque_SkAlphaType; |
249 return error_return(gif, *bm, "null cmap"); | 291 if (cmap != NULL) { |
| 292 colorCount = cmap->ColorCount; |
| 293 if (colorCount > 256) { |
| 294 colorCount = 256; // our kIndex8 can't support more |
| 295 } |
| 296 for (int index = 0; index < colorCount; index++) { |
| 297 colorPtr[index] = SkPackARGB32(0xFF, |
| 298 cmap->Colors[index].Red, |
| 299 cmap->Colors[index].Green
, |
| 300 cmap->Colors[index].Blue)
; |
| 301 } |
| 302 } else { |
| 303 // find_colormap() returned NULL. Some (rare, broken) |
| 304 // GIFs don't have a color table, so we force one. |
| 305 gif_warning(*bm, "missing colormap"); |
| 306 colorCount = 256; |
| 307 sk_memset32(colorPtr, SK_ColorWHITE, colorCount); |
250 } | 308 } |
251 colorCount = cmap->ColorCount; | |
252 if (colorCount > 256) { | |
253 colorCount = 256; // our kIndex8 can't support more | |
254 } | |
255 | |
256 SkPMColor colorPtr[256]; // storage for worst-case | |
257 SkAlphaType alphaType = kOpaque_SkAlphaType; | |
258 for (int index = 0; index < colorCount; index++) { | |
259 colorPtr[index] = SkPackARGB32(0xFF, | |
260 cmap->Colors[index].Red, | |
261 cmap->Colors[index].Green, | |
262 cmap->Colors[index].Blue); | |
263 } | |
264 | |
265 transpIndex = find_transpIndex(temp_save, colorCount); | 309 transpIndex = find_transpIndex(temp_save, colorCount); |
266 bool reallyHasAlpha = transpIndex >= 0; | 310 if (transpIndex >= 0) { |
267 if (reallyHasAlpha) { | |
268 colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a tra
nsparent SkPMColor | 311 colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a tra
nsparent SkPMColor |
269 alphaType = kPremul_SkAlphaType; | 312 alphaType = kPremul_SkAlphaType; |
| 313 fillIndex = transpIndex; |
| 314 } else if (fillIndex >= colorCount) { |
| 315 // gif->SBackGroundColor should be less than colorCount. |
| 316 fillIndex = 0; // If not, fix it. |
270 } | 317 } |
271 | 318 |
272 SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable, | 319 SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable, |
273 (colorPtr, colorCount, | 320 (colorPtr, colorCount, |
274 alphaType))); | 321 alphaType))); |
275 if (!this->allocPixelRef(bm, ctable)) { | 322 if (!this->allocPixelRef(bm, ctable)) { |
276 return error_return(gif, *bm, "allocPixelRef"); | 323 return error_return(*bm, "allocPixelRef"); |
277 } | 324 } |
278 } | 325 } |
279 | 326 |
280 const int innerWidth = desc.Width; | |
281 const int innerHeight = desc.Height; | |
282 | |
283 // abort if either inner dimension is <= 0 | 327 // abort if either inner dimension is <= 0 |
284 if (innerWidth <= 0 || innerHeight <= 0) { | 328 if (innerWidth <= 0 || innerHeight <= 0) { |
285 return error_return(gif, *bm, "non-pos inner width/height"); | 329 return error_return(*bm, "non-pos inner width/height"); |
286 } | 330 } |
287 | 331 |
288 SkAutoLockPixels alp(*bm); | 332 SkAutoLockPixels alp(*bm); |
289 | 333 |
290 SkAutoMalloc storage(innerWidth); | 334 SkAutoMalloc storage(innerWidth); |
291 uint8_t* scanline = (uint8_t*) storage.get(); | 335 uint8_t* scanline = (uint8_t*) storage.get(); |
292 | 336 |
293 // GIF has an option to store the scanlines of an image, plus a larg
er background, | 337 // GIF has an option to store the scanlines of an image, plus a larg
er background, |
294 // filled by a fill color. In this case, we will use a subset of the
larger bitmap | 338 // filled by a fill color. In this case, we will use a subset of the
larger bitmap |
295 // for sampling. | 339 // for sampling. |
296 SkBitmap subset; | 340 SkBitmap subset; |
297 SkBitmap* workingBitmap; | 341 SkBitmap* workingBitmap; |
298 // are we only a subset of the total bounds? | 342 // are we only a subset of the total bounds? |
299 if ((desc.Top | desc.Left) > 0 || | 343 if ((imageTop | imageLeft) > 0 || |
300 innerWidth < width || innerHeight < height) { | 344 innerWidth < width || innerHeight < height) { |
301 int fill; | |
302 if (transpIndex >= 0) { | |
303 fill = transpIndex; | |
304 } else { | |
305 fill = gif->SBackGroundColor; | |
306 } | |
307 // check for valid fill index/color | |
308 if (static_cast<unsigned>(fill) >= | |
309 static_cast<unsigned>(colorCount)) { | |
310 fill = 0; | |
311 } | |
312 // Fill the background. | 345 // Fill the background. |
313 memset(bm->getPixels(), fill, bm->getSize()); | 346 memset(bm->getPixels(), fillIndex, bm->getSize()); |
314 | 347 |
315 // Create a subset of the bitmap. | 348 // Create a subset of the bitmap. |
316 SkIRect subsetRect(SkIRect::MakeXYWH(desc.Left / sampler.srcDX()
, | 349 SkIRect subsetRect(SkIRect::MakeXYWH(imageLeft / sampler.srcDX()
, |
317 desc.Top / sampler.srcDY(), | 350 imageTop / sampler.srcDY(), |
318 innerWidth / sampler.srcDX(
), | 351 innerWidth / sampler.srcDX(
), |
319 innerHeight / sampler.srcDY
())); | 352 innerHeight / sampler.srcDY
())); |
320 if (!bm->extractSubset(&subset, subsetRect)) { | 353 if (!bm->extractSubset(&subset, subsetRect)) { |
321 return error_return(gif, *bm, "Extract failed."); | 354 return error_return(*bm, "Extract failed."); |
322 } | 355 } |
323 // Update the sampler. We'll now be only sampling into the subse
t. | 356 // Update the sampler. We'll now be only sampling into the subse
t. |
324 sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->g
etSampleSize()); | 357 sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->g
etSampleSize()); |
325 workingBitmap = ⊂ | 358 workingBitmap = ⊂ |
326 } else { | 359 } else { |
327 workingBitmap = bm; | 360 workingBitmap = bm; |
328 } | 361 } |
329 | 362 |
330 // bm is already locked, but if we had to take a subset, it must be
locked also, | 363 // bm is already locked, but if we had to take a subset, it must be
locked also, |
331 // so that getPixels() will point to its pixels. | 364 // so that getPixels() will point to its pixels. |
332 SkAutoLockPixels alpWorking(*workingBitmap); | 365 SkAutoLockPixels alpWorking(*workingBitmap); |
333 | 366 |
334 if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *th
is)) { | 367 if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *th
is)) { |
335 return error_return(gif, *bm, "Sampler failed to begin."); | 368 return error_return(*bm, "Sampler failed to begin."); |
336 } | 369 } |
337 | 370 |
338 // now decode each scanline | 371 // now decode each scanline |
339 if (gif->Image.Interlace) { | 372 if (gif->Image.Interlace) { |
340 // Iterate over the height of the source data. The sampler will | 373 // Iterate over the height of the source data. The sampler will |
341 // take care of skipping unneeded rows. | 374 // take care of skipping unneeded rows. |
342 GifInterlaceIter iter(innerHeight); | 375 GifInterlaceIter iter(innerHeight); |
343 for (int y = 0; y < innerHeight; y++){ | 376 for (int y = 0; y < innerHeight; y++) { |
344 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { | 377 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { |
345 return error_return(gif, *bm, "interlace DGifGetLine"); | 378 gif_warning(*bm, "interlace DGifGetLine"); |
| 379 memset(scanline, fillIndex, innerWidth); |
| 380 for (; y < innerHeight; y++) { |
| 381 sampler.sampleInterlaced(scanline, iter.currY()); |
| 382 iter.next(); |
| 383 } |
| 384 return true; |
346 } | 385 } |
347 sampler.sampleInterlaced(scanline, iter.currY()); | 386 sampler.sampleInterlaced(scanline, iter.currY()); |
348 iter.next(); | 387 iter.next(); |
349 } | 388 } |
350 } else { | 389 } else { |
351 // easy, non-interlace case | 390 // easy, non-interlace case |
352 const int outHeight = workingBitmap->height(); | 391 const int outHeight = workingBitmap->height(); |
353 skip_src_rows(gif, scanline, innerWidth, sampler.srcY0()); | 392 skip_src_rows(gif, scanline, innerWidth, sampler.srcY0()); |
354 for (int y = 0; y < outHeight; y++) { | 393 for (int y = 0; y < outHeight; y++) { |
355 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { | 394 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { |
356 return error_return(gif, *bm, "DGifGetLine"); | 395 gif_warning(*bm, "DGifGetLine"); |
| 396 memset(scanline, fillIndex, innerWidth); |
| 397 for (; y < outHeight; y++) { |
| 398 sampler.next(scanline); |
| 399 } |
| 400 return true; |
357 } | 401 } |
358 // scanline now contains the raw data. Sample it. | 402 // scanline now contains the raw data. Sample it. |
359 sampler.next(scanline); | 403 sampler.next(scanline); |
360 if (y < outHeight - 1) { | 404 if (y < outHeight - 1) { |
361 skip_src_rows(gif, scanline, innerWidth, sampler.srcDY()
- 1); | 405 skip_src_rows(gif, scanline, innerWidth, sampler.srcDY()
- 1); |
362 } | 406 } |
363 } | 407 } |
364 // skip the rest of the rows (if any) | 408 // skip the rest of the rows (if any) |
365 int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() +
1; | 409 int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() +
1; |
366 SkASSERT(read <= innerHeight); | 410 SkASSERT(read <= innerHeight); |
367 skip_src_rows(gif, scanline, innerWidth, innerHeight - read); | 411 skip_src_rows(gif, scanline, innerWidth, innerHeight - read); |
368 } | 412 } |
369 goto DONE; | 413 return true; |
370 } break; | 414 } break; |
371 | 415 |
372 case EXTENSION_RECORD_TYPE: | 416 case EXTENSION_RECORD_TYPE: |
373 #if GIFLIB_MAJOR < 5 | 417 #if GIFLIB_MAJOR < 5 |
374 if (DGifGetExtension(gif, &temp_save.Function, | 418 if (DGifGetExtension(gif, &temp_save.Function, |
375 &extData) == GIF_ERROR) { | 419 &extData) == GIF_ERROR) { |
376 #else | 420 #else |
377 if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) { | 421 if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) { |
378 #endif | 422 #endif |
379 return error_return(gif, *bm, "DGifGetExtension"); | 423 return error_return(*bm, "DGifGetExtension"); |
380 } | 424 } |
381 | 425 |
382 while (extData != NULL) { | 426 while (extData != NULL) { |
383 /* Create an extension block with our data */ | 427 /* Create an extension block with our data */ |
384 #if GIFLIB_MAJOR < 5 | 428 #if GIFLIB_MAJOR < 5 |
385 if (AddExtensionBlock(&temp_save, extData[0], | 429 if (AddExtensionBlock(&temp_save, extData[0], |
386 &extData[1]) == GIF_ERROR) { | 430 &extData[1]) == GIF_ERROR) { |
387 #else | 431 #else |
388 if (GifAddExtensionBlock(&gif->ExtensionBlockCount, | 432 if (GifAddExtensionBlock(&gif->ExtensionBlockCount, |
389 &gif->ExtensionBlocks, | 433 &gif->ExtensionBlocks, |
390 extFunction, | 434 extFunction, |
391 extData[0], | 435 extData[0], |
392 &extData[1]) == GIF_ERROR) { | 436 &extData[1]) == GIF_ERROR) { |
393 #endif | 437 #endif |
394 return error_return(gif, *bm, "AddExtensionBlock"); | 438 return error_return(*bm, "AddExtensionBlock"); |
395 } | 439 } |
396 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { | 440 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { |
397 return error_return(gif, *bm, "DGifGetExtensionNext"); | 441 return error_return(*bm, "DGifGetExtensionNext"); |
398 } | 442 } |
399 #if GIFLIB_MAJOR < 5 | 443 #if GIFLIB_MAJOR < 5 |
400 temp_save.Function = 0; | 444 temp_save.Function = 0; |
401 #endif | 445 #endif |
402 } | 446 } |
403 break; | 447 break; |
404 | 448 |
405 case TERMINATE_RECORD_TYPE: | 449 case TERMINATE_RECORD_TYPE: |
406 break; | 450 break; |
407 | 451 |
408 default: /* Should be trapped by DGifGetRecordType */ | 452 default: /* Should be trapped by DGifGetRecordType */ |
409 break; | 453 break; |
410 } | 454 } |
411 } while (recType != TERMINATE_RECORD_TYPE); | 455 } while (recType != TERMINATE_RECORD_TYPE); |
412 | 456 |
413 DONE: | |
414 return true; | 457 return true; |
415 } | 458 } |
416 | 459 |
417 /////////////////////////////////////////////////////////////////////////////// | 460 /////////////////////////////////////////////////////////////////////////////// |
418 DEFINE_DECODER_CREATOR(GIFImageDecoder); | 461 DEFINE_DECODER_CREATOR(GIFImageDecoder); |
419 /////////////////////////////////////////////////////////////////////////////// | 462 /////////////////////////////////////////////////////////////////////////////// |
420 | 463 |
421 static bool is_gif(SkStreamRewindable* stream) { | 464 static bool is_gif(SkStreamRewindable* stream) { |
422 char buf[GIF_STAMP_LEN]; | 465 char buf[GIF_STAMP_LEN]; |
423 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { | 466 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { |
(...skipping 16 matching lines...) Expand all Loading... |
440 static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory); | 483 static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory); |
441 | 484 |
442 static SkImageDecoder::Format get_format_gif(SkStreamRewindable* stream) { | 485 static SkImageDecoder::Format get_format_gif(SkStreamRewindable* stream) { |
443 if (is_gif(stream)) { | 486 if (is_gif(stream)) { |
444 return SkImageDecoder::kGIF_Format; | 487 return SkImageDecoder::kGIF_Format; |
445 } | 488 } |
446 return SkImageDecoder::kUnknown_Format; | 489 return SkImageDecoder::kUnknown_Format; |
447 } | 490 } |
448 | 491 |
449 static SkImageDecoder_FormatReg gFormatReg(get_format_gif); | 492 static SkImageDecoder_FormatReg gFormatReg(get_format_gif); |
OLD | NEW |