IMAGE: added GIFDecoder using libgif

This commit is contained in:
Martin Gerhardy 2021-04-04 13:05:28 +02:00 committed by Le Philousophe
parent 78c48fd9df
commit bb34721dd4
10 changed files with 302 additions and 6 deletions

View File

@ -15,21 +15,21 @@ jobs:
arch: x86
# fribidi is disabled due to timeouts when installing the package
configFlags: --enable-faad --enable-mpeg2 --enable-discord --disable-fribidi
vcpkgPackages: 'curl discord-rpc faad2 fluidsynth freetype glew libflac libjpeg-turbo libmad libmpeg2 libogg libpng libtheora libvorbis sdl2 sdl2-net zlib'
vcpkgPackages: 'curl discord-rpc faad2 fluidsynth freetype glew libflac libjpeg-turbo libmad libmpeg2 libogg libpng libtheora libvorbis sdl2 sdl2-net zlib giflib'
useNasm: 'true'
- platform: x64
arch: x64
triplet: x64-windows
# fribidi is disabled due to timeouts when installing the package
configFlags: --enable-faad --enable-mpeg2 --enable-discord --disable-fribidi
vcpkgPackages: 'curl discord-rpc faad2 fluidsynth freetype glew libflac libjpeg-turbo libmad libmpeg2 libogg libpng libtheora libvorbis sdl2 sdl2-net zlib'
vcpkgPackages: 'curl discord-rpc faad2 fluidsynth freetype glew libflac libjpeg-turbo libmad libmpeg2 libogg libpng libtheora libvorbis sdl2 sdl2-net zlib giflib'
- platform: arm64
arch: arm64
triplet: arm64-windows
# fribidi is disabled due to https://github.com/microsoft/vcpkg/issues/11248 [fribidi] Fribidi doesn't cross-compile on x86-64 to target arm/arm64
# Note that fribidi is also disabled on arm64 in devtools/create_project/msvc.cpp
configFlags: --enable-faad --enable-mpeg2 --enable-discord --disable-fribidi --disable-opengl
vcpkgPackages: 'curl discord-rpc faad2 fluidsynth freetype libflac libjpeg-turbo libmad libmpeg2 libogg libpng libtheora libvorbis sdl2 sdl2-net zlib'
vcpkgPackages: 'curl discord-rpc faad2 fluidsynth freetype libflac libjpeg-turbo libmad libmpeg2 libogg libpng libtheora libvorbis sdl2 sdl2-net zlib giflib'
env:
CONFIGURATION: Release
PLATFORM: ${{ matrix.platform }}
@ -103,7 +103,7 @@ jobs:
- platform: macosx
buildFlags: -scheme ScummVM-macOS
configFlags: --disable-nasm --enable-faad --enable-mpeg2
brewPackages: a52dec faad2 flac fluid-synth freetype fribidi glew mad libmpeg2 libogg libpng libvorbis sdl2 sdl2_net theora
brewPackages: a52dec faad2 flac fluid-synth freetype fribidi glew mad libmpeg2 libogg libpng libvorbis sdl2 sdl2_net theora giflib
- platform: ios7
buildFlags: -scheme ScummVM-iOS CODE_SIGN_IDENTITY="" CODE_SIGNING_ALLOWED=NO
configFlags: --disable-nasm --disable-opengl --disable-theora --disable-taskbar --disable-tts --disable-fribidi

View File

@ -32,6 +32,7 @@ addons:
- libsndio-dev
- libreadline-dev
- libglew-dev
- libgif-dev
branches:
only:

35
configure vendored
View File

