FrameSequence_gif.cpp revision edf9b83e5df6e178e5bce7ffb0fe6ff0c9248a7c
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