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     if (mGif->SColorMap) {
    115         // calculate bg color
    116         GraphicsControlBlock gcb;
    117         DGifSavedExtensionToGCB(mGif, 0, &gcb);
    118         if (gcb.TransparentColor == NO_TRANSPARENT_COLOR) {
    119             mBgColor = gifColorToColor8888(mGif->SColorMap->Colors[mGif->SBackGroundColor]);
    120         }
    121     }
    122 }
    123 
    124 FrameSequence_gif::~FrameSequence_gif() {
    125     if (mGif) {
    126         DGifCloseFile(mGif, NULL);
    127     }
    128     delete[] mPreservedFrames;
    129     delete[] mRestoringFrames;
    130 }
    131 
    132 FrameSequenceState* FrameSequence_gif::createState() const {
    133     return new FrameSequenceState_gif(*this);
    134 }
    135 
    136 ////////////////////////////////////////////////////////////////////////////////
    137 // draw helpers
    138 ////////////////////////////////////////////////////////////////////////////////
    139 
    140 // return true if area of 'target' is completely covers area of 'covered'
    141 static bool checkIfCover(const GifImageDesc& target, const GifImageDesc& covered) {
    142     return target.Left <= covered.Left
    143             && covered.Left + covered.Width <= target.Left + target.Width
    144             && target.Top <= covered.Top
    145             && covered.Top + covered.Height <= target.Top + target.Height;
    146 }
    147 
    148 static void copyLine(Color8888* dst, const unsigned char* src, const ColorMapObject* cmap,
    149                      int transparent, int width) {
    150     for (; width > 0; width--, src++, dst++) {
    151         if (*src != transparent) {
    152             *dst = gifColorToColor8888(cmap->Colors[*src]);
    153         }
    154     }
    155 }
    156 
    157 static void setLineColor(Color8888* dst, Color8888 color, int width) {
    158     for (; width > 0; width--, dst++) {
    159         *dst = color;
    160     }
    161 }
    162 
    163 static void getCopySize(const GifImageDesc& imageDesc, int maxWidth, int maxHeight,
    164         GifWord& copyWidth, GifWord& copyHeight) {
    165     copyWidth = imageDesc.Width;
    166     if (imageDesc.Left + copyWidth > maxWidth) {
    167         copyWidth = maxWidth - imageDesc.Left;
    168     }
    169     copyHeight = imageDesc.Height;
    170     if (imageDesc.Top + copyHeight > maxHeight) {
    171         copyHeight = maxHeight - imageDesc.Top;
    172     }
    173 }
    174 
    175 ////////////////////////////////////////////////////////////////////////////////
    176 // Frame sequence state
    177 ////////////////////////////////////////////////////////////////////////////////
    178 
    179 FrameSequenceState_gif::FrameSequenceState_gif(const FrameSequence_gif& frameSequence) :
    180     mFrameSequence(frameSequence), mPreserveBuffer(NULL), mPreserveBufferFrame(-1) {
    181 }
    182 
    183 FrameSequenceState_gif::~FrameSequenceState_gif() {
    184        delete[] mPreserveBuffer;
    185 }
    186 
    187 void FrameSequenceState_gif::savePreserveBuffer(Color8888* outputPtr, int outputPixelStride, int frameNr) {
    188     if (frameNr == mPreserveBufferFrame) return;
    189 
    190     mPreserveBufferFrame = frameNr;
    191     const int width = mFrameSequence.getWidth();
    192     const int height = mFrameSequence.getHeight();
    193     if (!mPreserveBuffer) {
    194         mPreserveBuffer = new Color8888[width * height];
    195     }
    196     for (int y = 0; y < height; y++) {
    197         memcpy(mPreserveBuffer + width * y,
    198                 outputPtr + outputPixelStride * y,
    199                 width * 4);
    200     }
    201 }
    202 
    203 void FrameSequenceState_gif::restorePreserveBuffer(Color8888* outputPtr, int outputPixelStride) {
    204     const int width = mFrameSequence.getWidth();
    205     const int height = mFrameSequence.getHeight();
    206     if (!mPreserveBuffer) {
    207         ALOGD("preserve buffer not allocated! ah!");
    208         return;
    209     }
    210     for (int y = 0; y < height; y++) {
    211         memcpy(outputPtr + outputPixelStride * y,
    212                 mPreserveBuffer + width * y,
    213                 width * 4);
    214     }
    215 }
    216 
    217 long FrameSequenceState_gif::drawFrame(int frameNr,
    218         Color8888* outputPtr, int outputPixelStride, int previousFrameNr) {
    219 
    220     GifFileType* gif = mFrameSequence.getGif();
    221     if (!gif) {
    222         ALOGD("Cannot drawFrame, mGif is NULL");
    223         return -1;
    224     }
    225 
    226 #if GIF_DEBUG
    227     ALOGD("      drawFrame on %p nr %d on addr %p, previous frame nr %d",
    228             this, frameNr, outputPtr, previousFrameNr);
    229 #endif
    230 
    231     const int height = mFrameSequence.getHeight();
    232     const int width = mFrameSequence.getWidth();
    233 
    234     GraphicsControlBlock gcb;
    235 
    236     int start = max(previousFrameNr + 1, 0);
    237 
    238     for (int i = max(start - 1, 0); i < frameNr; i++) {
    239         int neededPreservedFrame = mFrameSequence.getRestoringFrame(i);
    240         if (neededPreservedFrame >= 0 && (mPreserveBufferFrame != neededPreservedFrame)) {
    241 #if GIF_DEBUG
    242             ALOGD("frame %d needs frame %d preserved, but %d is currently, so drawing from scratch",
    243                     i, neededPreservedFrame, mPreserveBufferFrame);
    244 #endif
    245             start = 0;
    246         }
    247     }
    248 
    249     for (int i = start; i <= frameNr; i++) {
    250         DGifSavedExtensionToGCB(gif, i, &gcb);
    251         const SavedImage& frame = gif->SavedImages[i];
    252 
    253 #if GIF_DEBUG
    254         bool frameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR;
    255         ALOGD("producing frame %d, drawing frame %d (opaque %d, disp %d, del %d)",
    256                 frameNr, i, frameOpaque, gcb.DisposalMode, gcb.DelayTime);
    257 #endif
    258         if (i == 0) {
    259             //clear bitmap
    260             Color8888 bgColor = mFrameSequence.getBackgroundColor();
    261             for (int y = 0; y < height; y++) {
    262                 for (int x = 0; x < width; x++) {
    263                     outputPtr[y * outputPixelStride + x] = bgColor;
    264                 }
    265             }
    266         } else {
    267             GraphicsControlBlock prevGcb;
    268             DGifSavedExtensionToGCB(gif, i - 1, &prevGcb);
    269             const SavedImage& prevFrame = gif->SavedImages[i - 1];
    270             bool prevFrameDisposed = willBeCleared(prevGcb);
    271 
    272             bool newFrameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR;
    273             bool prevFrameCompletelyCovered = newFrameOpaque
    274                     && checkIfCover(frame.ImageDesc, prevFrame.ImageDesc);
    275 
    276             if (prevFrameDisposed && !prevFrameCompletelyCovered) {
    277                 switch (prevGcb.DisposalMode) {
    278                 case DISPOSE_BACKGROUND: {
    279                     Color8888* dst = outputPtr + prevFrame.ImageDesc.Left +
    280                             prevFrame.ImageDesc.Top * outputPixelStride;
    281 
    282                     GifWord copyWidth, copyHeight;
    283                     getCopySize(prevFrame.ImageDesc, width, height, copyWidth, copyHeight);
    284                     for (; copyHeight > 0; copyHeight--) {
    285                         setLineColor(dst, TRANSPARENT, copyWidth);
    286                         dst += outputPixelStride;
    287                     }
    288                 } break;
    289                 case DISPOSE_PREVIOUS: {
    290                     restorePreserveBuffer(outputPtr, outputPixelStride);
    291                 } break;
    292                 }
    293             }
    294 
    295             if (mFrameSequence.getPreservedFrame(i - 1)) {
    296                 // currently drawn frame will be restored by a following DISPOSE_PREVIOUS draw, so
    297                 // we preserve it
    298                 savePreserveBuffer(outputPtr, outputPixelStride, i - 1);
    299             }
    300         }
    301 
    302         bool willBeCleared = gcb.DisposalMode == DISPOSE_BACKGROUND
    303                 || gcb.DisposalMode == DISPOSE_PREVIOUS;
    304         if (i == frameNr || !willBeCleared) {
    305             const ColorMapObject* cmap = gif->SColorMap;
    306             if (frame.ImageDesc.ColorMap) {
    307                 cmap = frame.ImageDesc.ColorMap;
    308             }
    309 
    310             if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
    311                 ALOGW("Warning: potentially corrupt color map");
    312             }
    313 
    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     // return last frame's delay
    328     const int maxFrame = gif->ImageCount;
    329     const int lastFrame = (frameNr + maxFrame - 1) % maxFrame;
    330     DGifSavedExtensionToGCB(gif, lastFrame, &gcb);
    331     return getDelayMs(gcb);
    332 }
    333 
    334 ////////////////////////////////////////////////////////////////////////////////
    335 // Registry
    336 ////////////////////////////////////////////////////////////////////////////////
    337 
    338 #include "Registry.h"
    339 
    340 static bool isGif(void* header, int header_size) {
    341     return !memcmp(GIF_STAMP, header, GIF_STAMP_LEN)
    342             || !memcmp(GIF87_STAMP, header, GIF_STAMP_LEN)
    343             || !memcmp(GIF89_STAMP, header, GIF_STAMP_LEN);
    344 }
    345 
    346 static bool acceptsBuffers() {
    347     return false;
    348 }
    349 
    350 static FrameSequence* createFramesequence(Stream* stream) {
    351     return new FrameSequence_gif(stream);
    352 }
    353 
    354 static RegistryEntry gEntry = {
    355         GIF_STAMP_LEN,
    356         isGif,
    357         createFramesequence,
    358         NULL,
    359         acceptsBuffers,
    360 };
    361 static Registry gRegister(gEntry);
    362 
    363