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