| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
| 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 #include "SkCodec_libgif.h" | 8 #include "SkCodec_libgif.h" |
| 9 #include "SkCodecPriv.h" | 9 #include "SkCodecPriv.h" |
| 10 #include "SkColorPriv.h" | 10 #include "SkColorPriv.h" |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 52 static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, | 52 static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, |
| 53 int32_t size) { | 53 int32_t size) { |
| 54 SkStream* stream = (SkStream*) fileType->UserData; | 54 SkStream* stream = (SkStream*) fileType->UserData; |
| 55 return (int32_t) stream->read(out, size); | 55 return (int32_t) stream->read(out, size); |
| 56 } | 56 } |
| 57 | 57 |
| 58 /* | 58 /* |
| 59 * Open the gif file | 59 * Open the gif file |
| 60 */ | 60 */ |
| 61 static GifFileType* open_gif(SkStream* stream) { | 61 static GifFileType* open_gif(SkStream* stream) { |
| 62 return DGifOpen(stream, read_bytes_callback, NULL); | 62 return DGifOpen(stream, read_bytes_callback, nullptr); |
| 63 } | 63 } |
| 64 | 64 |
| 65 /* | 65 /* |
| 66 * This function cleans up the gif object after the decode completes | 66 * This function cleans up the gif object after the decode completes |
| 67 * It is used in a SkAutoTCallIProc template | 67 * It is used in a SkAutoTCallIProc template |
| 68 */ | 68 */ |
| 69 void SkGifCodec::CloseGif(GifFileType* gif) { | 69 void SkGifCodec::CloseGif(GifFileType* gif) { |
| 70 DGifCloseFile(gif, NULL); | 70 DGifCloseFile(gif, nullptr); |
| 71 } | 71 } |
| 72 | 72 |
| 73 /* | 73 /* |
| 74 * This function free extension data that has been saved to assist the image | 74 * This function free extension data that has been saved to assist the image |
| 75 * decoder | 75 * decoder |
| 76 */ | 76 */ |
| 77 void SkGifCodec::FreeExtension(SavedImage* image) { | 77 void SkGifCodec::FreeExtension(SavedImage* image) { |
| 78 if (NULL != image->ExtensionBlocks) { | 78 if (nullptr != image->ExtensionBlocks) { |
| 79 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks); | 79 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks); |
| 80 } | 80 } |
| 81 } | 81 } |
| 82 | 82 |
| 83 /* | 83 /* |
| 84 * Check if a there is an index of the color table for a transparent pixel | 84 * Check if a there is an index of the color table for a transparent pixel |
| 85 */ | 85 */ |
| 86 static uint32_t find_trans_index(const SavedImage& image) { | 86 static uint32_t find_trans_index(const SavedImage& image) { |
| 87 // If there is a transparent index specified, it will be contained in an | 87 // If there is a transparent index specified, it will be contained in an |
| 88 // extension block. We will loop through extension blocks in reverse order | 88 // extension block. We will loop through extension blocks in reverse order |
| (...skipping 27 matching lines...) Expand all Loading... |
| 116 // Use maximum unsigned int (surely an invalid index) to indicate that a val
id | 116 // Use maximum unsigned int (surely an invalid index) to indicate that a val
id |
| 117 // index was not found. | 117 // index was not found. |
| 118 return SK_MaxU32; | 118 return SK_MaxU32; |
| 119 } | 119 } |
| 120 | 120 |
| 121 /* | 121 /* |
| 122 * Read enough of the stream to initialize the SkGifCodec. | 122 * Read enough of the stream to initialize the SkGifCodec. |
| 123 * Returns a bool representing success or failure. | 123 * Returns a bool representing success or failure. |
| 124 * | 124 * |
| 125 * @param codecOut | 125 * @param codecOut |
| 126 * If it returned true, and codecOut was not NULL, | 126 * If it returned true, and codecOut was not nullptr, |
| 127 * codecOut will be set to a new SkGifCodec. | 127 * codecOut will be set to a new SkGifCodec. |
| 128 * | 128 * |
| 129 * @param gifOut | 129 * @param gifOut |
| 130 * If it returned true, and codecOut was NULL, | 130 * If it returned true, and codecOut was nullptr, |
| 131 * gifOut must be non-NULL and gifOut will be set to a new | 131 * gifOut must be non-nullptr and gifOut will be set to a new |
| 132 * GifFileType pointer. | 132 * GifFileType pointer. |
| 133 * | 133 * |
| 134 * @param stream | 134 * @param stream |
| 135 * Deleted on failure. | 135 * Deleted on failure. |
| 136 * codecOut will take ownership of it in the case where we created a codec. | 136 * codecOut will take ownership of it in the case where we created a codec. |
| 137 * Ownership is unchanged when we returned a gifOut. | 137 * Ownership is unchanged when we returned a gifOut. |
| 138 * | 138 * |
| 139 */ | 139 */ |
| 140 bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType**
gifOut) { | 140 bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType**
gifOut) { |
| 141 SkAutoTDelete<SkStream> streamDeleter(stream); | 141 SkAutoTDelete<SkStream> streamDeleter(stream); |
| 142 | 142 |
| 143 // Read gif header, logical screen descriptor, and global color table | 143 // Read gif header, logical screen descriptor, and global color table |
| 144 SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream)); | 144 SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream)); |
| 145 | 145 |
| 146 if (NULL == gif) { | 146 if (nullptr == gif) { |
| 147 gif_error("DGifOpen failed.\n"); | 147 gif_error("DGifOpen failed.\n"); |
| 148 return false; | 148 return false; |
| 149 } | 149 } |
| 150 | 150 |
| 151 if (NULL != codecOut) { | 151 if (nullptr != codecOut) { |
| 152 // Get fields from header | 152 // Get fields from header |
| 153 const int32_t width = gif->SWidth; | 153 const int32_t width = gif->SWidth; |
| 154 const int32_t height = gif->SHeight; | 154 const int32_t height = gif->SHeight; |
| 155 if (width <= 0 || height <= 0) { | 155 if (width <= 0 || height <= 0) { |
| 156 gif_error("Invalid dimensions.\n"); | 156 gif_error("Invalid dimensions.\n"); |
| 157 return false; | 157 return false; |
| 158 } | 158 } |
| 159 | 159 |
| 160 // Return the codec | 160 // Return the codec |
| 161 // kIndex is the most natural color type for gifs, so we set this as | 161 // kIndex is the most natural color type for gifs, so we set this as |
| 162 // the default. | 162 // the default. |
| 163 // Many gifs specify a color table index for transparent pixels. Every | 163 // Many gifs specify a color table index for transparent pixels. Every |
| 164 // other pixel is guaranteed to be opaque. Despite this, because of the | 164 // other pixel is guaranteed to be opaque. Despite this, because of the |
| 165 // possiblity of transparent pixels, we cannot assume that the image is | 165 // possiblity of transparent pixels, we cannot assume that the image is |
| 166 // opaque. We have the option to set the alpha type as kPremul or | 166 // opaque. We have the option to set the alpha type as kPremul or |
| 167 // kUnpremul. Both are valid since the alpha component will always be | 167 // kUnpremul. Both are valid since the alpha component will always be |
| 168 // 0xFF or the entire 32-bit pixel will be set to zero. We prefer | 168 // 0xFF or the entire 32-bit pixel will be set to zero. We prefer |
| 169 // kPremul because we support kPremul, and it is more efficient to | 169 // kPremul because we support kPremul, and it is more efficient to |
| 170 // use kPremul directly even when kUnpremul is supported. | 170 // use kPremul directly even when kUnpremul is supported. |
| 171 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, | 171 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, |
| 172 kIndex_8_SkColorType, kPremul_SkAlphaType); | 172 kIndex_8_SkColorType, kPremul_SkAlphaType); |
| 173 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach
()); | 173 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach
()); |
| 174 } else { | 174 } else { |
| 175 SkASSERT(NULL != gifOut); | 175 SkASSERT(nullptr != gifOut); |
| 176 streamDeleter.detach(); | 176 streamDeleter.detach(); |
| 177 *gifOut = gif.detach(); | 177 *gifOut = gif.detach(); |
| 178 } | 178 } |
| 179 return true; | 179 return true; |
| 180 } | 180 } |
| 181 | 181 |
| 182 /* | 182 /* |
| 183 * Assumes IsGif was called and returned true | 183 * Assumes IsGif was called and returned true |
| 184 * Creates a gif decoder | 184 * Creates a gif decoder |
| 185 * Reads enough of the stream to determine the image format | 185 * Reads enough of the stream to determine the image format |
| 186 */ | 186 */ |
| 187 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { | 187 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { |
| 188 SkCodec* codec = NULL; | 188 SkCodec* codec = nullptr; |
| 189 if (ReadHeader(stream, &codec, NULL)) { | 189 if (ReadHeader(stream, &codec, nullptr)) { |
| 190 return codec; | 190 return codec; |
| 191 } | 191 } |
| 192 return NULL; | 192 return nullptr; |
| 193 } | 193 } |
| 194 | 194 |
| 195 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, | 195 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, |
| 196 GifFileType* gif) | 196 GifFileType* gif) |
| 197 : INHERITED(srcInfo, stream) | 197 : INHERITED(srcInfo, stream) |
| 198 , fGif(gif) | 198 , fGif(gif) |
| 199 {} | 199 {} |
| 200 | 200 |
| 201 bool SkGifCodec::onRewind() { | 201 bool SkGifCodec::onRewind() { |
| 202 GifFileType* gifOut = NULL; | 202 GifFileType* gifOut = nullptr; |
| 203 if (!ReadHeader(this->stream(), NULL, &gifOut)) { | 203 if (!ReadHeader(this->stream(), nullptr, &gifOut)) { |
| 204 return false; | 204 return false; |
| 205 } | 205 } |
| 206 | 206 |
| 207 SkASSERT(NULL != gifOut); | 207 SkASSERT(nullptr != gifOut); |
| 208 fGif.reset(gifOut); | 208 fGif.reset(gifOut); |
| 209 return true; | 209 return true; |
| 210 } | 210 } |
| 211 | 211 |
| 212 /* | 212 /* |
| 213 * Initiates the gif decode | 213 * Initiates the gif decode |
| 214 */ | 214 */ |
| 215 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, | 215 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
| 216 void* dst, size_t dstRowBytes, | 216 void* dst, size_t dstRowBytes, |
| 217 const Options& opts, | 217 const Options& opts, |
| (...skipping 14 matching lines...) Expand all Loading... |
| 232 } | 232 } |
| 233 if (!conversion_possible(dstInfo, this->getInfo())) { | 233 if (!conversion_possible(dstInfo, this->getInfo())) { |
| 234 return gif_error("Cannot convert input type to output type.\n", | 234 return gif_error("Cannot convert input type to output type.\n", |
| 235 kInvalidConversion); | 235 kInvalidConversion); |
| 236 } | 236 } |
| 237 | 237 |
| 238 // Use this as a container to hold information about any gif extension | 238 // Use this as a container to hold information about any gif extension |
| 239 // blocks. This generally stores transparency and animation instructions. | 239 // blocks. This generally stores transparency and animation instructions. |
| 240 SavedImage saveExt; | 240 SavedImage saveExt; |
| 241 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); | 241 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); |
| 242 saveExt.ExtensionBlocks = NULL; | 242 saveExt.ExtensionBlocks = nullptr; |
| 243 saveExt.ExtensionBlockCount = 0; | 243 saveExt.ExtensionBlockCount = 0; |
| 244 GifByteType* extData; | 244 GifByteType* extData; |
| 245 int32_t extFunction; | 245 int32_t extFunction; |
| 246 | 246 |
| 247 // We will loop over components of gif images until we find an image. Once | 247 // We will loop over components of gif images until we find an image. Once |
| 248 // we find an image, we will decode and return it. While many gif files | 248 // we find an image, we will decode and return it. While many gif files |
| 249 // contain more than one image, we will simply decode the first image. | 249 // contain more than one image, we will simply decode the first image. |
| 250 const int32_t width = dstInfo.width(); | 250 const int32_t width = dstInfo.width(); |
| 251 const int32_t height = dstInfo.height(); | 251 const int32_t height = dstInfo.height(); |
| 252 GifRecordType recordType; | 252 GifRecordType recordType; |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 302 } else if (imageTop < 0) { | 302 } else if (imageTop < 0) { |
| 303 gif_warning("Shifting image down to fit\n"); | 303 gif_warning("Shifting image down to fit\n"); |
| 304 imageTop = 0; | 304 imageTop = 0; |
| 305 } | 305 } |
| 306 | 306 |
| 307 // Create a color table to store colors the giflib colorMap | 307 // Create a color table to store colors the giflib colorMap |
| 308 SkPMColor alternateColorPtr[256]; | 308 SkPMColor alternateColorPtr[256]; |
| 309 SkPMColor* colorTable; | 309 SkPMColor* colorTable; |
| 310 SkColorType dstColorType = dstInfo.colorType(); | 310 SkColorType dstColorType = dstInfo.colorType(); |
| 311 if (kIndex_8_SkColorType == dstColorType) { | 311 if (kIndex_8_SkColorType == dstColorType) { |
| 312 SkASSERT(NULL != inputColorPtr); | 312 SkASSERT(nullptr != inputColorPtr); |
| 313 SkASSERT(NULL != inputColorCount); | 313 SkASSERT(nullptr != inputColorCount); |
| 314 colorTable = inputColorPtr; | 314 colorTable = inputColorPtr; |
| 315 } else { | 315 } else { |
| 316 colorTable = alternateColorPtr; | 316 colorTable = alternateColorPtr; |
| 317 } | 317 } |
| 318 | 318 |
| 319 // Set up the color table | 319 // Set up the color table |
| 320 uint32_t colorCount = 0; | 320 uint32_t colorCount = 0; |
| 321 // Allocate maximum storage to deal with invalid indices safely | 321 // Allocate maximum storage to deal with invalid indices safely |
| 322 const uint32_t maxColors = 256; | 322 const uint32_t maxColors = 256; |
| 323 ColorMapObject* colorMap = fGif->Image.ColorMap; | 323 ColorMapObject* colorMap = fGif->Image.ColorMap; |
| 324 // If there is no local color table, use the global color table | 324 // If there is no local color table, use the global color table |
| 325 if (NULL == colorMap) { | 325 if (nullptr == colorMap) { |
| 326 colorMap = fGif->SColorMap; | 326 colorMap = fGif->SColorMap; |
| 327 } | 327 } |
| 328 if (NULL != colorMap) { | 328 if (nullptr != colorMap) { |
| 329 colorCount = colorMap->ColorCount; | 329 colorCount = colorMap->ColorCount; |
| 330 SkASSERT(colorCount == | 330 SkASSERT(colorCount == |
| 331 (unsigned) (1 << (colorMap->BitsPerPixel))); | 331 (unsigned) (1 << (colorMap->BitsPerPixel))); |
| 332 SkASSERT(colorCount <= 256); | 332 SkASSERT(colorCount <= 256); |
| 333 for (uint32_t i = 0; i < colorCount; i++) { | 333 for (uint32_t i = 0; i < colorCount; i++) { |
| 334 colorTable[i] = SkPackARGB32(0xFF, | 334 colorTable[i] = SkPackARGB32(0xFF, |
| 335 colorMap->Colors[i].Red, | 335 colorMap->Colors[i].Red, |
| 336 colorMap->Colors[i].Green, | 336 colorMap->Colors[i].Green, |
| 337 colorMap->Colors[i].Blue); | 337 colorMap->Colors[i].Blue); |
| 338 } | 338 } |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 373 kYes_ZeroInitialized == zeroInit; | 373 kYes_ZeroInitialized == zeroInit; |
| 374 | 374 |
| 375 | 375 |
| 376 // Fill in the color table for indices greater than color count. | 376 // Fill in the color table for indices greater than color count. |
| 377 // This allows for predictable, safe behavior. | 377 // This allows for predictable, safe behavior. |
| 378 for (uint32_t i = colorCount; i < maxColors; i++) { | 378 for (uint32_t i = colorCount; i < maxColors; i++) { |
| 379 colorTable[i] = colorTable[fillIndex]; | 379 colorTable[i] = colorTable[fillIndex]; |
| 380 } | 380 } |
| 381 | 381 |
| 382 // Check if image is only a subset of the image frame | 382 // Check if image is only a subset of the image frame |
| 383 SkAutoTDelete<SkSwizzler> swizzler(NULL); | 383 SkAutoTDelete<SkSwizzler> swizzler(nullptr); |
| 384 if (innerWidth < width || innerHeight < height) { | 384 if (innerWidth < width || innerHeight < height) { |
| 385 | 385 |
| 386 // Modify the destination info | 386 // Modify the destination info |
| 387 const SkImageInfo subsetDstInfo = | 387 const SkImageInfo subsetDstInfo = |
| 388 dstInfo.makeWH(innerWidth, innerHeight); | 388 dstInfo.makeWH(innerWidth, innerHeight); |
| 389 | 389 |
| 390 // Fill the destination with the fill color | 390 // Fill the destination with the fill color |
| 391 // FIXME: This may not be the behavior that we want for | 391 // FIXME: This may not be the behavior that we want for |
| 392 // animated gifs where we draw on top of the | 392 // animated gifs where we draw on top of the |
| 393 // previous frame. | 393 // previous frame. |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 490 // such as transparency or animation. | 490 // such as transparency or animation. |
| 491 case EXTENSION_RECORD_TYPE: | 491 case EXTENSION_RECORD_TYPE: |
| 492 // Read extension data | 492 // Read extension data |
| 493 if (GIF_ERROR == | 493 if (GIF_ERROR == |
| 494 DGifGetExtension(fGif, &extFunction, &extData)) { | 494 DGifGetExtension(fGif, &extFunction, &extData)) { |
| 495 return gif_error("Could not get extension.\n", | 495 return gif_error("Could not get extension.\n", |
| 496 kIncompleteInput); | 496 kIncompleteInput); |
| 497 } | 497 } |
| 498 | 498 |
| 499 // Create an extension block with our data | 499 // Create an extension block with our data |
| 500 while (NULL != extData) { | 500 while (nullptr != extData) { |
| 501 // Add a single block | 501 // Add a single block |
| 502 if (GIF_ERROR == | 502 if (GIF_ERROR == |
| 503 GifAddExtensionBlock(&saveExt.ExtensionBlockCount, | 503 GifAddExtensionBlock(&saveExt.ExtensionBlockCount, |
| 504 &saveExt.ExtensionBlocks, extFunction, extData[0], | 504 &saveExt.ExtensionBlocks, extFunction, extData[0], |
| 505 &extData[1])) { | 505 &extData[1])) { |
| 506 return gif_error("Could not add extension block.\n", | 506 return gif_error("Could not add extension block.\n", |
| 507 kIncompleteInput); | 507 kIncompleteInput); |
| 508 } | 508 } |
| 509 // Move to the next block | 509 // Move to the next block |
| 510 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) { | 510 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 522 // giflib returns an error code if the record type is not known. | 522 // giflib returns an error code if the record type is not known. |
| 523 // We should catch this error immediately. | 523 // We should catch this error immediately. |
| 524 SkASSERT(false); | 524 SkASSERT(false); |
| 525 break; | 525 break; |
| 526 } | 526 } |
| 527 } while (TERMINATE_RECORD_TYPE != recordType); | 527 } while (TERMINATE_RECORD_TYPE != recordType); |
| 528 | 528 |
| 529 return gif_error("Could not find any images to decode in gif file.\n", | 529 return gif_error("Could not find any images to decode in gif file.\n", |
| 530 kInvalidInput); | 530 kInvalidInput); |
| 531 } | 531 } |
| OLD | NEW |