OLD | NEW |
1 | |
2 /* | 1 /* |
3 * Copyright 2006 The Android Open Source Project | 2 * Copyright 2006 The Android Open Source Project |
4 * | 3 * |
5 * 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 |
6 * found in the LICENSE file. | 5 * found in the LICENSE file. |
7 */ | 6 */ |
8 | 7 |
9 | 8 |
10 #include "SkImageDecoder.h" | |
11 #include "SkColor.h" | 9 #include "SkColor.h" |
12 #include "SkColorPriv.h" | 10 #include "SkColorPriv.h" |
| 11 #include "SkColorTable.h" |
| 12 #include "SkImageDecoder.h" |
| 13 #include "SkScaledBitmapSampler.h" |
13 #include "SkStream.h" | 14 #include "SkStream.h" |
14 #include "SkTemplates.h" | 15 #include "SkTemplates.h" |
15 #include "SkPackBits.h" | |
16 | 16 |
17 #include "gif_lib.h" | 17 #include "gif_lib.h" |
18 | 18 |
19 class SkGIFImageDecoder : public SkImageDecoder { | 19 class SkGIFImageDecoder : public SkImageDecoder { |
20 public: | 20 public: |
21 virtual Format getFormat() const SK_OVERRIDE { | 21 virtual Format getFormat() const SK_OVERRIDE { |
22 return kGIF_Format; | 22 return kGIF_Format; |
23 } | 23 } |
24 | 24 |
25 protected: | 25 protected: |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 | 147 |
148 static bool error_return(GifFileType* gif, const SkBitmap& bm, | 148 static bool error_return(GifFileType* gif, const SkBitmap& bm, |
149 const char msg[]) { | 149 const char msg[]) { |
150 #if 0 | 150 #if 0 |
151 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", |
152 msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable()); | 152 msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable()); |
153 #endif | 153 #endif |
154 return false; | 154 return false; |
155 } | 155 } |
156 | 156 |
| 157 /** |
| 158 * Skip rows in the source gif image. |
| 159 * @param gif Source image. |
| 160 * @param dst Scratch output needed by gif library call. Must be >= width bytes
. |
| 161 * @param width Bytes per row in the source image. |
| 162 * @param rowsToSkip Number of rows to skip. |
| 163 * @return True on success, false on GIF_ERROR. |
| 164 */ |
| 165 static bool skip_src_rows(GifFileType* gif, uint8_t* dst, int width, int rowsToS
kip) { |
| 166 for (int i = 0; i < rowsToSkip; i++) { |
| 167 if (DGifGetLine(gif, dst, width) == GIF_ERROR) { |
| 168 return false; |
| 169 } |
| 170 } |
| 171 return true; |
| 172 } |
| 173 |
157 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { | 174 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { |
158 #if GIFLIB_MAJOR < 5 | 175 #if GIFLIB_MAJOR < 5 |
159 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); | 176 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); |
160 #else | 177 #else |
161 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL); | 178 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL); |
162 #endif | 179 #endif |
163 if (NULL == gif) { | 180 if (NULL == gif) { |
164 return error_return(gif, *bm, "DGifOpen"); | 181 return error_return(gif, *bm, "DGifOpen"); |
165 } | 182 } |
166 | 183 |
(...skipping 22 matching lines...) Expand all Loading... |
189 if (DGifGetImageDesc(gif) == GIF_ERROR) { | 206 if (DGifGetImageDesc(gif) == GIF_ERROR) { |
190 return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE"); | 207 return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE"); |
191 } | 208 } |
192 | 209 |
193 if (gif->ImageCount < 1) { // sanity check | 210 if (gif->ImageCount < 1) { // sanity check |
194 return error_return(gif, *bm, "ImageCount < 1"); | 211 return error_return(gif, *bm, "ImageCount < 1"); |
195 } | 212 } |
196 | 213 |
197 width = gif->SWidth; | 214 width = gif->SWidth; |
198 height = gif->SHeight; | 215 height = gif->SHeight; |
199 if (width <= 0 || height <= 0 || | 216 if (width <= 0 || height <= 0) { |
200 !this->chooseFromOneChoice(SkBitmap::kIndex8_Config, | 217 return error_return(gif, *bm, "invalid dimensions"); |
201 width, height)) { | 218 } |
| 219 |
| 220 // FIXME: We could give the caller a choice of images or configs. |
| 221 if (!this->chooseFromOneChoice(SkBitmap::kIndex8_Config, width, heig
ht)) { |
202 return error_return(gif, *bm, "chooseFromOneChoice"); | 222 return error_return(gif, *bm, "chooseFromOneChoice"); |
203 } | 223 } |
204 | 224 |
205 bm->setConfig(SkBitmap::kIndex8_Config, width, height); | 225 SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); |
| 226 |
| 227 bm->setConfig(SkBitmap::kIndex8_Config, sampler.scaledWidth(), |
| 228 sampler.scaledHeight()); |
| 229 |
206 if (SkImageDecoder::kDecodeBounds_Mode == mode) { | 230 if (SkImageDecoder::kDecodeBounds_Mode == mode) { |
207 return true; | 231 return true; |
208 } | 232 } |
209 | 233 |
210 SavedImage* image = &gif->SavedImages[gif->ImageCount-1]; | 234 SavedImage* image = &gif->SavedImages[gif->ImageCount-1]; |
211 const GifImageDesc& desc = image->ImageDesc; | 235 const GifImageDesc& desc = image->ImageDesc; |
212 | 236 |
213 // check for valid descriptor | 237 // check for valid descriptor |
214 if ( (desc.Top | desc.Left) < 0 || | 238 if ( (desc.Top | desc.Left) < 0 || |
215 desc.Left + desc.Width > width || | 239 desc.Left + desc.Width > width || |
216 desc.Top + desc.Height > height) { | 240 desc.Top + desc.Height > height) { |
217 return error_return(gif, *bm, "TopLeft"); | 241 return error_return(gif, *bm, "TopLeft"); |
218 } | 242 } |
219 | 243 |
220 // now we decode the colortable | 244 // now we decode the colortable |
221 int colorCount = 0; | 245 int colorCount = 0; |
222 { | 246 { |
223 const ColorMapObject* cmap = find_colormap(gif); | 247 const ColorMapObject* cmap = find_colormap(gif); |
224 if (NULL == cmap) { | 248 if (NULL == cmap) { |
225 return error_return(gif, *bm, "null cmap"); | 249 return error_return(gif, *bm, "null cmap"); |
226 } | 250 } |
227 | 251 |
228 colorCount = cmap->ColorCount; | 252 colorCount = cmap->ColorCount; |
229 SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount)); | 253 SkAutoTMalloc<SkPMColor> colorStorage(colorCount); |
230 SkPMColor* colorPtr = ctable->lockColors(); | 254 SkPMColor* colorPtr = colorStorage.get(); |
231 for (int index = 0; index < colorCount; index++) | 255 for (int index = 0; index < colorCount; index++) { |
232 colorPtr[index] = SkPackARGB32(0xFF, | 256 colorPtr[index] = SkPackARGB32(0xFF, |
233 cmap->Colors[index].Red, | 257 cmap->Colors[index].Red, |
234 cmap->Colors[index].Green, | 258 cmap->Colors[index].Green, |
235 cmap->Colors[index].Blue); | 259 cmap->Colors[index].Blue); |
| 260 } |
236 | 261 |
237 transpIndex = find_transpIndex(temp_save, colorCount); | 262 transpIndex = find_transpIndex(temp_save, colorCount); |
238 if (transpIndex < 0) | 263 bool reallyHasAlpha = transpIndex >= 0; |
239 ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsA
reOpaque_Flag); | 264 if (reallyHasAlpha) { |
240 else | 265 colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a tra
nsparent SkPMColor |
241 colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor | 266 } |
242 ctable->unlockColors(true); | |
243 | 267 |
244 SkAutoUnref aurts(ctable); | 268 SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable, (colo
rPtr, colorCount))); |
| 269 ctable->setIsOpaque(!reallyHasAlpha); |
245 if (!this->allocPixelRef(bm, ctable)) { | 270 if (!this->allocPixelRef(bm, ctable)) { |
246 return error_return(gif, *bm, "allocPixelRef"); | 271 return error_return(gif, *bm, "allocPixelRef"); |
247 } | 272 } |
248 } | 273 } |
249 | 274 |
250 SkAutoLockPixels alp(*bm); | |
251 | |
252 // time to decode the scanlines | |
253 // | |
254 uint8_t* scanline = bm->getAddr8(0, 0); | |
255 const int rowBytes = bm->rowBytes(); | |
256 const int innerWidth = desc.Width; | 275 const int innerWidth = desc.Width; |
257 const int innerHeight = desc.Height; | 276 const int innerHeight = desc.Height; |
258 | 277 |
259 // abort if either inner dimension is <= 0 | 278 // abort if either inner dimension is <= 0 |
260 if (innerWidth <= 0 || innerHeight <= 0) { | 279 if (innerWidth <= 0 || innerHeight <= 0) { |
261 return error_return(gif, *bm, "non-pos inner width/height"); | 280 return error_return(gif, *bm, "non-pos inner width/height"); |
262 } | 281 } |
263 | 282 |
| 283 SkAutoLockPixels alp(*bm); |
| 284 |
| 285 SkAutoMalloc storage(innerWidth); |
| 286 uint8_t* scanline = (uint8_t*) storage.get(); |
| 287 |
| 288 // GIF has an option to store the scanlines of an image, plus a larg
er background, |
| 289 // filled by a fill color. In this case, we will use a subset of the
larger bitmap |
| 290 // for sampling. |
| 291 SkBitmap subset; |
| 292 SkBitmap* workingBitmap; |
264 // are we only a subset of the total bounds? | 293 // are we only a subset of the total bounds? |
265 if ((desc.Top | desc.Left) > 0 || | 294 if ((desc.Top | desc.Left) > 0 || |
266 innerWidth < width || innerHeight < height) | 295 innerWidth < width || innerHeight < height) { |
267 { | |
268 int fill; | 296 int fill; |
269 if (transpIndex >= 0) { | 297 if (transpIndex >= 0) { |
270 fill = transpIndex; | 298 fill = transpIndex; |
271 } else { | 299 } else { |
272 fill = gif->SBackGroundColor; | 300 fill = gif->SBackGroundColor; |
273 } | 301 } |
274 // check for valid fill index/color | 302 // check for valid fill index/color |
275 if (static_cast<unsigned>(fill) >= | 303 if (static_cast<unsigned>(fill) >= |
276 static_cast<unsigned>(colorCount)) { | 304 static_cast<unsigned>(colorCount)) { |
277 fill = 0; | 305 fill = 0; |
278 } | 306 } |
279 memset(scanline, fill, bm->getSize()); | 307 // Fill the background. |
280 // bump our starting address | 308 memset(bm->getPixels(), fill, bm->getSize()); |
281 scanline += desc.Top * rowBytes + desc.Left; | 309 |
| 310 // Create a subset of the bitmap. |
| 311 SkIRect subsetRect(SkIRect::MakeXYWH(desc.Left / sampler.srcDX()
, |
| 312 desc.Top / sampler.srcDY(), |
| 313 innerWidth / sampler.srcDX(
), |
| 314 innerHeight / sampler.srcDY
())); |
| 315 if (!bm->extractSubset(&subset, subsetRect)) { |
| 316 return error_return(gif, *bm, "Extract failed."); |
| 317 } |
| 318 // Update the sampler. We'll now be only sampling into the subse
t. |
| 319 sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->g
etSampleSize()); |
| 320 workingBitmap = ⊂ |
| 321 } else { |
| 322 workingBitmap = bm; |
| 323 } |
| 324 |
| 325 // bm is already locked, but if we had to take a subset, it must be
locked also, |
| 326 // so that getPixels() will point to its pixels. |
| 327 SkAutoLockPixels alpWorking(*workingBitmap); |
| 328 |
| 329 if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *th
is)) { |
| 330 return error_return(gif, *bm, "Sampler failed to begin."); |
282 } | 331 } |
283 | 332 |
284 // now decode each scanline | 333 // now decode each scanline |
285 if (gif->Image.Interlace) | 334 if (gif->Image.Interlace) { |
286 { | 335 // Iterate over the height of the source data. The sampler will |
| 336 // take care of skipping unneeded rows. |
287 GifInterlaceIter iter(innerHeight); | 337 GifInterlaceIter iter(innerHeight); |
288 for (int y = 0; y < innerHeight; y++) | 338 for (int y = 0; y < innerHeight; y++){ |
289 { | 339 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { |
290 uint8_t* row = scanline + iter.currY() * rowBytes; | |
291 if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) { | |
292 return error_return(gif, *bm, "interlace DGifGetLine"); | 340 return error_return(gif, *bm, "interlace DGifGetLine"); |
293 } | 341 } |
| 342 sampler.sampleInterlaced(scanline, iter.currY()); |
294 iter.next(); | 343 iter.next(); |
295 } | 344 } |
296 } | 345 } else { |
297 else | |
298 { | |
299 // easy, non-interlace case | 346 // easy, non-interlace case |
300 for (int y = 0; y < innerHeight; y++) { | 347 const int outHeight = workingBitmap->height(); |
| 348 skip_src_rows(gif, scanline, innerWidth, sampler.srcY0()); |
| 349 for (int y = 0; y < outHeight; y++) { |
301 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { | 350 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { |
302 return error_return(gif, *bm, "DGifGetLine"); | 351 return error_return(gif, *bm, "DGifGetLine"); |
303 } | 352 } |
304 scanline += rowBytes; | 353 // scanline now contains the raw data. Sample it. |
| 354 sampler.next(scanline); |
| 355 if (y < outHeight - 1) { |
| 356 skip_src_rows(gif, scanline, innerWidth, sampler.srcDY()
- 1); |
| 357 } |
305 } | 358 } |
| 359 // skip the rest of the rows (if any) |
| 360 int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() +
1; |
| 361 SkASSERT(read <= innerHeight); |
| 362 skip_src_rows(gif, scanline, innerWidth, innerHeight - read); |
306 } | 363 } |
307 goto DONE; | 364 goto DONE; |
308 } break; | 365 } break; |
309 | 366 |
310 case EXTENSION_RECORD_TYPE: | 367 case EXTENSION_RECORD_TYPE: |
311 #if GIFLIB_MAJOR < 5 | 368 #if GIFLIB_MAJOR < 5 |
312 if (DGifGetExtension(gif, &temp_save.Function, | 369 if (DGifGetExtension(gif, &temp_save.Function, |
313 &extData) == GIF_ERROR) { | 370 &extData) == GIF_ERROR) { |
314 #else | 371 #else |
315 if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) { | 372 if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) { |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
378 static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory); | 435 static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory); |
379 | 436 |
380 static SkImageDecoder::Format get_format_gif(SkStreamRewindable* stream) { | 437 static SkImageDecoder::Format get_format_gif(SkStreamRewindable* stream) { |
381 if (is_gif(stream)) { | 438 if (is_gif(stream)) { |
382 return SkImageDecoder::kGIF_Format; | 439 return SkImageDecoder::kGIF_Format; |
383 } | 440 } |
384 return SkImageDecoder::kUnknown_Format; | 441 return SkImageDecoder::kUnknown_Format; |
385 } | 442 } |
386 | 443 |
387 static SkImageDecoder_FormatReg gFormatReg(get_format_gif); | 444 static SkImageDecoder_FormatReg gFormatReg(get_format_gif); |
OLD | NEW |