aboutsummaryrefslogtreecommitdiff
path: root/src/Gif.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Gif.cpp')
-rw-r--r--src/Gif.cpp189
1 files changed, 189 insertions, 0 deletions
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);
+ }
+}