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 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 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 60 SkGIFMovie::~SkGIFMovie() 61 { 62 if (fGIF) 63 DGifCloseFile(fGIF); 64 } 65 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 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 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 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 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