FrameSequence_webp.cpp revision d2fd12358d35eb999ef8b92b1dcce07f7be15fc3
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <string.h>
18#include "JNIHelpers.h"
19#include "utils/log.h"
20#include "utils/math.h"
21#include "webp/format_constants.h"
22
23#include "FrameSequence_webp.h"
24
25#define WEBP_DEBUG 0
26
27////////////////////////////////////////////////////////////////////////////////
28// Frame sequence
29////////////////////////////////////////////////////////////////////////////////
30
31static uint32_t GetLE32(const uint8_t* const data) {
32    return MKFOURCC(data[0], data[1], data[2], data[3]);
33}
34
35// Returns true if the frame covers full canvas.
36static bool isFullFrame(const WebPIterator& frame, int canvasWidth, int canvasHeight) {
37    return (frame.width == canvasWidth && frame.height == canvasHeight);
38}
39
40// Returns true if the rectangle defined by 'frame' contains pixel (x, y).
41static bool FrameContainsPixel(const WebPIterator& frame, int x, int y) {
42    const int left = frame.x_offset;
43    const int right = left + frame.width;
44    const int top = frame.y_offset;
45    const int bottom = top + frame.height;
46    return x >= left && x < right && y >= top && y < bottom;
47}
48
49// Construct mIsKeyFrame array.
50void FrameSequence_webp::constructDependencyChain() {
51    const size_t frameCount = getFrameCount();
52    mIsKeyFrame = new bool[frameCount];
53    const int canvasWidth = getWidth();
54    const int canvasHeight = getHeight();
55
56    WebPIterator prev;
57    WebPIterator curr;
58
59    // Note: WebPDemuxGetFrame() uses base-1 counting.
60    int ok = WebPDemuxGetFrame(mDemux, 1, &curr);
61    ALOG_ASSERT(ok, "Could not retrieve frame# 0");
62    mIsKeyFrame[0] = true;  // 0th frame is always a key frame.
63    for (size_t i = 1; i < frameCount; i++) {
64        prev = curr;
65        ok = WebPDemuxGetFrame(mDemux, i + 1, &curr);  // Get ith frame.
66        ALOG_ASSERT(ok, "Could not retrieve frame# %d", i);
67
68        if ((!curr.has_alpha || curr.blend_method == WEBP_MUX_NO_BLEND) &&
69                isFullFrame(curr, canvasWidth, canvasHeight)) {
70            mIsKeyFrame[i] = true;
71        } else {
72            mIsKeyFrame[i] = (prev.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) &&
73                    (isFullFrame(prev, canvasWidth, canvasHeight) || mIsKeyFrame[i - 1]);
74        }
75    }
76    WebPDemuxReleaseIterator(&prev);
77    WebPDemuxReleaseIterator(&curr);
78
79#if WEBP_DEBUG
80    ALOGD("Dependency chain:");
81    for (size_t i = 0; i < frameCount; i++) {
82        ALOGD("Frame# %zu: %s", i, mIsKeyFrame[i] ? "Key frame" : "NOT a key frame");
83    }
84#endif
85}
86
87FrameSequence_webp::FrameSequence_webp(Stream* stream)
88        : mDemux(NULL)
89        , mIsKeyFrame(NULL)
90        , mRawByteBuffer(NULL) {
91    if (stream->getRawBuffer() != NULL) {
92        mData.size = stream->getRawBufferSize();
93        mData.bytes = stream->getRawBufferAddr();
94        mRawByteBuffer = stream->getRawBuffer();
95    } else {
96        // Read RIFF header to get file size.
97        uint8_t riff_header[RIFF_HEADER_SIZE];
98        if (stream->read(riff_header, RIFF_HEADER_SIZE) != RIFF_HEADER_SIZE) {
99            ALOGE("WebP header load failed");
100            return;
101        }
102        uint32_t readSize = GetLE32(riff_header + TAG_SIZE);
103        if (readSize > MAX_CHUNK_PAYLOAD) {
104            ALOGE("WebP got header size too large");
105            return;
106        }
107        mData.size = CHUNK_HEADER_SIZE + readSize;
108        if(mData.size < RIFF_HEADER_SIZE) {
109            ALOGE("WebP file malformed");
110            return;
111        }
112        mData.bytes = new uint8_t[mData.size];
113        memcpy((void*)mData.bytes, riff_header, RIFF_HEADER_SIZE);
114
115        // Read rest of the bytes.
116        void* remaining_bytes = (void*)(mData.bytes + RIFF_HEADER_SIZE);
117        size_t remaining_size = mData.size - RIFF_HEADER_SIZE;
118        if (stream->read(remaining_bytes, remaining_size) != remaining_size) {
119            ALOGE("WebP full load failed");
120            return;
121        }
122    }
123
124    // Construct demux.
125    mDemux = WebPDemux(&mData);
126    if (!mDemux) {
127        ALOGE("Parsing of WebP container file failed");
128        return;
129    }
130    mLoopCount = WebPDemuxGetI(mDemux, WEBP_FF_LOOP_COUNT);
131    mFormatFlags = WebPDemuxGetI(mDemux, WEBP_FF_FORMAT_FLAGS);
132#if WEBP_DEBUG
133    ALOGD("FrameSequence_webp created with size = %d x %d, number of frames = %d, flags = 0x%X",
134          getWidth(), getHeight(), getFrameCount(), mFormatFlags);
135#endif
136    constructDependencyChain();
137}
138
139FrameSequence_webp::~FrameSequence_webp() {
140    WebPDemuxDelete(mDemux);
141    delete[] mIsKeyFrame;
142    if (mRawByteBuffer == NULL) {
143        delete[] mData.bytes;
144    }
145}
146
147FrameSequenceState* FrameSequence_webp::createState() const {
148    return new FrameSequenceState_webp(*this);
149}
150
151////////////////////////////////////////////////////////////////////////////////
152// draw helpers
153////////////////////////////////////////////////////////////////////////////////
154
155static bool willBeCleared(const WebPIterator& iter) {
156    return iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND;
157}
158
159// return true if area of 'target' completely covers area of 'covered'
160static bool checkIfCover(const WebPIterator& target, const WebPIterator& covered) {
161    const int covered_x_max = covered.x_offset + covered.width;
162    const int target_x_max = target.x_offset + target.width;
163    const int covered_y_max = covered.y_offset + covered.height;
164    const int target_y_max = target.y_offset + target.height;
165    return target.x_offset <= covered.x_offset
166           && covered_x_max <= target_x_max
167           && target.y_offset <= covered.y_offset
168           && covered_y_max <= target_y_max;
169}
170
171// Clear all pixels in a line to transparent.
172static void clearLine(Color8888* dst, int width) {
173    memset(dst, 0, width * sizeof(*dst));  // Note: Assumes TRANSPARENT == 0x0.
174}
175
176// Copy all pixels from 'src' to 'dst'.
177static void copyFrame(const Color8888* src, int srcStride, Color8888* dst, int dstStride,
178        int width, int height) {
179    for (int y = 0; y < height; y++) {
180        memcpy(dst, src, width * sizeof(*dst));
181        src += srcStride;
182        dst += dstStride;
183    }
184}
185
186////////////////////////////////////////////////////////////////////////////////
187// Frame sequence state
188////////////////////////////////////////////////////////////////////////////////
189
190FrameSequenceState_webp::FrameSequenceState_webp(const FrameSequence_webp& frameSequence) :
191        mFrameSequence(frameSequence) {
192    WebPInitDecoderConfig(&mDecoderConfig);
193    mDecoderConfig.output.is_external_memory = 1;
194    mDecoderConfig.output.colorspace = MODE_rgbA;  // Pre-multiplied alpha mode.
195    const int canvasWidth = mFrameSequence.getWidth();
196    const int canvasHeight = mFrameSequence.getHeight();
197    mPreservedBuffer = new Color8888[canvasWidth * canvasHeight];
198}
199
200FrameSequenceState_webp::~FrameSequenceState_webp() {
201    delete[] mPreservedBuffer;
202}
203
204void FrameSequenceState_webp::initializeFrame(const WebPIterator& currIter, Color8888* currBuffer,
205        int currStride, const WebPIterator& prevIter, const Color8888* prevBuffer, int prevStride) {
206    const int canvasWidth = mFrameSequence.getWidth();
207    const int canvasHeight = mFrameSequence.getHeight();
208    const bool currFrameIsKeyFrame = mFrameSequence.isKeyFrame(currIter.frame_num - 1);
209
210    if (currFrameIsKeyFrame) {  // Clear canvas.
211        for (int y = 0; y < canvasHeight; y++) {
212            Color8888* dst = currBuffer + y * currStride;
213            clearLine(dst, canvasWidth);
214        }
215    } else {
216        // Preserve previous frame as starting state of current frame.
217        copyFrame(prevBuffer, prevStride, currBuffer, currStride, canvasWidth, canvasHeight);
218
219        // Dispose previous frame rectangle to Background if needed.
220        bool prevFrameCompletelyCovered =
221                (!currIter.has_alpha || currIter.blend_method == WEBP_MUX_NO_BLEND) &&
222                checkIfCover(currIter, prevIter);
223        if ((prevIter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) &&
224                !prevFrameCompletelyCovered) {
225            Color8888* dst = currBuffer + prevIter.x_offset + prevIter.y_offset * currStride;
226            for (int j = 0; j < prevIter.height; j++) {
227                clearLine(dst, prevIter.width);
228                dst += currStride;
229            }
230        }
231    }
232}
233
234bool FrameSequenceState_webp::decodeFrame(const WebPIterator& currIter, Color8888* currBuffer,
235        int currStride, const WebPIterator& prevIter, const Color8888* prevBuffer, int prevStride) {
236    Color8888* dst = currBuffer + currIter.x_offset + currIter.y_offset * currStride;
237    mDecoderConfig.output.u.RGBA.rgba = (uint8_t*)dst;
238    mDecoderConfig.output.u.RGBA.stride = currStride * 4;
239    mDecoderConfig.output.u.RGBA.size = mDecoderConfig.output.u.RGBA.stride * currIter.height;
240
241    const WebPData& currFrame = currIter.fragment;
242    if (WebPDecode(currFrame.bytes, currFrame.size, &mDecoderConfig) != VP8_STATUS_OK) {
243        return false;
244    }
245
246    const int canvasWidth = mFrameSequence.getWidth();
247    const int canvasHeight = mFrameSequence.getHeight();
248    const bool currFrameIsKeyFrame = mFrameSequence.isKeyFrame(currIter.frame_num - 1);
249    // During the decoding of current frame, we may have set some pixels to be transparent
250    // (i.e. alpha < 255). However, the value of each of these pixels should have been determined
251    // by blending it against the value of that pixel in the previous frame if WEBP_MUX_BLEND was
252    // specified. So, we correct these pixels based on disposal method of the previous frame and
253    // the previous frame buffer.
254    if (currIter.blend_method == WEBP_MUX_BLEND && !currFrameIsKeyFrame) {
255        if (prevIter.dispose_method == WEBP_MUX_DISPOSE_NONE) {
256            for (int y = 0; y < currIter.height; y++) {
257                const int canvasY = currIter.y_offset + y;
258                for (int x = 0; x < currIter.width; x++) {
259                    const int canvasX = currIter.x_offset + x;
260                    Color8888& currPixel = currBuffer[canvasY * currStride + canvasX];
261                    // FIXME: Use alpha-blending when alpha is between 0 and 255.
262                    if (!(currPixel & COLOR_8888_ALPHA_MASK)) {
263                        const Color8888 prevPixel = prevBuffer[canvasY * prevStride + canvasX];
264                        currPixel = prevPixel;
265                    }
266                }
267            }
268        } else {  // prevIter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND
269            // Need to restore transparent pixels to as they were just after frame initialization.
270            // That is:
271            //   * Transparent if it belongs to previous frame rectangle <-- This is a no-op.
272            //   * Pixel in the previous canvas otherwise <-- Need to restore.
273            for (int y = 0; y < currIter.height; y++) {
274                const int canvasY = currIter.y_offset + y;
275                for (int x = 0; x < currIter.width; x++) {
276                    const int canvasX = currIter.x_offset + x;
277                    Color8888& currPixel = currBuffer[canvasY * currStride + canvasX];
278                    // FIXME: Use alpha-blending when alpha is between 0 and 255.
279                    if (!(currPixel & COLOR_8888_ALPHA_MASK)
280                            && !FrameContainsPixel(prevIter, canvasX, canvasY)) {
281                        const Color8888 prevPixel = prevBuffer[canvasY * prevStride + canvasX];
282                        currPixel = prevPixel;
283                    }
284                }
285            }
286        }
287    }
288    return true;
289}
290
291long FrameSequenceState_webp::drawFrame(int frameNr,
292        Color8888* outputPtr, int outputPixelStride, int previousFrameNr) {
293    WebPDemuxer* demux = mFrameSequence.getDemuxer();
294    ALOG_ASSERT(demux, "Cannot drawFrame, mDemux is NULL");
295
296#if WEBP_DEBUG
297    ALOGD("  drawFrame called for frame# %d, previous frame# %d", frameNr, previousFrameNr);
298#endif
299
300    const int canvasWidth = mFrameSequence.getWidth();
301    const int canvasHeight = mFrameSequence.getHeight();
302
303    // Find the first frame to be decoded.
304    int start = max(previousFrameNr + 1, 0);
305    int earliestRequired = frameNr;
306    while (earliestRequired > start) {
307        if (mFrameSequence.isKeyFrame(earliestRequired)) {
308            start = earliestRequired;
309            break;
310        }
311        earliestRequired--;
312    }
313
314    WebPIterator currIter;
315    WebPIterator prevIter;
316    int ok = WebPDemuxGetFrame(demux, start, &currIter);  // Get frame number 'start - 1'.
317    ALOG_ASSERT(ok, "Could not retrieve frame# %d", start - 1);
318
319    // Use preserve buffer only if needed.
320    Color8888* prevBuffer = (frameNr == 0) ? outputPtr : mPreservedBuffer;
321    int prevStride = (frameNr == 0) ? outputPixelStride : canvasWidth;
322    Color8888* currBuffer = outputPtr;
323    int currStride = outputPixelStride;
324
325    for (int i = start; i <= frameNr; i++) {
326        prevIter = currIter;
327        ok = WebPDemuxGetFrame(demux, i + 1, &currIter);  // Get ith frame.
328        ALOG_ASSERT(ok, "Could not retrieve frame# %d", i);
329#if WEBP_DEBUG
330        ALOGD("      producing frame %d (has_alpha = %d, dispose = %s, blend = %s, duration = %d)",
331              i, currIter.has_alpha,
332              (currIter.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none" : "background",
333              (currIter.blend_method == WEBP_MUX_BLEND) ? "yes" : "no", currIter.duration);
334#endif
335        // We swap the prev/curr buffers as we go.
336        Color8888* tmpBuffer = prevBuffer;
337        prevBuffer = currBuffer;
338        currBuffer = tmpBuffer;
339
340        int tmpStride = prevStride;
341        prevStride = currStride;
342        currStride = tmpStride;
343
344#if WEBP_DEBUG
345        ALOGD("            prev = %p, curr = %p, out = %p, tmp = %p",
346              prevBuffer, currBuffer, outputPtr, mPreservedBuffer);
347#endif
348        // Process this frame.
349        initializeFrame(currIter, currBuffer, currStride, prevIter, prevBuffer, prevStride);
350
351        if (i == frameNr || !willBeCleared(currIter)) {
352            if (!decodeFrame(currIter, currBuffer, currStride, prevIter, prevBuffer, prevStride)) {
353                ALOGE("Error decoding frame# %d", i);
354                return -1;
355            }
356        }
357    }
358
359    if (outputPtr != currBuffer) {
360        copyFrame(currBuffer, currStride, outputPtr, outputPixelStride, canvasWidth, canvasHeight);
361    }
362
363    // Return last frame's delay.
364    const int frameCount = mFrameSequence.getFrameCount();
365    const int lastFrame = (frameNr + frameCount - 1) % frameCount;
366    ok = WebPDemuxGetFrame(demux, lastFrame, &currIter);
367    ALOG_ASSERT(ok, "Could not retrieve frame# %d", lastFrame - 1);
368    const int lastFrameDelay = currIter.duration;
369
370    WebPDemuxReleaseIterator(&currIter);
371    WebPDemuxReleaseIterator(&prevIter);
372
373    return lastFrameDelay;
374}
375
376////////////////////////////////////////////////////////////////////////////////
377// Registry
378////////////////////////////////////////////////////////////////////////////////
379
380#include "Registry.h"
381
382static bool isWebP(void* header, int header_size) {
383    const uint8_t* const header_str = (const uint8_t*)header;
384    return (header_size >= RIFF_HEADER_SIZE) &&
385            !memcmp("RIFF", header_str, 4) &&
386            !memcmp("WEBP", header_str + 8, 4);
387}
388
389static bool acceptsWebPBuffer() {
390    return true;
391}
392
393static FrameSequence* createFramesequence(Stream* stream) {
394    return new FrameSequence_webp(stream);
395}
396
397static RegistryEntry gEntry = {
398        RIFF_HEADER_SIZE,
399        isWebP,
400        createFramesequence,
401        NULL,
402        acceptsWebPBuffer,
403};
404static Registry gRegister(gEntry);
405
406