blob: 5a6be42d39d9492bf8bd0db1edaf70c8f0519da0 [file] [log] [blame]
/* libs/graphics/images/SkImageDecoder_libgif.cpp
**
** Copyright 2006, Google Inc.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include "SkImageDecoder.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkStream.h"
#include "SkTemplates.h"
class SkGIFImageDecoder : public SkImageDecoder {
protected:
virtual bool onDecode(SkStream* stream, SkBitmap* bm, SkBitmap::Config pref);
};
extern "C" {
#include "gif_lib.h"
}
#define GIF_STAMP "GIF" /* First chars in file - GIF stamp. */
#define GIF_STAMP_LEN (sizeof(GIF_STAMP) - 1)
SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream* stream)
{
char buf[GIF_STAMP_LEN];
if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN &&
memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0)
{
stream->rewind();
return SkNEW(SkGIFImageDecoder);
}
return NULL;
}
static int Decode(GifFileType* fileType, GifByteType* out, int size)
{
SkStream* stream = (SkStream*) fileType->UserData;
return (int) stream->read(out, size);
}
class AutoGifClose {
public:
AutoGifClose(GifFileType* gif) : fGIF(gif) {}
~AutoGifClose()
{
DGifCloseFile(fGIF);
}
private:
GifFileType* fGIF;
};
bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, SkBitmap::Config prefConfig)
{
GifFileType* gif = DGifOpen( sk_stream, Decode );
if (gif == nil)
return false;
AutoGifClose ac(gif);
if (DGifSlurp(gif) != GIF_OK)
return false;
ColorMapObject* cmap = gif->SColorMap;
if (cmap == 0 ||
gif->ImageCount < 1 ||
cmap->ColorCount != (1 << cmap->BitsPerPixel))
return false;
SavedImage* gif_image = gif->SavedImages + 0;
const int width = gif->SWidth;
const int height = gif->SHeight;
SkBitmap::Config config = SkBitmap::kIndex8_Config;
if (!this->chooseFromOneChoice(config, width, height))
return false;
bm->setConfig(config, width, height, 0);
bm->allocPixels();
SkColorTable* colorTable = SkNEW(SkColorTable);
colorTable->setColors(cmap->ColorCount);
bm->setColorTable(colorTable)->unref();
int transparent = -1;
for (int i = 0; i < gif_image->ExtensionBlockCount; ++i) {
ExtensionBlock* eb = gif_image->ExtensionBlocks + i;
if (eb->Function == 0xF9 &&
eb->ByteCount == 4) {
bool has_transparency = ((eb->Bytes[0] & 1) == 1);
if (has_transparency) {
transparent = eb->Bytes[3];
}
}
}
SkPMColor* colorPtr = colorTable->lockColors();
if (transparent >= 0)
memset(colorPtr, 0, cmap->ColorCount * 4);
else
colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
for (int index = 0; index < cmap->ColorCount; index++)
{
if (transparent != index)
colorPtr[index] = SkColorSetRGB(cmap->Colors[index].Red,
cmap->Colors[index].Green, cmap->Colors[index].Blue);
}
colorTable->unlockColors(true);
unsigned char* in = (unsigned char*)gif_image->RasterBits;
unsigned char* out = bm->getAddr8(0, 0);
if (gif->Image.Interlace) {
// deinterlace
int row;
// group 1 - every 8th row, starting with row 0
for (row = 0; row < height; row += 8) {
memcpy(out + width * row, in, width);
in += width;
}
// group 2 - every 8th row, starting with row 4
for (row = 4; row < height; row += 8) {
memcpy(out + width * row, in, width);
in += width;
}
// group 3 - every 4th row, starting with row 2
for (row = 2; row < height; row += 4) {
memcpy(out + width * row, in, width);
in += width;
}
for (row = 1; row < height; row += 2) {
memcpy(out + width * row, in, width);
in += width;
}
} else {
memcpy(out, in, width * height);
}
return true;
}