aboutsummaryrefslogtreecommitdiff
path: root/src/loader.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/loader.c')
-rw-r--r--src/loader.c239
1 files changed, 239 insertions, 0 deletions
diff --git a/src/loader.c b/src/loader.c
new file mode 100644
index 0000000..c9c5976
--- /dev/null
+++ b/src/loader.c
@@ -0,0 +1,239 @@
+/* Copyright (c) 2015 Harry Jeffery
+
+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 "loader.h"
+
+void imv_init_loader(struct imv_loader *ldr)
+{
+ ldr->mbmp = NULL;
+ ldr->cur_bmp = NULL;
+ ldr->width = 0;
+ ldr->height = 0;
+ ldr->cur_frame = 0;
+ ldr->next_frame = 0;
+ ldr->num_frames = 0;
+ ldr->frame_time = 0;
+ ldr->changed = 0;
+}
+
+void imv_destroy_loader(struct imv_loader *ldr)
+{
+ if(ldr->cur_bmp) {
+ FreeImage_Unload(ldr->cur_bmp);
+ ldr->cur_bmp = NULL;
+ }
+ if(ldr->mbmp) {
+ FreeImage_CloseMultiBitmap(ldr->mbmp, 0);
+ ldr->mbmp = NULL;
+ }
+}
+
+int imv_can_load_image(const char* path)
+{
+ if(FreeImage_GetFileType(path, 0) == FIF_UNKNOWN) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+int imv_loader_load(struct imv_loader *ldr, const char* path)
+{
+ if(ldr->mbmp) {
+ FreeImage_CloseMultiBitmap(ldr->mbmp, 0);
+ ldr->mbmp = NULL;
+ }
+
+ if(ldr->cur_bmp) {
+ FreeImage_Unload(ldr->cur_bmp);
+ ldr->cur_bmp = NULL;
+ }
+
+
+ FREE_IMAGE_FORMAT fmt = FreeImage_GetFileType(path,0);
+
+ if(fmt == FIF_UNKNOWN) {
+ return 1;
+ }
+
+ ldr->num_frames = 0;
+ ldr->cur_frame = 0;
+ ldr->next_frame = 0;
+ ldr->frame_time = 0;
+
+ if(fmt == FIF_GIF) {
+ ldr->mbmp = FreeImage_OpenMultiBitmap(FIF_GIF, path,
+ /* don't create file */ 0,
+ /* read only */ 1,
+ /* keep in memory */ 1,
+ /* flags */ GIF_LOAD256);
+ if(!ldr->mbmp) {
+ return 1;
+ }
+ ldr->num_frames = FreeImage_GetPageCount(ldr->mbmp);
+
+ /* get the dimensions from the first frame */
+ FIBITMAP *frame = FreeImage_LockPage(ldr->mbmp, 0);
+ ldr->width = FreeImage_GetWidth(frame);
+ ldr->height = FreeImage_GetHeight(frame);
+ if(!imv_loader_is_animated(ldr)) {
+ ldr->cur_bmp = FreeImage_ConvertTo32Bits(frame);
+ }
+ FreeImage_UnlockPage(ldr->mbmp, frame, 0);
+
+ if(imv_loader_is_animated(ldr)) {
+ /* load a frame */
+ imv_loader_load_next_frame(ldr);
+ }
+ } else {
+ FIBITMAP *image = FreeImage_Load(fmt, path, 0);
+ if(!image) {
+ return 1;
+ }
+ ldr->cur_bmp = FreeImage_ConvertTo32Bits(image);
+ ldr->width = FreeImage_GetWidth(ldr->cur_bmp);
+ ldr->height = FreeImage_GetHeight(ldr->cur_bmp);
+ FreeImage_Unload(image);
+ }
+
+ ldr->changed = 1;
+ return 0;
+}
+
+void imv_loader_load_next_frame(struct imv_loader *ldr)
+{
+ if(!imv_loader_is_animated(ldr)) {
+ return;
+ }
+
+ FITAG *tag = NULL;
+ char disposal_method = 0;
+ int frame_time = 0;
+ short top = 0;
+ short left = 0;
+
+ ldr->cur_frame = ldr->next_frame;
+ ldr->next_frame = (ldr->cur_frame + 1) % ldr->num_frames;
+ FIBITMAP *frame = FreeImage_LockPage(ldr->mbmp, ldr->cur_frame);
+ FIBITMAP *frame32 = FreeImage_ConvertTo32Bits(frame);
+
+ /* First frame is always going to use the raw frame */
+ if(ldr->cur_frame > 0) {
+ FreeImage_GetMetadata(FIMD_ANIMATION, frame, "DisposalMethod", &tag);
+ if(FreeImage_GetTagValue(tag)) {
+ disposal_method = *(char*)FreeImage_GetTagValue(tag);
+ }
+ }
+
+ FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameLeft", &tag);
+ if(FreeImage_GetTagValue(tag)) {
+ left = *(short*)FreeImage_GetTagValue(tag);
+ }
+
+ FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTop", &tag);
+ if(FreeImage_GetTagValue(tag)) {
+ top = *(short*)FreeImage_GetTagValue(tag);
+ }
+
+ FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTime", &tag);
+ if(FreeImage_GetTagValue(tag)) {
+ frame_time = *(int*)FreeImage_GetTagValue(tag);
+ }
+
+ /* some gifs don't provide a frame time at all */
+ if(frame_time == 0) {
+ frame_time = 100;
+ }
+ ldr->frame_time += frame_time * 0.001;
+
+ FreeImage_UnlockPage(ldr->mbmp, frame, 0);
+
+ /* If this frame is inset, we need to expand it for compositing */
+ if(ldr->width != (int)FreeImage_GetWidth(frame32) ||
+ ldr->height != (int)FreeImage_GetHeight(frame32)) {
+ FIBITMAP *expanded = FreeImage_Allocate(ldr->width, ldr->height, 32, 0,0,0);
+ FreeImage_Paste(expanded, frame32, left, top, 255);
+ FreeImage_Unload(frame32);
+ frame32 = expanded;
+ }
+
+ switch(disposal_method) {
+ case 0: /* nothing specified, just use the raw frame */
+ if(ldr->cur_bmp) {
+ FreeImage_Unload(ldr->cur_bmp);
+ }
+ ldr->cur_bmp = frame32;
+ break;
+ case 1: /* composite over previous frame */
+ if(ldr->cur_bmp && ldr->cur_frame > 0) {
+ FIBITMAP *bg_frame = FreeImage_ConvertTo24Bits(ldr->cur_bmp);
+ FreeImage_Unload(ldr->cur_bmp);
+ FIBITMAP *comp = FreeImage_Composite(frame32, 1, NULL, bg_frame);
+ FreeImage_Unload(bg_frame);
+ FreeImage_Unload(frame32);
+ ldr->cur_bmp = comp;
+ } else {
+ /* No previous frame, just render directly */
+ if(ldr->cur_bmp) {
+ FreeImage_Unload(ldr->cur_bmp);
+ }
+ ldr->cur_bmp = frame32;
+ }
+ break;
+ case 2: /* TODO - set to background, composite over that */
+ if(ldr->cur_bmp) {
+ FreeImage_Unload(ldr->cur_bmp);
+ }
+ ldr->cur_bmp = frame32;
+ break;
+ case 3: /* TODO - restore to previous content */
+ if(ldr->cur_bmp) {
+ FreeImage_Unload(ldr->cur_bmp);
+ }
+ ldr->cur_bmp = frame32;
+ break;
+ }
+ ldr->changed = 1;
+}
+
+int imv_loader_is_animated(struct imv_loader *ldr)
+{
+ return ldr->num_frames > 1;
+}
+
+void imv_loader_play(struct imv_loader *ldr, double time)
+{
+ if(!imv_loader_is_animated(ldr)) {
+ return;
+ }
+
+ ldr->frame_time -= time;
+ if(ldr->frame_time < 0) {
+ ldr->frame_time = 0;
+ imv_loader_load_next_frame(ldr);
+ }
+}
+
+int imv_loader_has_changed(struct imv_loader *ldr)
+{
+ if(ldr->changed) {
+ ldr->changed = 0;
+ return 1;
+ } else {
+ return 0;
+ }
+}