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