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