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