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 
     22 #include "FrameSequence_gif.h"
     23 
     24 #define GIF_DEBUG 0
     25 
     26 static int streamReader(GifFileType* fileType, GifByteType* out, int size) {
     27     Stream* stream = (Stream*) fileType->UserData;
     28     return (int) stream->read(out, size);
     29 }
     30 
     31 static Color8888 gifColorToColor8888(const GifColorType& color) {
     32     return ARGB_TO_COLOR8888(0xff, color.Red, color.Green, color.Blue);
     33 }
     34 
     35 static long getDelayMs(GraphicsControlBlock& gcb) {
     36     return gcb.DelayTime * 10;
     37 }
     38 
     39 static bool willBeCleared(const GraphicsControlBlock& gcb) {
     40     return gcb.DisposalMode == DISPOSE_BACKGROUND || gcb.DisposalMode == DISPOSE_PREVIOUS;
     41 }
     42 
     43 ////////////////////////////////////////////////////////////////////////////////
     44 // Frame sequence
     45 ////////////////////////////////////////////////////////////////////////////////
     46 
     47 FrameSequence_gif::FrameSequence_gif(Stream* stream) :
     48         mLoopCount(1), mBgColor(TRANSPARENT), mPreservedFrames(NULL), mRestoringFrames(NULL) {
     49     mGif = DGifOpen(stream, streamReader, NULL);
     50     if (!mGif) {
     51         ALOGW("Gif load failed");
     52         return;
     53     }
     54 
     55     if (DGifSlurp(mGif) != GIF_OK) {
     56         ALOGW("Gif slurp failed");
     57         DGifCloseFile(mGif, NULL);
     58         mGif = NULL;
     59         return;
     60     }
     61 
     62     long durationMs = 0;
     63     int lastUnclearedFrame = -1;
     64     mPreservedFrames = new bool[mGif->ImageCount];
     65     mRestoringFrames = new int[mGif->ImageCount];
     66 
     67     GraphicsControlBlock gcb;
     68     for (int i = 0; i < mGif->ImageCount; i++) {
     69         const SavedImage& image = mGif->SavedImages[i];
     70 
     71         // find the loop extension pair
     72         for (int j = 0; (j + 1) < image.ExtensionBlockCount; j++) {
     73             ExtensionBlock* eb1 = image.ExtensionBlocks + j;
     74             ExtensionBlock* eb2 = image.ExtensionBlocks + j + 1;
     75             if (eb1->Function == APPLICATION_EXT_FUNC_CODE
     76                     // look for "NETSCAPE2.0" app extension
     77                     && eb1->ByteCount == 11
     78                     && !memcmp((const char*)(eb1->Bytes), "NETSCAPE2.0", 11)
     79                     // verify extension contents and get loop count
     80                     && eb2->Function == CONTINUE_EXT_FUNC_CODE
     81                     && eb2->ByteCount == 3
     82                     && eb2->Bytes[0] == 1) {
     83                 mLoopCount = (int)(eb2->Bytes[2] << 8) + (int)(eb2->Bytes[1]);
     84             }
     85         }
     86 
     87         DGifSavedExtensionToGCB(mGif, i, &gcb);
     88 
     89         // timing
     90         durationMs += getDelayMs(gcb);
     91 
     92         // preserve logic
     93         mPreservedFrames[i] = false;
     94         mRestoringFrames[i] = -1;
     95         if (gcb.DisposalMode == DISPOSE_PREVIOUS && lastUnclearedFrame >= 0) {
     96             mPreservedFrames[lastUnclearedFrame] = true;
     97             mRestoringFrames[i] = lastUnclearedFrame;
     98         }
     99         if (!willBeCleared(gcb)) {
    100             lastUnclearedFrame = i;
    101         }
    102     }
    103 
    104 #if GIF_DEBUG
    105     ALOGD("FrameSequence_gif created with size %d %d, frames %d dur %ld",
    106             mGif->SWidth, mGif->SHeight, mGif->ImageCount, durationMs);
    107     for (int i = 0; i < mGif->ImageCount; i++) {
    108         DGifSavedExtensionToGCB(mGif, i, &gcb);
    109         ALOGD("    Frame %d - must preserve %d, restore point %d, trans color %d",
    110                 i, mPreservedFrames[i], mRestoringFrames[i], gcb.TransparentColor);
    111     }
    112 #endif
    113 
    114     const ColorMapObject* cmap = mGif->SColorMap;
    115     if (cmap) {
    116         // calculate bg color
    117         GraphicsControlBlock gcb;
    118         DGifSavedExtensionToGCB(mGif, 0, &gcb);
    119         if (gcb.TransparentColor == NO_TRANSPARENT_COLOR
    120                 && mGif->SBackGroundColor < cmap->ColorCount) {
    121             mBgColor = gifColorToColor8888(cmap->Colors[mGif->SBackGroundColor]);
    122         }
    123     }
    124 }
    125 
    126 FrameSequence_gif::~FrameSequence_gif() {
    127     if (mGif) {
    128         DGifCloseFile(mGif, NULL);
    129     }
    130     delete[] mPreservedFrames;
    131     delete[] mRestoringFrames;
    132 }
    133 
    134 FrameSequenceState* FrameSequence_gif::createState() const {
    135     return new FrameSequenceState_gif(*this);
    136 }
    137 
    138 ////////////////////////////////////////////////////////////////////////////////
    139 // draw helpers
    140 ////////////////////////////////////////////////////////////////////////////////
    141 
    142 // return true if area of 'target' is completely covers area of 'covered'
    143 static bool checkIfCover(const GifImageDesc& target, const GifImageDesc& covered) {
    144     return target.Left <= covered.Left
    145             && covered.Left + covered.Width <= target.Left + target.Width
    146             && target.Top <= covered.Top
    147             && covered.Top + covered.Height <= target.Top + target.Height;
    148 }
    149 
    150 static void copyLine(Color8888* dst, const unsigned char* src, const ColorMapObject* cmap,
    151                      int transparent, int width) {
    152     for (; width > 0; width--, src++, dst++) {
    153         if (*src != transparent && *src < cmap->ColorCount) {
    154             *dst = gifColorToColor8888(cmap->Colors[*src]);
    155         }
    156     }
    157 }
    158 
    159 static void setLineColor(Color8888* dst, Color8888 color, int width) {
    160     for (; width > 0; width--, dst++) {
    161         *dst = color;
    162     }
    163 }
    164 
    165 static void getCopySize(const GifImageDesc& imageDesc, int maxWidth, int maxHeight,
    166         GifWord& copyWidth, GifWord& copyHeight) {
    167     copyWidth = imageDesc.Width;
    168     if (imageDesc.Left + copyWidth > maxWidth) {
    169         copyWidth = maxWidth - imageDesc.Left;
    170     }
    171     copyHeight = imageDesc.Height;
    172     if (imageDesc.Top + copyHeight > maxHeight) {
    173         copyHeight = maxHeight - imageDesc.Top;
    174     }
    175 }
    176 
    177 ////////////////////////////////////////////////////////////////////////////////
    178 // Frame sequence state
    179 ////////////////////////////////////////////////////////////////////////////////
    180 
    181 FrameSequenceState_gif::FrameSequenceState_gif(const FrameSequence_gif& frameSequence) :
    182     mFrameSequence(frameSequence), mPreserveBuffer(NULL), mPreserveBufferFrame(-1) {
    183 }
    184 
    185 FrameSequenceState_gif::~FrameSequenceState_gif() {
    186        delete[] mPreserveBuffer;
    187 }
    188 
    189 void FrameSequenceState_gif::savePreserveBuffer(Color8888* outputPtr, int outputPixelStride, int frameNr) {
    190     if (frameNr == mPreserveBufferFrame) return;
    191 
    192     mPreserveBufferFrame = frameNr;
    193     const int width = mFrameSequence.getWidth();
    194     const int height = mFrameSequence.getHeight();
    195     if (!mPreserveBuffer) {
    196         mPreserveBuffer = new Color8888[width * height];
    197     }
    198     for (int y = 0; y < height; y++) {
    199         memcpy(mPreserveBuffer + width * y,
    200                 outputPtr + outputPixelStride * y,
    201                 width * 4);
    202     }
    203 }
    204 
    205 void FrameSequenceState_gif::restorePreserveBuffer(Color8888* outputPtr, int outputPixelStride) {
    206     const int width = mFrameSequence.getWidth();
    207     const int height = mFrameSequence.getHeight();
    208     if (!mPreserveBuffer) {
    209         ALOGD("preserve buffer not allocated! ah!");
    210         return;
    211     }
    212     for (int y = 0; y < height; y++) {
    213         memcpy(outputPtr + outputPixelStride * y,
    214                 mPreserveBuffer + width * y,
    215                 width * 4);
    216     }
    217 }
    218 
    219 long FrameSequenceState_gif::drawFrame(int frameNr,
    220         Color8888* outputPtr, int outputPixelStride, int previousFrameNr) {
    221 
    222     GifFileType* gif = mFrameSequence.getGif();
    223     if (!gif) {
    224         ALOGD("Cannot drawFrame, mGif is NULL");
    225         return -1;
    226     }
    227 
    228 #if GIF_DEBUG
    229     ALOGD("      drawFrame on %p nr %d on addr %p, previous frame nr %d",
    230             this, frameNr, outputPtr, previousFrameNr);
    231 #endif
    232 
    233     const int height = mFrameSequence.getHeight();
    234     const int width = mFrameSequence.getWidth();
    235 
    236     GraphicsControlBlock gcb;
    237 
    238     int start = max(previousFrameNr + 1, 0);
    239 
    240     for (int i = max(start - 1, 0); i < frameNr; i++) {
    241         int neededPreservedFrame = mFrameSequence.getRestoringFrame(i);
    242         if (neededPreservedFrame >= 0 && (mPreserveBufferFrame != neededPreservedFrame)) {
    243 #if GIF_DEBUG
    244             ALOGD("frame %d needs frame %d preserved, but %d is currently, so drawing from scratch",
    245                     i, neededPreservedFrame, mPreserveBufferFrame);
    246 #endif
    247             start = 0;
    248         }
    249     }
    250 
    251     for (int i = start; i <= frameNr; i++) {
    252         DGifSavedExtensionToGCB(gif, i, &gcb);
    253         const SavedImage& frame = gif->SavedImages[i];
    254 
    255 #if GIF_DEBUG
    256         bool frameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR;
    257         ALOGD("producing frame %d, drawing frame %d (opaque %d, disp %d, del %d)",
    258                 frameNr, i, frameOpaque, gcb.DisposalMode, gcb.DelayTime);
    259 #endif
    260         if (i == 0) {
    261             //clear bitmap
    262             Color8888 bgColor = mFrameSequence.getBackgroundColor();
    263             for (int y = 0; y < height; y++) {
    264                 for (int x = 0; x < width; x++) {
    265                     outputPtr[y * outputPixelStride + x] = bgColor;
    266                 }
    267             }
    268         } else {
    269             GraphicsControlBlock prevGcb;
    270             DGifSavedExtensionToGCB(gif, i - 1, &prevGcb);
    271             const SavedImage& prevFrame = gif->SavedImages[i - 1];
    272             bool prevFrameDisposed = willBeCleared(prevGcb);
    273 
    274             bool newFrameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR;
    275             bool prevFrameCompletelyCovered = newFrameOpaque
    276                     && checkIfCover(frame.ImageDesc, prevFrame.ImageDesc);
    277 
    278             if (prevFrameDisposed && !prevFrameCompletelyCovered) {
    279                 switch (prevGcb.DisposalMode) {
    280                 case DISPOSE_BACKGROUND: {
    281                     Color8888* dst = outputPtr + prevFrame.ImageDesc.Left +
    282                             prevFrame.ImageDesc.Top * outputPixelStride;
    283 
    284                     GifWord copyWidth, copyHeight;
    285                     getCopySize(prevFrame.ImageDesc, width, height, copyWidth, copyHeight);
    286                     for (; copyHeight > 0; copyHeight--) {
    287                         setLineColor(dst, TRANSPARENT, copyWidth);
    288                         dst += outputPixelStride;
    289                     }
    290                 } break;
    291                 case DISPOSE_PREVIOUS: {
    292                     restorePreserveBuffer(outputPtr, outputPixelStride);
    293                 } break;
    294                 }
    295             }
    296 
    297             if (mFrameSequence.getPreservedFrame(i - 1)) {
    298                 // currently drawn frame will be restored by a following DISPOSE_PREVIOUS draw, so
    299                 // we preserve it
    300                 savePreserveBuffer(outputPtr, outputPixelStride, i - 1);
    301             }
    302         }
    303 
    304         bool willBeCleared = gcb.DisposalMode == DISPOSE_BACKGROUND
    305                 || gcb.DisposalMode == DISPOSE_PREVIOUS;
    306         if (i == frameNr || !willBeCleared) {
    307             const ColorMapObject* cmap = gif->SColorMap;
    308             if (frame.ImageDesc.ColorMap) {
    309                 cmap = frame.ImageDesc.ColorMap;
    310             }
    311 
    312             // If a cmap is missing, the frame can't be decoded, so we skip it.
    313             if (cmap) {
    314                 const unsigned char* src = (unsigned char*)frame.RasterBits;
    315                 Color8888* dst = outputPtr + frame.ImageDesc.Left +
    316                         frame.ImageDesc.Top * outputPixelStride;
    317                 GifWord copyWidth, copyHeight;
    318                 getCopySize(frame.ImageDesc, width, height, copyWidth, copyHeight);
    319                 for (; copyHeight > 0; copyHeight--) {
    320                     copyLine(dst, src, cmap, gcb.TransparentColor, copyWidth);
    321                     src += frame.ImageDesc.Width;
    322                     dst += outputPixelStride;
    323                 }
    324             }
    325         }
    326     }
    327 
    328     // return last frame's delay
    329     const int maxFrame = gif->ImageCount;
    330     const int lastFrame = (frameNr + maxFrame - 1) % maxFrame;
    331     DGifSavedExtensionToGCB(gif, lastFrame, &gcb);
    332     return getDelayMs(gcb);
    333 }
    334 
    335 ////////////////////////////////////////////////////////////////////////////////
    336 // Registry
    337 ////////////////////////////////////////////////////////////////////////////////
    338 
    339 #include "Registry.h"
    340 
    341 static bool isGif(void* header, int header_size) {
    342     return !memcmp(GIF_STAMP, header, GIF_STAMP_LEN)
    343             || !memcmp(GIF87_STAMP, header, GIF_STAMP_LEN)
    344             || !memcmp(GIF89_STAMP, header, GIF_STAMP_LEN);
    345 }
    346 
    347 static bool acceptsBuffers() {
    348     return false;
    349 }
    350 
    351 static FrameSequence* createFramesequence(Stream* stream) {
    352     return new FrameSequence_gif(stream);
    353 }
    354 
    355 static RegistryEntry gEntry = {
    356         GIF_STAMP_LEN,
    357         isGif,
    358         createFramesequence,
    359         NULL,
    360         acceptsBuffers,
    361 };
    362 static Registry gRegister(gEntry);
    363