@ -154,6 +154,7 @@ _sparkle=auto
_osxdockplugin=auto
_jpeg=auto
_png=auto
_gif=auto
_theoradec=auto
_faad=auto
_fluidsynth=auto
@ -272,6 +273,7 @@ add_feature jpeg "JPEG" "_jpeg"
add_feature mpeg2 "mpeg2" "_mpeg2"
add_feature opengl_game_shaders "OpenGL with shaders" "_opengl_game_shaders"
add_feature png "PNG" "_png"
add_feature png "GIF" "_gif"
add_feature theoradec "libtheoradec" "_theoradec"
add_feature tinygl "TinyGL" "_tinygl"
add_feature vorbis "Vorbis file support" "_vorbis _tremor"
@ -1111,6 +1113,8 @@ for ac_option in $@; do
--enable-jpeg) _jpeg=yes ;;
--disable-png) _png=no ;;
--enable-png) _png=yes ;;
--disable-gif) _gif=no ;;
--enable-gif) _gif=yes ;;
--disable-theoradec) _theoradec=no ;;
--enable-theoradec) _theoradec=yes ;;
--disable-faad) _faad=no ;;
@ -4477,6 +4481,37 @@ fi
define_in_config_if_yes "$_png" 'USE_PNG'
echo "$_png"
#
# Check for GIF
#
echocheck "GIF >= 5.0.0"
if test "$_pkg_config" = "yes" && $_pkgconfig --exists libgif; then
append_var GIF_LIBS "`$_pkgconfig --libs libgif`"
append_var GIF_CFLAGS "`$_pkgconfig --cflags libgif`"
else
append_var GIF_LIBS "-lgif"
fi
if test "$_gif" = auto ; then
_gif=no
cat > $TMPC << EOF
#include <gif_lib.h>
int main(void) {
#if GIFLIB_MAJOR >= 5
#else
syntax error
#endif
return 0;
}
EOF
cc_check $GIF_CFLAGS $GIF_LIBS && _gif=yes
fi
if test "$_gif" = yes ; then
append_var LIBS "$GIF_LIBS"
append_var INCLUDES "$GIF_CFLAGS"
fi
define_in_config_if_yes "$_gif" 'USE_GIF'
echo "$_gif"
#
# Check for Theora Decoder
#

View File

@ -1043,6 +1043,7 @@ const Feature s_features[] = {
{ "tremor", "USE_TREMOR", true, false, "Tremor support" },
{ "flac", "USE_FLAC", true, true, "FLAC support" },
{ "png", "USE_PNG", true, true, "libpng support" },
{ "gif", "USE_GIF", true, false, "libgif support" },
{ "faad", "USE_FAAD", true, false, "AAC support" },
{ "mpeg2", "USE_MPEG2", true, false, "MPEG-2 support" },
{ "theora", "USE_THEORADEC", true, true, "Theora decoding support" },

145
image/gif.cpp Normal file
View File

@ -0,0 +1,145 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "image/gif.h"
#include "common/array.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "graphics/surface.h"
#include "graphics/pixelformat.h"
#ifdef USE_GIF
#include <gif_lib.h>
#endif
namespace Image {
GIFDecoder::GIFDecoder() : _outputSurface(0), _palette(0), _colorCount(0) {
}
GIFDecoder::~GIFDecoder() {
destroy();
}
#ifdef USE_GIF
static int gifReadFromStream(GifFileType *gif, GifByteType *bytes, int size) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)gif->UserData;
return stream->read(bytes, size);
}
#endif
bool GIFDecoder::loadStream(Common::SeekableReadStream &stream) {
destroy();
#ifdef USE_GIF
int error = 0;
GifFileType *gif = DGifOpen(&stream, gifReadFromStream, &error);
if (!gif) {
warning("GIF open failed with error %s", GifErrorString(error));
return false;
}
const int errcode = DGifSlurp(gif);
if (errcode != GIF_OK) {
warning("GIF failed to load");
DGifCloseFile(gif, 0);
return false;
}
if (gif->ImageCount <= 0) {
warning("GIF doesn't contain valid image data");
DGifCloseFile(gif, 0);
return false;
}
if (gif->ImageCount > 1) {
warning("GIF contains more than one frame - only loading the first one");
}
const SavedImage *gifImage = gif->SavedImages;
const int width = gif->SWidth;
const int height = gif->SHeight;
const ColorMapObject *colorMap = gif->SColorMap;
_transparentColor = NO_TRANSPARENT_COLOR;
for (int i = 0; i < gif->ExtensionBlockCount; ++i) {
const ExtensionBlock &eb = gif->ExtensionBlocks[i];
GraphicsControlBlock gcb;
DGifExtensionToGCB(eb.ByteCount, eb.Bytes, &gcb);
if (gcb.TransparentColor != NO_TRANSPARENT_COLOR) {
_transparentColor = gcb.TransparentColor;
break;
}
}
_colorCount = colorMap->ColorCount;
_outputSurface = new Graphics::Surface();
_palette = new uint8[_colorCount * 3];
const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
for (int i = 0; i < _colorCount; ++i) {
_palette[(i * 3) + 0] = colorMap->Colors[i].Red;
_palette[(i * 3) + 1] = colorMap->Colors[i].Green;
_palette[(i * 3) + 2] = colorMap->Colors[i].Blue;
}
// TODO: support transparency
_outputSurface->create(width, height, format);
const uint8 *in = (const uint8 *)gifImage->RasterBits;
uint8 *pixelPtr = (uint8 *)_outputSurface->getBasePtr(0, 0);
if (gif->Image.Interlace) {
const int interlacedOffset[] = {0, 4, 2, 1};
const int interlacedJumps[] = {8, 8, 4, 2};
for (int i = 0; i < 4; ++i) {
for (int row = interlacedOffset[i]; row < height; row += interlacedJumps[i]) {
memcpy(pixelPtr + width * row, in, width);
in += width;
}
}
} else {
memcpy(pixelPtr, in, width * height);
}
DGifCloseFile(gif, 0);
return true;
#else
return false;
#endif
}
void GIFDecoder::destroy() {
if (_outputSurface) {
_outputSurface->free();
delete _outputSurface;
_outputSurface = 0;
}
if (_palette) {
delete[] _palette;
_palette = 0;
}
}
} // End of namespace Image

