Home | History | Annotate | Download | only in jni
      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 
     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.
     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).
     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.
     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 
     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 
    139 FrameSequence_webp::~FrameSequence_webp() {
    140     WebPDemuxDelete(mDemux);
    141     delete[] mIsKeyFrame;
    142     if (mRawByteBuffer == NULL) {
    143         delete[] mData.bytes;
    144     }
    145 }
    146 
    147 FrameSequenceState* FrameSequence_webp::createState() const {
    148     return new FrameSequenceState_webp(*this);
    149 }
    150 
    151 ////////////////////////////////////////////////////////////////////////////////
    152 // draw helpers
    153 ////////////////////////////////////////////////////////////////////////////////
    154 
    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'
    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.
    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'.
    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 
    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 
    200 FrameSequenceState_webp::~FrameSequenceState_webp() {
    201     delete[] mPreservedBuffer;
    202 }
    203 
    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 
    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 
    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 
    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 
    389 static bool acceptsWebPBuffer() {
    390     return true;
    391 }
    392 
    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