1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <string.h> 18 #include "JNIHelpers.h" 19 #include "utils/log.h" 20 #include "utils/math.h" 21 22 #include "FrameSequence_gif.h" 23 24 #define GIF_DEBUG 0 25 26 static int streamReader(GifFileType* fileType, GifByteType* out, int size) { 27 Stream* stream = (Stream*) fileType->UserData; 28 return (int) stream->read(out, size); 29 } 30 31 static Color8888 gifColorToColor8888(const GifColorType& color) { 32 return ARGB_TO_COLOR8888(0xff, color.Red, color.Green, color.Blue); 33 } 34 35 static long getDelayMs(GraphicsControlBlock& gcb) { 36 return gcb.DelayTime * 10; 37 } 38 39 static bool willBeCleared(const GraphicsControlBlock& gcb) { 40 return gcb.DisposalMode == DISPOSE_BACKGROUND || gcb.DisposalMode == DISPOSE_PREVIOUS; 41 } 42 43 //////////////////////////////////////////////////////////////////////////////// 44 // Frame sequence 45 //////////////////////////////////////////////////////////////////////////////// 46 47 FrameSequence_gif::FrameSequence_gif(Stream* stream) : 48 mLoopCount(1), mBgColor(TRANSPARENT), mPreservedFrames(NULL), mRestoringFrames(NULL) { 49 mGif = DGifOpen(stream, streamReader, NULL); 50 if (!mGif) { 51 ALOGW("Gif load failed"); 52 return; 53 } 54 55 if (DGifSlurp(mGif) != GIF_OK) { 56 ALOGW("Gif slurp failed"); 57 DGifCloseFile(mGif, NULL); 58 mGif = NULL; 59 return; 60 } 61 62 long durationMs = 0; 63 int lastUnclearedFrame = -1; 64 mPreservedFrames = new bool[mGif->ImageCount]; 65 mRestoringFrames = new int[mGif->ImageCount]; 66 67 GraphicsControlBlock gcb; 68 for (int i = 0; i < mGif->ImageCount; i++) { 69 const SavedImage& image = mGif->SavedImages[i]; 70 71 // find the loop extension pair 72 for (int j = 0; (j + 1) < image.ExtensionBlockCount; j++) { 73 ExtensionBlock* eb1 = image.ExtensionBlocks + j; 74 ExtensionBlock* eb2 = image.ExtensionBlocks + j + 1; 75 if (eb1->Function == APPLICATION_EXT_FUNC_CODE 76 // look for "NETSCAPE2.0" app extension 77 && eb1->ByteCount == 11 78 && !memcmp((const char*)(eb1->Bytes), "NETSCAPE2.0", 11) 79 // verify extension contents and get loop count 80 && eb2->Function == CONTINUE_EXT_FUNC_CODE 81 && eb2->ByteCount == 3 82 && eb2->Bytes[0] == 1) { 83 mLoopCount = (int)(eb2->Bytes[2] << 8) + (int)(eb2->Bytes[1]); 84 } 85 } 86 87 DGifSavedExtensionToGCB(mGif, i, &gcb); 88 89 // timing 90 durationMs += getDelayMs(gcb); 91 92 // preserve logic 93 mPreservedFrames[i] = false; 94 mRestoringFrames[i] = -1; 95 if (gcb.DisposalMode == DISPOSE_PREVIOUS && lastUnclearedFrame >= 0) { 96 mPreservedFrames[lastUnclearedFrame] = true; 97 mRestoringFrames[i] = lastUnclearedFrame; 98 } 99 if (!willBeCleared(gcb)) { 100 lastUnclearedFrame = i; 101 } 102 } 103 104 #if GIF_DEBUG 105 ALOGD("FrameSequence_gif created with size %d %d, frames %d dur %ld", 106 mGif->SWidth, mGif->SHeight, mGif->ImageCount, durationMs); 107 for (int i = 0; i < mGif->ImageCount; i++) { 108 DGifSavedExtensionToGCB(mGif, i, &gcb); 109 ALOGD(" Frame %d - must preserve %d, restore point %d, trans color %d", 110 i, mPreservedFrames[i], mRestoringFrames[i], gcb.TransparentColor); 111 } 112 #endif 113 114 const ColorMapObject* cmap = mGif->SColorMap; 115 if (cmap) { 116 // calculate bg color 117 GraphicsControlBlock gcb; 118 DGifSavedExtensionToGCB(mGif, 0, &gcb); 119 if (gcb.TransparentColor == NO_TRANSPARENT_COLOR 120 && mGif->SBackGroundColor < cmap->ColorCount) { 121 mBgColor = gifColorToColor8888(cmap->Colors[mGif->SBackGroundColor]); 122 } 123 } 124 } 125 126 FrameSequence_gif::~FrameSequence_gif() { 127 if (mGif) { 128 DGifCloseFile(mGif, NULL); 129 } 130 delete[] mPreservedFrames; 131 delete[] mRestoringFrames; 132 } 133 134 FrameSequenceState* FrameSequence_gif::createState() const { 135 return new FrameSequenceState_gif(*this); 136 } 137 138 //////////////////////////////////////////////////////////////////////////////// 139 // draw helpers 140 //////////////////////////////////////////////////////////////////////////////// 141 142 // return true if area of 'target' is completely covers area of 'covered' 143 static bool checkIfCover(const GifImageDesc& target, const GifImageDesc& covered) { 144 return target.Left <= covered.Left 145 && covered.Left + covered.Width <= target.Left + target.Width 146 && target.Top <= covered.Top 147 && covered.Top + covered.Height <= target.Top + target.Height; 148 } 149 150 static void copyLine(Color8888* dst, const unsigned char* src, const ColorMapObject* cmap, 151 int transparent, int width) { 152 for (; width > 0; width--, src++, dst++) { 153 if (*src != transparent && *src < cmap->ColorCount) { 154 *dst = gifColorToColor8888(cmap->Colors[*src]); 155 } 156 } 157 } 158 159 static void setLineColor(Color8888* dst, Color8888 color, int width) { 160 for (; width > 0; width--, dst++) { 161 *dst = color; 162 } 163 } 164 165 static void getCopySize(const GifImageDesc& imageDesc, int maxWidth, int maxHeight, 166 GifWord& copyWidth, GifWord& copyHeight) { 167 copyWidth = imageDesc.Width; 168 if (imageDesc.Left + copyWidth > maxWidth) { 169 copyWidth = maxWidth - imageDesc.Left; 170 } 171 copyHeight = imageDesc.Height; 172 if (imageDesc.Top + copyHeight > maxHeight) { 173 copyHeight = maxHeight - imageDesc.Top; 174 } 175 } 176 177 //////////////////////////////////////////////////////////////////////////////// 178 // Frame sequence state 179 //////////////////////////////////////////////////////////////////////////////// 180 181 FrameSequenceState_gif::FrameSequenceState_gif(const FrameSequence_gif& frameSequence) : 182 mFrameSequence(frameSequence), mPreserveBuffer(NULL), mPreserveBufferFrame(-1) { 183 } 184 185 FrameSequenceState_gif::~FrameSequenceState_gif() { 186 delete[] mPreserveBuffer; 187 } 188 189 void FrameSequenceState_gif::savePreserveBuffer(Color8888* outputPtr, int outputPixelStride, int frameNr) { 190 if (frameNr == mPreserveBufferFrame) return; 191 192 mPreserveBufferFrame = frameNr; 193 const int width = mFrameSequence.getWidth(); 194 const int height = mFrameSequence.getHeight(); 195 if (!mPreserveBuffer) { 196 mPreserveBuffer = new Color8888[width * height]; 197 } 198 for (int y = 0; y < height; y++) { 199 memcpy(mPreserveBuffer + width * y, 200 outputPtr + outputPixelStride * y, 201 width * 4); 202 } 203 } 204 205 void FrameSequenceState_gif::restorePreserveBuffer(Color8888* outputPtr, int outputPixelStride) { 206 const int width = mFrameSequence.getWidth(); 207 const int height = mFrameSequence.getHeight(); 208 if (!mPreserveBuffer) { 209 ALOGD("preserve buffer not allocated! ah!"); 210 return; 211 } 212 for (int y = 0; y < height; y++) { 213 memcpy(outputPtr + outputPixelStride * y, 214 mPreserveBuffer + width * y, 215 width * 4); 216 } 217 } 218 219 long FrameSequenceState_gif::drawFrame(int frameNr, 220 Color8888* outputPtr, int outputPixelStride, int previousFrameNr) { 221 222 GifFileType* gif = mFrameSequence.getGif(); 223 if (!gif) { 224 ALOGD("Cannot drawFrame, mGif is NULL"); 225 return -1; 226 } 227 228 #if GIF_DEBUG 229 ALOGD(" drawFrame on %p nr %d on addr %p, previous frame nr %d", 230 this, frameNr, outputPtr, previousFrameNr); 231 #endif 232 233 const int height = mFrameSequence.getHeight(); 234 const int width = mFrameSequence.getWidth(); 235 236 GraphicsControlBlock gcb; 237 238 int start = max(previousFrameNr + 1, 0); 239 240 for (int i = max(start - 1, 0); i < frameNr; i++) { 241 int neededPreservedFrame = mFrameSequence.getRestoringFrame(i); 242 if (neededPreservedFrame >= 0 && (mPreserveBufferFrame != neededPreservedFrame)) { 243 #if GIF_DEBUG 244 ALOGD("frame %d needs frame %d preserved, but %d is currently, so drawing from scratch", 245 i, neededPreservedFrame, mPreserveBufferFrame); 246 #endif 247 start = 0; 248 } 249 } 250 251 for (int i = start; i <= frameNr; i++) { 252 DGifSavedExtensionToGCB(gif, i, &gcb); 253 const SavedImage& frame = gif->SavedImages[i]; 254 255 #if GIF_DEBUG 256 bool frameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR; 257 ALOGD("producing frame %d, drawing frame %d (opaque %d, disp %d, del %d)", 258 frameNr, i, frameOpaque, gcb.DisposalMode, gcb.DelayTime); 259 #endif 260 if (i == 0) { 261 //clear bitmap 262 Color8888 bgColor = mFrameSequence.getBackgroundColor(); 263 for (int y = 0; y < height; y++) { 264 for (int x = 0; x < width; x++) { 265 outputPtr[y * outputPixelStride + x] = bgColor; 266 } 267 } 268 } else { 269 GraphicsControlBlock prevGcb; 270 DGifSavedExtensionToGCB(gif, i - 1, &prevGcb); 271 const SavedImage& prevFrame = gif->SavedImages[i - 1]; 272 bool prevFrameDisposed = willBeCleared(prevGcb); 273 274 bool newFrameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR; 275 bool prevFrameCompletelyCovered = newFrameOpaque 276 && checkIfCover(frame.ImageDesc, prevFrame.ImageDesc); 277 278 if (prevFrameDisposed && !prevFrameCompletelyCovered) { 279 switch (prevGcb.DisposalMode) { 280 case DISPOSE_BACKGROUND: { 281 Color8888* dst = outputPtr + prevFrame.ImageDesc.Left + 282 prevFrame.ImageDesc.Top * outputPixelStride; 283 284 GifWord copyWidth, copyHeight; 285 getCopySize(prevFrame.ImageDesc, width, height, copyWidth, copyHeight); 286 for (; copyHeight > 0; copyHeight--) { 287 setLineColor(dst, TRANSPARENT, copyWidth); 288 dst += outputPixelStride; 289 } 290 } break; 291 case DISPOSE_PREVIOUS: { 292 restorePreserveBuffer(outputPtr, outputPixelStride); 293 } break; 294 } 295 } 296 297 if (mFrameSequence.getPreservedFrame(i - 1)) { 298 // currently drawn frame will be restored by a following DISPOSE_PREVIOUS draw, so 299 // we preserve it 300 savePreserveBuffer(outputPtr, outputPixelStride, i - 1); 301 } 302 } 303 304 bool willBeCleared = gcb.DisposalMode == DISPOSE_BACKGROUND 305 || gcb.DisposalMode == DISPOSE_PREVIOUS; 306 if (i == frameNr || !willBeCleared) { 307 const ColorMapObject* cmap = gif->SColorMap; 308 if (frame.ImageDesc.ColorMap) { 309 cmap = frame.ImageDesc.ColorMap; 310 } 311 312 // If a cmap is missing, the frame can't be decoded, so we skip it. 313 if (cmap) { 314 const unsigned char* src = (unsigned char*)frame.RasterBits; 315 Color8888* dst = outputPtr + frame.ImageDesc.Left + 316 frame.ImageDesc.Top * outputPixelStride; 317 GifWord copyWidth, copyHeight; 318 getCopySize(frame.ImageDesc, width, height, copyWidth, copyHeight); 319 for (; copyHeight > 0; copyHeight--) { 320 copyLine(dst, src, cmap, gcb.TransparentColor, copyWidth); 321 src += frame.ImageDesc.Width; 322 dst += outputPixelStride; 323 } 324 } 325 } 326 } 327 328 // return last frame's delay 329 const int maxFrame = gif->ImageCount; 330 const int lastFrame = (frameNr + maxFrame - 1) % maxFrame; 331 DGifSavedExtensionToGCB(gif, lastFrame, &gcb); 332 return getDelayMs(gcb); 333 } 334 335 //////////////////////////////////////////////////////////////////////////////// 336 // Registry 337 //////////////////////////////////////////////////////////////////////////////// 338 339 #include "Registry.h" 340 341 static bool isGif(void* header, int header_size) { 342 return !memcmp(GIF_STAMP, header, GIF_STAMP_LEN) 343 || !memcmp(GIF87_STAMP, header, GIF_STAMP_LEN) 344 || !memcmp(GIF89_STAMP, header, GIF_STAMP_LEN); 345 } 346 347 static bool acceptsBuffers() { 348 return false; 349 } 350 351 static FrameSequence* createFramesequence(Stream* stream) { 352 return new FrameSequence_gif(stream); 353 } 354 355 static RegistryEntry gEntry = { 356 GIF_STAMP_LEN, 357 isGif, 358 createFramesequence, 359 NULL, 360 acceptsBuffers, 361 }; 362 static Registry gRegister(gEntry); 363