gfx/skia/trunk/src/images/SkImageDecoder_libgif.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /*
     2  * Copyright 2006 The Android Open Source Project
     3  *
     4  * Use of this source code is governed by a BSD-style license that can be
     5  * found in the LICENSE file.
     6  */
     8 #include "SkColor.h"
     9 #include "SkColorPriv.h"
    10 #include "SkColorTable.h"
    11 #include "SkImageDecoder.h"
    12 #include "SkRTConf.h"
    13 #include "SkScaledBitmapSampler.h"
    14 #include "SkStream.h"
    15 #include "SkTemplates.h"
    16 #include "SkUtils.h"
    18 #include "gif_lib.h"
    20 class SkGIFImageDecoder : public SkImageDecoder {
    21 public:
    22     virtual Format getFormat() const SK_OVERRIDE {
    23         return kGIF_Format;
    24     }
    26 protected:
    27     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
    29 private:
    30     typedef SkImageDecoder INHERITED;
    31 };
    33 static const uint8_t gStartingIterlaceYValue[] = {
    34     0, 4, 2, 1
    35 };
    36 static const uint8_t gDeltaIterlaceYValue[] = {
    37     8, 8, 4, 2
    38 };
    40 SK_CONF_DECLARE(bool, c_suppressGIFImageDecoderWarnings,
    41                 "images.gif.suppressDecoderWarnings", true,
    42                 "Suppress GIF warnings and errors when calling image decode "
    43                 "functions.");
    46 /*  Implement the GIF interlace algorithm in an iterator.
    47     1) grab every 8th line beginning at 0
    48     2) grab every 8th line beginning at 4
    49     3) grab every 4th line beginning at 2
    50     4) grab every 2nd line beginning at 1
    51 */
    52 class GifInterlaceIter {
    53 public:
    54     GifInterlaceIter(int height) : fHeight(height) {
    55         fStartYPtr = gStartingIterlaceYValue;
    56         fDeltaYPtr = gDeltaIterlaceYValue;
    58         fCurrY = *fStartYPtr++;
    59         fDeltaY = *fDeltaYPtr++;
    60     }
    62     int currY() const {
    63         SkASSERT(fStartYPtr);
    64         SkASSERT(fDeltaYPtr);
    65         return fCurrY;
    66     }
    68     void next() {
    69         SkASSERT(fStartYPtr);
    70         SkASSERT(fDeltaYPtr);
    72         int y = fCurrY + fDeltaY;
    73         // We went from an if statement to a while loop so that we iterate
    74         // through fStartYPtr until a valid row is found. This is so that images
    75         // that are smaller than 5x5 will not trash memory.
    76         while (y >= fHeight) {
    77             if (gStartingIterlaceYValue +
    78                     SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
    79                 // we done
    80                 SkDEBUGCODE(fStartYPtr = NULL;)
    81                 SkDEBUGCODE(fDeltaYPtr = NULL;)
    82                 y = 0;
    83             } else {
    84                 y = *fStartYPtr++;
    85                 fDeltaY = *fDeltaYPtr++;
    86             }
    87         }
    88         fCurrY = y;
    89     }
    91 private:
    92     const int fHeight;
    93     int fCurrY;
    94     int fDeltaY;
    95     const uint8_t* fStartYPtr;
    96     const uint8_t* fDeltaYPtr;
    97 };
    99 ///////////////////////////////////////////////////////////////////////////////
   101 static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
   102                               int size) {
   103     SkStream* stream = (SkStream*) fileType->UserData;
   104     return (int) stream->read(out, size);
   105 }
   107 void CheckFreeExtension(SavedImage* Image) {
   108     if (Image->ExtensionBlocks) {
   109 #if GIFLIB_MAJOR < 5
   110         FreeExtension(Image);
   111 #else
   112         GifFreeExtensions(&Image->ExtensionBlockCount, &Image->ExtensionBlocks);
   113 #endif
   114     }
   115 }
   117 // return NULL on failure
   118 static const ColorMapObject* find_colormap(const GifFileType* gif) {
   119     const ColorMapObject* cmap = gif->Image.ColorMap;
   120     if (NULL == cmap) {
   121         cmap = gif->SColorMap;
   122     }
   124     if (NULL == cmap) {
   125         // no colormap found
   126         return NULL;
   127     }
   128     // some sanity checks
   129     if (cmap && ((unsigned)cmap->ColorCount > 256 ||
   130                  cmap->ColorCount != (1 << cmap->BitsPerPixel))) {
   131         cmap = NULL;
   132     }
   133     return cmap;
   134 }
   136 // return -1 if not found (i.e. we're completely opaque)
   137 static int find_transpIndex(const SavedImage& image, int colorCount) {
   138     int transpIndex = -1;
   139     for (int i = 0; i < image.ExtensionBlockCount; ++i) {
   140         const ExtensionBlock* eb = image.ExtensionBlocks + i;
   141         if (eb->Function == 0xF9 && eb->ByteCount == 4) {
   142             if (eb->Bytes[0] & 1) {
   143                 transpIndex = (unsigned char)eb->Bytes[3];
   144                 // check for valid transpIndex
   145                 if (transpIndex >= colorCount) {
   146                     transpIndex = -1;
   147                 }
   148                 break;
   149             }
   150         }
   151     }
   152     return transpIndex;
   153 }
   155 static bool error_return(const SkBitmap& bm, const char msg[]) {
   156     if (!c_suppressGIFImageDecoderWarnings) {
   157         SkDebugf("libgif error [%s] bitmap [%d %d] pixels %p colortable %p\n",
   158                  msg, bm.width(), bm.height(), bm.getPixels(),
   159                  bm.getColorTable());
   160     }
   161     return false;
   162 }
   163 static void gif_warning(const SkBitmap& bm, const char msg[]) {
   164     if (!c_suppressGIFImageDecoderWarnings) {
   165         SkDebugf("libgif warning [%s] bitmap [%d %d] pixels %p colortable %p\n",
   166                  msg, bm.width(), bm.height(), bm.getPixels(),
   167                  bm.getColorTable());
   168     }
   169 }
   171 /**
   172  *  Skip rows in the source gif image.
   173  *  @param gif Source image.
   174  *  @param dst Scratch output needed by gif library call. Must be >= width bytes.
   175  *  @param width Bytes per row in the source image.
   176  *  @param rowsToSkip Number of rows to skip.
   177  *  @return True on success, false on GIF_ERROR.
   178  */
   179 static bool skip_src_rows(GifFileType* gif, uint8_t* dst, int width, int rowsToSkip) {
   180     for (int i = 0; i < rowsToSkip; i++) {
   181         if (DGifGetLine(gif, dst, width) == GIF_ERROR) {
   182             return false;
   183         }
   184     }
   185     return true;
   186 }
   188 /**
   189  *  GIFs with fewer then 256 color entries will sometimes index out of
   190  *  bounds of the color table (this is malformed, but libgif does not
   191  *  check sicne it is rare).  This function checks for this error and
   192  *  fixes it.  This makes the output image consistantly deterministic.
   193  */
   194 static void sanitize_indexed_bitmap(SkBitmap* bm) {
   195     if ((SkBitmap::kIndex8_Config == bm->config()) && !(bm->empty())) {
   196         SkAutoLockPixels alp(*bm);
   197         if (NULL != bm->getPixels()) {
   198             SkColorTable* ct = bm->getColorTable();  // Index8 must have it.
   199             SkASSERT(ct != NULL);
   200             uint32_t count = ct->count();
   201             SkASSERT(count > 0);
   202             SkASSERT(count <= 0x100);
   203             if (count != 0x100) {  // Full colortables can't go wrong.
   204                 // Count is a power of 2; asserted elsewhere.
   205                 uint8_t byteMask = (~(count - 1));
   206                 bool warning = false;
   207                 uint8_t* addr = static_cast<uint8_t*>(bm->getPixels());
   208                 int height = bm->height();
   209                 int width = bm->width();
   210                 size_t rowBytes = bm->rowBytes();
   211                 while (--height >= 0) {
   212                     uint8_t* ptr = addr;
   213                     int x = width;
   214                     while (--x >= 0) {
   215                         if (0 != ((*ptr) & byteMask)) {
   216                             warning = true;
   217                             *ptr = 0;
   218                         }
   219                         ++ptr;
   220                     }
   221                     addr += rowBytes;
   222                 }
   223                 if (warning) {
   224                     gif_warning(*bm, "Index out of bounds.");
   225                 }
   226             }
   227         }
   228     }
   229 }
   231 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
   232 #if GIFLIB_MAJOR < 5
   233     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
   234 #else
   235     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);
   236 #endif
   237     if (NULL == gif) {
   238         return error_return(*bm, "DGifOpen");
   239     }
   241     SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);
   243     SavedImage temp_save;
   244     temp_save.ExtensionBlocks=NULL;
   245     temp_save.ExtensionBlockCount=0;
   246     SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);
   248     int width, height;
   249     GifRecordType recType;
   250     GifByteType *extData;
   251 #if GIFLIB_MAJOR >= 5
   252     int extFunction;
   253 #endif
   254     int transpIndex = -1;   // -1 means we don't have it (yet)
   255     int fillIndex = gif->SBackGroundColor;
   257     do {
   258         if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
   259             return error_return(*bm, "DGifGetRecordType");
   260         }
   262         switch (recType) {
   263         case IMAGE_DESC_RECORD_TYPE: {
   264             if (DGifGetImageDesc(gif) == GIF_ERROR) {
   265                 return error_return(*bm, "IMAGE_DESC_RECORD_TYPE");
   266             }
   268             if (gif->ImageCount < 1) {    // sanity check
   269                 return error_return(*bm, "ImageCount < 1");
   270             }
   272             width = gif->SWidth;
   273             height = gif->SHeight;
   275             SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
   276             const GifImageDesc& desc = image->ImageDesc;
   278             int imageLeft = desc.Left;
   279             int imageTop = desc.Top;
   280             const int innerWidth = desc.Width;
   281             const int innerHeight = desc.Height;
   282             if (innerWidth <= 0 || innerHeight <= 0) {
   283                 return error_return(*bm, "invalid dimensions");
   284             }
   286             // check for valid descriptor
   287             if (innerWidth > width) {
   288                 gif_warning(*bm, "image too wide, expanding output to size");
   289                 width = innerWidth;
   290                 imageLeft = 0;
   291             } else if (imageLeft + innerWidth > width) {
   292                 gif_warning(*bm, "shifting image left to fit");
   293                 imageLeft = width - innerWidth;
   294             } else if (imageLeft < 0) {
   295                 gif_warning(*bm, "shifting image right to fit");
   296                 imageLeft = 0;
   297             }
   300             if (innerHeight > height) {
   301                 gif_warning(*bm, "image too tall,  expanding output to size");
   302                 height = innerHeight;
   303                 imageTop = 0;
   304             } else if (imageTop + innerHeight > height) {
   305                 gif_warning(*bm, "shifting image up to fit");
   306                 imageTop = height - innerHeight;
   307             } else if (imageTop < 0) {
   308                 gif_warning(*bm, "shifting image down to fit");
   309                 imageTop = 0;
   310             }
   312             // FIXME: We could give the caller a choice of images or configs.
   313             if (!this->chooseFromOneChoice(SkBitmap::kIndex8_Config, width, height)) {
   314                 return error_return(*bm, "chooseFromOneChoice");
   315             }
   317             SkScaledBitmapSampler sampler(width, height, this->getSampleSize());
   319             bm->setConfig(SkBitmap::kIndex8_Config, sampler.scaledWidth(),
   320                           sampler.scaledHeight());
   322             if (SkImageDecoder::kDecodeBounds_Mode == mode) {
   323                 return true;
   324             }
   327             // now we decode the colortable
   328             int colorCount = 0;
   329             {
   330                 // Declare colorPtr here for scope.
   331                 SkPMColor colorPtr[256]; // storage for worst-case
   332                 const ColorMapObject* cmap = find_colormap(gif);
   333                 SkAlphaType alphaType = kOpaque_SkAlphaType;
   334                 if (cmap != NULL) {
   335                     SkASSERT(cmap->ColorCount == (1 << (cmap->BitsPerPixel)));
   336                     colorCount = cmap->ColorCount;
   337                     if (colorCount > 256) {
   338                         colorCount = 256;  // our kIndex8 can't support more
   339                     }
   340                     for (int index = 0; index < colorCount; index++) {
   341                         colorPtr[index] = SkPackARGB32(0xFF,
   342                                                        cmap->Colors[index].Red,
   343                                                        cmap->Colors[index].Green,
   344                                                        cmap->Colors[index].Blue);
   345                     }
   346                 } else {
   347                     // find_colormap() returned NULL.  Some (rare, broken)
   348                     // GIFs don't have a color table, so we force one.
   349                     gif_warning(*bm, "missing colormap");
   350                     colorCount = 256;
   351                     sk_memset32(colorPtr, SK_ColorWHITE, colorCount);
   352                 }
   353                 transpIndex = find_transpIndex(temp_save, colorCount);
   354                 if (transpIndex >= 0) {
   355                     colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a transparent SkPMColor
   356                     alphaType = kPremul_SkAlphaType;
   357                     fillIndex = transpIndex;
   358                 } else if (fillIndex >= colorCount) {
   359                     // gif->SBackGroundColor should be less than colorCount.
   360                     fillIndex = 0;  // If not, fix it.
   361                 }
   363                 SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable,
   364                                                   (colorPtr, colorCount,
   365                                                    alphaType)));
   366                 if (!this->allocPixelRef(bm, ctable)) {
   367                     return error_return(*bm, "allocPixelRef");
   368                 }
   369             }
   371             // abort if either inner dimension is <= 0
   372             if (innerWidth <= 0 || innerHeight <= 0) {
   373                 return error_return(*bm, "non-pos inner width/height");
   374             }
   376             SkAutoLockPixels alp(*bm);
   378             SkAutoMalloc storage(innerWidth);
   379             uint8_t* scanline = (uint8_t*) storage.get();
   381             // GIF has an option to store the scanlines of an image, plus a larger background,
   382             // filled by a fill color. In this case, we will use a subset of the larger bitmap
   383             // for sampling.
   384             SkBitmap subset;
   385             SkBitmap* workingBitmap;
   386             // are we only a subset of the total bounds?
   387             if ((imageTop | imageLeft) > 0 ||
   388                  innerWidth < width || innerHeight < height) {
   389                 // Fill the background.
   390                 memset(bm->getPixels(), fillIndex, bm->getSize());
   392                 // Create a subset of the bitmap.
   393                 SkIRect subsetRect(SkIRect::MakeXYWH(imageLeft / sampler.srcDX(),
   394                                                      imageTop / sampler.srcDY(),
   395                                                      innerWidth / sampler.srcDX(),
   396                                                      innerHeight / sampler.srcDY()));
   397                 if (!bm->extractSubset(&subset, subsetRect)) {
   398                     return error_return(*bm, "Extract failed.");
   399                 }
   400                 // Update the sampler. We'll now be only sampling into the subset.
   401                 sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->getSampleSize());
   402                 workingBitmap = &subset;
   403             } else {
   404                 workingBitmap = bm;
   405             }
   407             // bm is already locked, but if we had to take a subset, it must be locked also,
   408             // so that getPixels() will point to its pixels.
   409             SkAutoLockPixels alpWorking(*workingBitmap);
   411             if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *this)) {
   412                 return error_return(*bm, "Sampler failed to begin.");
   413             }
   415             // now decode each scanline
   416             if (gif->Image.Interlace) {
   417                 // Iterate over the height of the source data. The sampler will
   418                 // take care of skipping unneeded rows.
   419                 GifInterlaceIter iter(innerHeight);
   420                 for (int y = 0; y < innerHeight; y++) {
   421                     if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
   422                         gif_warning(*bm, "interlace DGifGetLine");
   423                         memset(scanline, fillIndex, innerWidth);
   424                         for (; y < innerHeight; y++) {
   425                             sampler.sampleInterlaced(scanline, iter.currY());
   426                             iter.next();
   427                         }
   428                         return true;
   429                     }
   430                     sampler.sampleInterlaced(scanline, iter.currY());
   431                     iter.next();
   432                 }
   433             } else {
   434                 // easy, non-interlace case
   435                 const int outHeight = workingBitmap->height();
   436                 skip_src_rows(gif, scanline, innerWidth, sampler.srcY0());
   437                 for (int y = 0; y < outHeight; y++) {
   438                     if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
   439                         gif_warning(*bm, "DGifGetLine");
   440                         memset(scanline, fillIndex, innerWidth);
   441                         for (; y < outHeight; y++) {
   442                             sampler.next(scanline);
   443                         }
   444                         return true;
   445                     }
   446                     // scanline now contains the raw data. Sample it.
   447                     sampler.next(scanline);
   448                     if (y < outHeight - 1) {
   449                         skip_src_rows(gif, scanline, innerWidth, sampler.srcDY() - 1);
   450                     }
   451                 }
   452                 // skip the rest of the rows (if any)
   453                 int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() + 1;
   454                 SkASSERT(read <= innerHeight);
   455                 skip_src_rows(gif, scanline, innerWidth, innerHeight - read);
   456             }
   457             sanitize_indexed_bitmap(bm);
   458             return true;
   459             } break;
   461         case EXTENSION_RECORD_TYPE:
   462 #if GIFLIB_MAJOR < 5
   463             if (DGifGetExtension(gif, &temp_save.Function,
   464                                  &extData) == GIF_ERROR) {
   465 #else
   466             if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
   467 #endif
   468                 return error_return(*bm, "DGifGetExtension");
   469             }
   471             while (extData != NULL) {
   472                 /* Create an extension block with our data */
   473 #if GIFLIB_MAJOR < 5
   474                 if (AddExtensionBlock(&temp_save, extData[0],
   475                                       &extData[1]) == GIF_ERROR) {
   476 #else
   477                 if (GifAddExtensionBlock(&gif->ExtensionBlockCount,
   478                                          &gif->ExtensionBlocks,
   479                                          extFunction,
   480                                          extData[0],
   481                                          &extData[1]) == GIF_ERROR) {
   482 #endif
   483                     return error_return(*bm, "AddExtensionBlock");
   484                 }
   485                 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
   486                     return error_return(*bm, "DGifGetExtensionNext");
   487                 }
   488 #if GIFLIB_MAJOR < 5
   489                 temp_save.Function = 0;
   490 #endif
   491             }
   492             break;
   494         case TERMINATE_RECORD_TYPE:
   495             break;
   497         default:    /* Should be trapped by DGifGetRecordType */
   498             break;
   499         }
   500     } while (recType != TERMINATE_RECORD_TYPE);
   502     sanitize_indexed_bitmap(bm);
   503     return true;
   504 }
   506 ///////////////////////////////////////////////////////////////////////////////
   507 DEFINE_DECODER_CREATOR(GIFImageDecoder);
   508 ///////////////////////////////////////////////////////////////////////////////
   510 static bool is_gif(SkStreamRewindable* stream) {
   511     char buf[GIF_STAMP_LEN];
   512     if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
   513         if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
   514                 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
   515                 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
   516             return true;
   517         }
   518     }
   519     return false;
   520 }
   522 static SkImageDecoder* sk_libgif_dfactory(SkStreamRewindable* stream) {
   523     if (is_gif(stream)) {
   524         return SkNEW(SkGIFImageDecoder);
   525     }
   526     return NULL;
   527 }
   529 static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory);
   531 static SkImageDecoder::Format get_format_gif(SkStreamRewindable* stream) {
   532     if (is_gif(stream)) {
   533         return SkImageDecoder::kGIF_Format;
   534     }
   535     return SkImageDecoder::kUnknown_Format;
   536 }
   538 static SkImageDecoder_FormatReg gFormatReg(get_format_gif);

mercurial