From b1296f2c97c6fdc1c6a9922dc09c951b5cafdc12 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 29 Oct 2018 21:49:54 +0100 Subject: Initial commit --- src/Gif.cpp | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 src/Gif.cpp (limited to 'src/Gif.cpp') diff --git a/src/Gif.cpp b/src/Gif.cpp new file mode 100644 index 0000000..0c52f7d --- /dev/null +++ b/src/Gif.cpp @@ -0,0 +1,189 @@ +#include "../include/dchat/Gif.hpp" +#include "../include/FileUtil.hpp" + +using namespace std; + +namespace dchat +{ + static void* bitmapCreate(int width, int height) + { + return calloc(width * height, 4); + } + + static void bitmapDestroy(void *bitmap) + { + free(bitmap); + } + + static unsigned char* bitmapGetBuffer(void *bitmap) + { + return (unsigned char*)bitmap; + } + + static void bitmapSetOpaque(void *bitmap, bool opaque) + { + + } + + static bool bitmapTestOpaque(void *bitmap) + { + return false; + } + + static void bitmapModified(void *bitmap) + { + + } + + static const char* gifResultToString(gif_result code) + { + switch(code) + { + case GIF_INSUFFICIENT_FRAME_DATA: + return "GIF_INSUFFICIENT_FRAME_DATA"; + case GIF_FRAME_DATA_ERROR: + return "GIF_FRAME_DATA_ERROR"; + case GIF_INSUFFICIENT_DATA: + return "GIF_INSUFFICIENT_DATA"; + case GIF_DATA_ERROR: + return "GIF_DATA_ERROR"; + case GIF_INSUFFICIENT_MEMORY: + return "GIF_INSUFFICIENT_MEMORY"; + default: + return "Unknown gif result code"; + } + } + + Gif::Gif(const boost::filesystem::path &filepath) : + currentFrame(0), + timeElapsedCs(0.0) + { + try + { + fileContent = getFileContent(filepath); + } + catch(FileException &e) + { + throw GifLoadException(e.what()); + } + + try + { + init(); + } + catch(GifLoadException &e) + { + delete[] fileContent.data; + throw e; + } + } + + Gif::Gif(StringView _fileContent) : + fileContent(move(_fileContent)), + currentFrame(0), + timeElapsedCs(0.0) + { + try + { + init(); + } + catch(GifLoadException &e) + { + delete[] fileContent.data; + throw e; + } + } + + void Gif::init() + { + gif_bitmap_callback_vt bitmapCallbacks = + { + bitmapCreate, + bitmapDestroy, + bitmapGetBuffer, + bitmapSetOpaque, + bitmapTestOpaque, + bitmapModified + }; + + gif_create(&gif, &bitmapCallbacks); + + gif_result code; + do + { + code = gif_initialise(&gif, fileContent.size, (unsigned char*)fileContent.data); + if(code != GIF_OK && code != GIF_WORKING) + { + string errMsg = "Failed to initialize gif, reason: "; + errMsg += gifResultToString(code); + throw GifLoadException(errMsg); + } + } + while(code != GIF_OK); + + if(!createTexture(gif.width, gif.height)) + throw GifLoadException("Failed to create texture for gif"); + } + + Gif::~Gif() + { + gif_finalise(&gif); + delete[] fileContent.data; + } + + Vec2u Gif::getSize() const + { + return { gif.width, gif.height }; + } + + void Gif::update() + { + double timeElapsedMilli = (double)frameTimer.getElapsedTimeMillis(); + // If gif is not redrawn for a while, then we reset timer (gif is paused). This happens when gif is not visible and then appears visible + // (because it's visible in window). The reason this is done is to prevent too much time between rendering gif frames, as processing a gif + // requires to process all frames between two points in time, if elapsed frame time is too high, then we would require to process several + // frames of gif in one application render frame. + if(timeElapsedMilli > 1000.0) + timeElapsedMilli = 0.0; + double frameDeltaCs = timeElapsedMilli * 0.1; // Centisecond + frameTimer.restart(); + timeElapsedCs += frameDeltaCs; + + unsigned char *image = nullptr; + u32 startFrame = currentFrame; + while(true) + { + u32 i = currentFrame % gif.frame_count; + gif_result code = gif_decode_frame(&gif, i); + if(code != GIF_OK) + { + printf("Warning: gif_decode_frame: %s\n", gifResultToString(code)); + break; + } + + gif_frame &frame = gif.frames[i]; + // frame_delay is in centiseconds + unsigned int frameDelay = frame.frame_delay; + if(frameDelay == 0) + frameDelay = 7; + double fFrameDelay = (double)frameDelay; + if(timeElapsedCs >= fFrameDelay) + timeElapsedCs -= fFrameDelay; + else + break; + + image = (unsigned char*)gif.frame_image; + ++currentFrame; + } + + if(currentFrame != startFrame) + { + updateTexture(image); + } + } + + bool Gif::isDataGif(const StringView &data) + { + return data.size >= 6 && (memcmp(data.data, "GIF87a", 6) == 0 || memcmp(data.data, "GIF89a", 6) == 0); + } +} -- cgit v1.2.3