aboutsummaryrefslogtreecommitdiff
path: root/src/Gif.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Gif.cpp')
-rw-r--r--src/Gif.cpp169
1 files changed, 169 insertions, 0 deletions
diff --git a/src/Gif.cpp b/src/Gif.cpp
new file mode 100644
index 0000000..3f7216e
--- /dev/null
+++ b/src/Gif.cpp
@@ -0,0 +1,169 @@
+#include "../include/Gif.hpp"
+#include "../include/FileUtil.hpp"
+
+using namespace std;
+
+namespace dchat
+{
+ void* bitmapCreate(int width, int height)
+ {
+ return calloc(width * height, 4);
+ }
+
+ void bitmapDestroy(void *bitmap)
+ {
+ free(bitmap);
+ }
+
+ unsigned char* bitmapGetBuffer(void *bitmap)
+ {
+ return (unsigned char*)bitmap;
+ }
+
+ void bitmapSetOpaque(void *bitmap, bool opaque)
+ {
+
+ }
+
+ bool bitmapTestOpaque(void *bitmap)
+ {
+ return false;
+ }
+
+ void bitmapModified(void *bitmap)
+ {
+
+ }
+
+ 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());
+ }
+ init();
+ }
+
+ Gif::Gif(StringView &&_fileContent) :
+ fileContent(move(_fileContent)),
+ currentFrame(0),
+ timeElapsedCs(0.0)
+ {
+ init();
+ }
+
+ 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(!texture.create(gif.width, gif.height))
+ throw GifLoadException("Failed to create texture for gif");
+
+ sprite.setTexture(texture, true);
+ }
+
+ Gif::~Gif()
+ {
+ gif_finalise(&gif);
+ delete fileContent.data;
+ }
+
+ void Gif::setPosition(const sf::Vector2f &position)
+ {
+ sprite.setPosition(position);
+ }
+
+ void Gif::setSize(const sf::Vector2f &size)
+ {
+ sf::Vector2u textureSize = sprite.getTexture()->getSize();
+ sprite.setScale(size.x / (float)textureSize.x, size.y / (float)textureSize.y);
+ }
+
+ void Gif::draw(sf::RenderWindow &window)
+ {
+ double frameDeltaCs = (double)frameTimer.getElapsedTime().asMilliseconds() * 0.1; // Centisecond
+ frameTimer.restart();
+ timeElapsedCs += frameDeltaCs;
+
+ unsigned char *image = nullptr;
+ u32 startFrame = currentFrame;
+ while(true)
+ {
+ int 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));
+
+ gif_frame &frame = gif.frames[i];
+ // frame_delay is in centiseconds
+ double frameDelay = (double)frame.frame_delay;
+ if(timeElapsedCs >= frameDelay)
+ timeElapsedCs -= frameDelay;
+ else
+ break;
+
+ image = (unsigned char*)gif.frame_image;
+ ++currentFrame;
+ }
+
+ if(currentFrame != startFrame)
+ {
+ texture.update(image);
+ sprite.setTexture(texture, true);
+ }
+ window.draw(sprite);
+ }
+
+ bool Gif::isDataGif(const StringView &data)
+ {
+ return data.size >= 6 && (memcmp(data.data, "GIF87a", 6) == 0 || memcmp(data.data, "GIF89a", 6) == 0);
+ }
+}