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 if (mGif->SColorMap) { 115 // calculate bg color 116 GraphicsControlBlock gcb; 117 DGifSavedExtensionToGCB(mGif, 0, &gcb); 118 if (gcb.TransparentColor == NO_TRANSPARENT_COLOR) { 119 mBgColor = gifColorToColor8888(mGif->SColorMap->Colors[mGif->SBackGroundColor]); 120 } 121 } 122 } 123 124 FrameSequence_gif::~FrameSequence_gif() { 125 if (mGif) { 126 DGifCloseFile(mGif, NULL); 127 } 128 delete[] mPreservedFrames; 129 delete[] mRestoringFrames; 130 } 131 132 FrameSequenceState* FrameSequence_gif::createState() const { 133 return new FrameSequenceState_gif(*this); 134 } 135 136 //////////////////////////////////////////////////////////////////////////////// 137 // draw helpers 138 //////////////////////////////////////////////////////////////////////////////// 139 140 // return true if area of 'target' is completely covers area of 'covered' 141 static bool checkIfCover(const GifImageDesc& target, const GifImageDesc& covered) { 142 return target.Left <= covered.Left 143 && covered.Left + covered.Width <= target.Left + target.Width 144 && target.Top <= covered.Top 145 && covered.Top + covered.Height <= target.Top + target.Height; 146 } 147 148 static void copyLine(Color8888* dst, const unsigned char* src, const ColorMapObject* cmap, 149 int transparent, int width) { 150 for (; width > 0; width--, src++, dst++) { 151 if (*src != transparent) { 152 *dst = gifColorToColor8888(cmap->Colors[*src]); 153 } 154 } 155 } 156 157 static void setLineColor(Color8888* dst, Color8888 color, int width) { 158 for (; width > 0; width--, dst++) { 159 *dst = color; 160 } 161 } 162 163 static void getCopySize(const GifImageDesc& imageDesc, int maxWidth, int maxHeight, 164 GifWord& copyWidth, GifWord& copyHeight) { 165 copyWidth = imageDesc.Width; 166 if (imageDesc.Left + copyWidth > maxWidth) { 167 copyWidth = maxWidth - imageDesc.Left; 168 } 169 copyHeight = imageDesc.Height; 170 if (imageDesc.Top + copyHeight > maxHeight) { 171 copyHeight = maxHeight - imageDesc.Top; 172 } 173 } 174 175 //////////////////////////////////////////////////////////////////////////////// 176 // Frame sequence state 177 //////////////////////////////////////////////////////////////////////////////// 178 179 FrameSequenceState_gif::FrameSequenceState_gif(const FrameSequence_gif& frameSequence) : 180 mFrameSequence(frameSequence), mPreserveBuffer(NULL), mPreserveBufferFrame(-1) { 181 } 182 183 FrameSequenceState_gif::~FrameSequenceState_gif() { 184 delete[] mPreserveBuffer; 185 } 186 187 void FrameSequenceState_gif::savePreserveBuffer(Color8888* outputPtr, int outputPixelStride, int frameNr) { 188 if (frameNr == mPreserveBufferFrame) return; 189 190 mPreserveBufferFrame = frameNr; 191 const int width = mFrameSequence.getWidth(); 192 const int height = mFrameSequence.getHeight(); 193 if (!mPreserveBuffer) { 194 mPreserveBuffer = new Color8888[width * height]; 195 } 196 for (int y = 0; y < height; y++) { 197 memcpy(mPreserveBuffer + width * y, 198 outputPtr + outputPixelStride * y, 199 width * 4); 200 } 201 } 202 203 void FrameSequenceState_gif::restorePreserveBuffer(Color8888* outputPtr, int outputPixelStride) { 204 const int width = mFrameSequence.getWidth(); 205 const int height = mFrameSequence.getHeight(); 206 if (!mPreserveBuffer) { 207 ALOGD("preserve buffer not allocated! ah!"); 208 return; 209 } 210 for (int y = 0; y < height; y++) { 211 memcpy(outputPtr + outputPixelStride * y, 212 mPreserveBuffer + width * y, 213 width * 4); 214 } 215 } 216 217 long FrameSequenceState_gif::drawFrame(int frameNr, 218 Color8888* outputPtr, int outputPixelStride, int previousFrameNr) { 219 220 GifFileType* gif = mFrameSequence.getGif(); 221 if (!gif) { 222 ALOGD("Cannot drawFrame, mGif is NULL"); 223 return -1; 224 } 225 226 #if GIF_DEBUG 227 ALOGD(" drawFrame on %p nr %d on addr %p, previous frame nr %d", 228 this, frameNr, outputPtr, previousFrameNr); 229 #endif 230 231 const int height = mFrameSequence.getHeight(); 232 const int width = mFrameSequence.getWidth(); 233 234 GraphicsControlBlock gcb; 235 236 int start = max(previousFrameNr + 1, 0); 237 238 for (int i = max(start - 1, 0); i < frameNr; i++) { 239 int neededPreservedFrame = mFrameSequence.getRestoringFrame(i); 240 if (neededPreservedFrame >= 0 && (mPreserveBufferFrame != neededPreservedFrame)) { 241 #if GIF_DEBUG 242 ALOGD("frame %d needs frame %d preserved, but %d is currently, so drawing from scratch", 243 i, neededPreservedFrame, mPreserveBufferFrame); 244 #endif 245 start = 0; 246 } 247 } 248 249 for (int i = start; i <= frameNr; i++) { 250 DGifSavedExtensionToGCB(gif, i, &gcb); 251 const SavedImage& frame = gif->SavedImages[i]; 252 253 #if GIF_DEBUG 254 bool frameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR; 255 ALOGD("producing frame %d, drawing frame %d (opaque %d, disp %d, del %d)", 256 frameNr, i, frameOpaque, gcb.DisposalMode, gcb.DelayTime); 257 #endif 258 if (i == 0) { 259 //clear bitmap 260 Color8888 bgColor = mFrameSequence.getBackgroundColor(); 261 for (int y = 0; y < height; y++) { 262 for (int x = 0; x < width; x++) { 263 outputPtr[y * outputPixelStride + x] = bgColor; 264 } 265 } 266 } else { 267 GraphicsControlBlock prevGcb; 268 DGifSavedExtensionToGCB(gif, i - 1, &prevGcb); 269 const SavedImage& prevFrame = gif->SavedImages[i - 1]; 270 bool prevFrameDisposed = willBeCleared(prevGcb); 271 272 bool newFrameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR; 273 bool prevFrameCompletelyCovered = newFrameOpaque 274 && checkIfCover(frame.ImageDesc, prevFrame.ImageDesc); 275 276 if (prevFrameDisposed && !prevFrameCompletelyCovered) { 277 switch (prevGcb.DisposalMode) { 278 case DISPOSE_BACKGROUND: { 279 Color8888* dst = outputPtr + prevFrame.ImageDesc.Left + 280 prevFrame.ImageDesc.Top * outputPixelStride; 281 282 GifWord copyWidth, copyHeight; 283 getCopySize(prevFrame.ImageDesc, width, height, copyWidth, copyHeight); 284 for (; copyHeight > 0; copyHeight--) { 285 setLineColor(dst, TRANSPARENT, copyWidth); 286 dst += outputPixelStride; 287 } 288 } break; 289 case DISPOSE_PREVIOUS: { 290 restorePreserveBuffer(outputPtr, outputPixelStride); 291 } break; 292 } 293 } 294 295 if (mFrameSequence.getPreservedFrame(i - 1)) { 296 // currently drawn frame will be restored by a following DISPOSE_PREVIOUS draw, so 297 // we preserve it 298 savePreserveBuffer(outputPtr, outputPixelStride, i - 1); 299 } 300 } 301 302 bool willBeCleared = gcb.DisposalMode == DISPOSE_BACKGROUND 303 || gcb.DisposalMode == DISPOSE_PREVIOUS; 304 if (i == frameNr || !willBeCleared) { 305 const ColorMapObject* cmap = gif->SColorMap; 306 if (frame.ImageDesc.ColorMap) { 307 cmap = frame.ImageDesc.ColorMap; 308 } 309 310 if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) { 311 ALOGW("Warning: potentially corrupt color map"); 312 } 313 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 // return last frame's delay 328 const int maxFrame = gif->ImageCount; 329 const int lastFrame = (frameNr + maxFrame - 1) % maxFrame; 330 DGifSavedExtensionToGCB(gif, lastFrame, &gcb); 331 return getDelayMs(gcb); 332 } 333 334 //////////////////////////////////////////////////////////////////////////////// 335 // Registry 336 //////////////////////////////////////////////////////////////////////////////// 337 338 #include "Registry.h" 339 340 static bool isGif(void* header, int header_size) { 341 return !memcmp(GIF_STAMP, header, GIF_STAMP_LEN) 342 || !memcmp(GIF87_STAMP, header, GIF_STAMP_LEN) 343 || !memcmp(GIF89_STAMP, header, GIF_STAMP_LEN); 344 } 345 346 static bool acceptsBuffers() { 347 return false; 348 } 349 350 static FrameSequence* createFramesequence(Stream* stream) { 351 return new FrameSequence_gif(stream); 352 } 353 354 static RegistryEntry gEntry = { 355 GIF_STAMP_LEN, 356 isGif, 357 createFramesequence, 358 NULL, 359 acceptsBuffers, 360 }; 361 static Registry gRegister(gEntry); 362 363