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