71
image/gif.h Normal file
View File

@ -0,0 +1,71 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef IMAGE_GIF_H
#define IMAGE_GIF_H
#include "image/image_decoder.h"
namespace Common {
class SeekableReadStream;
}
namespace Graphics {
struct Surface;
}
namespace Image {
/**
* @defgroup image_gif GIF decoder
* @ingroup image
*
* @brief Decoder for images encoded as Graphics Interchange Format (GIF).
*
* This decoder has a dependency on the libgif library.
*
* Used in engines:
* - TwinE
* @{
*/
class GIFDecoder : public ImageDecoder {
public:
GIFDecoder();
~GIFDecoder();
bool loadStream(Common::SeekableReadStream &stream) override;
void destroy() override;
const byte *getPalette() const override { return _palette; }
uint16 getPaletteColorCount() const override { return _colorCount; }
const Graphics::Surface *getSurface() const override { return _outputSurface; }
int getTransparentColor() const { return _transparentColor; }
private:
Graphics::Surface *_outputSurface;
uint8 *_palette;
uint16 _colorCount;
int _transparentColor;
};
/** @} */
} // End of namespace Image
#endif

View File

@ -3,6 +3,7 @@ MODULE := image
MODULE_OBJS := \
bmp.o \
cel_3do.o \
gif.o \
iff.o \
jpeg.o \
pcx.o \

1
test/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/engine-data

41
test/image/gif.h Normal file
View File

@ -0,0 +1,41 @@
#include <cxxtest/TestSuite.h>
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include "common/memstream.h"
#include "image/gif.h"
#include "graphics/surface.h"
class GIFDecoderTestSuite : public CxxTest::TestSuite {
public:
void test_load_gif_2x2() {
#ifdef USE_GIF
const uint8 gifBuf[63] = {
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x02, 0x00, 0x02, 0x00, 0xa1,
0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x14, 0x82, 0x31,
0xff, 0xff, 0xff, 0x21, 0xfe, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d,
0x50, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00,
0x00, 0x02, 0x03, 0x54, 0x06, 0x05, 0x00, 0x3b
};
Image::GIFDecoder decoder;
Common::MemoryReadStream stream(gifBuf, sizeof(gifBuf));
const bool status = decoder.loadStream(stream);
TS_ASSERT(status);
if (!status) {
return;
}
const Graphics::Surface *surface = decoder.getSurface();
TS_ASSERT(surface != 0);
if (surface == 0) {
return;
}
TS_ASSERT_EQUALS(surface->w, 2);
TS_ASSERT_EQUALS(surface->h, 2);
TS_ASSERT_EQUALS(surface->format.bytesPerPixel, 1);
#endif
}
};

View File

@ -5,7 +5,7 @@
#
######################################################################
TESTS := $(srcdir)/test/common/*.h $(srcdir)/test/audio/*.h $(srcdir)/test/math/*.h
TESTS := $(srcdir)/test/common/*.h $(srcdir)/test/audio/*.h $(srcdir)/test/math/*.h $(srcdir)/test/image/*.h
TEST_LIBS :=
ifdef POSIX
@ -27,7 +27,7 @@ TEST_LIBS += test/null_osystem.o \
backends/modular-backend.o
endif
TEST_LIBS += audio/libaudio.a math/libmath.a common/libcommon.a
TEST_LIBS += audio/libaudio.a math/libmath.a common/libcommon.a image/libimage.a graphics/libgraphics.a
ifeq ($(ENABLE_WINTERMUTE), STATIC_PLUGIN)
TESTS += $(srcdir)/test/engines/wintermute/*.h