• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetLE32(const uint8_t * const data)31 static 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.
isFullFrame(const WebPIterator & frame,int canvasWidth,int canvasHeight)36 static 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).
FrameContainsPixel(const WebPIterator & frame,int x,int y)41 static 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.
constructDependencyChain()50 void 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 
FrameSequence_webp(Stream * stream)87 FrameSequence_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 
~FrameSequence_webp()139 FrameSequence_webp::~FrameSequence_webp() {
140     WebPDemuxDelete(mDemux);
141     delete[] mIsKeyFrame;
142     if (mRawByteBuffer == NULL) {
143         delete[] mData.bytes;
144     }
145 }
146 
createState() const147 FrameSequenceState* FrameSequence_webp::createState() const {
148     return new FrameSequenceState_webp(*this);
149 }
150 
151 ////////////////////////////////////////////////////////////////////////////////
152 // draw helpers
153 ////////////////////////////////////////////////////////////////////////////////
154 
willBeCleared(const WebPIterator & iter)155 static 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'
checkIfCover(const WebPIterator & target,const WebPIterator & covered)160 static 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.
clearLine(Color8888 * dst,int width)172 static 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'.
copyFrame(const Color8888 * src,int srcStride,Color8888 * dst,int dstStride,int width,int height)177 static 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 
FrameSequenceState_webp(const FrameSequence_webp & frameSequence)190 FrameSequenceState_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 
~FrameSequenceState_webp()200 FrameSequenceState_webp::~FrameSequenceState_webp() {
201     delete[] mPreservedBuffer;
202 }
203 
initializeFrame(const WebPIterator & currIter,Color8888 * currBuffer,int currStride,const WebPIterator & prevIter,const Color8888 * prevBuffer,int prevStride)204 void 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 
decodeFrame(const WebPIterator & currIter,Color8888 * currBuffer,int currStride,const WebPIterator & prevIter,const Color8888 * prevBuffer,int prevStride)234 bool 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 
drawFrame(int frameNr,Color8888 * outputPtr,int outputPixelStride,int previousFrameNr)291 long 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 + 1, &currIter);
367     ALOG_ASSERT(ok, "Could not retrieve frame# %d", lastFrame);
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 
isWebP(void * header,int header_size)382 static 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 
acceptsWebPBuffer()389 static bool acceptsWebPBuffer() {
390     return true;
391 }
392 
createFramesequence(Stream * stream)393 static FrameSequence* createFramesequence(Stream* stream) {
394     return new FrameSequence_webp(stream);
395 }
396 
397 static RegistryEntry gEntry = {
398         RIFF_HEADER_SIZE,
399         isWebP,
400         createFramesequence,
401         NULL,
402         acceptsWebPBuffer,
403 };
404 static Registry gRegister(gEntry);
405 
406