2022-12-10 17:06:57 +01:00
|
|
|
|
|
|
|
#include "gif_loader.h"
|
2022-12-10 17:14:50 +01:00
|
|
|
|
2022-12-10 19:06:41 +01:00
|
|
|
#ifdef BIG_ENDIAN_ENABLED
|
|
|
|
#define GIF_BIGE 1
|
|
|
|
#endif
|
|
|
|
|
2022-12-10 18:34:58 +01:00
|
|
|
#include "./thirdparty/gif_load/gif_load.h"
|
|
|
|
#include "core/os/file_access.h"
|
2022-12-10 19:25:06 +01:00
|
|
|
#include "scene/resources/texture.h"
|
2022-12-10 17:14:50 +01:00
|
|
|
|
2022-12-10 18:34:58 +01:00
|
|
|
Array GIFLoader::get_images() {
|
|
|
|
return _images;
|
|
|
|
}
|
|
|
|
|
2022-12-10 19:25:06 +01:00
|
|
|
Ref<AnimatedTexture> GIFLoader::create_texture(float fps) {
|
|
|
|
Ref<AnimatedTexture> tex;
|
|
|
|
tex.instance();
|
|
|
|
tex->set_fps(0);
|
|
|
|
tex->set_flags(0);
|
|
|
|
|
|
|
|
float per_frame_time = 1.0 / fps;
|
|
|
|
|
|
|
|
for (int i = 0; i < _images.size(); ++i) {
|
|
|
|
Array arr = _images[i];
|
|
|
|
|
|
|
|
ERR_CONTINUE(arr.size() != 2);
|
|
|
|
|
|
|
|
float frames = arr[0];
|
|
|
|
Ref<Image> img = arr[1];
|
|
|
|
|
|
|
|
Ref<ImageTexture> imgt;
|
|
|
|
imgt.instance();
|
|
|
|
imgt->create_from_image(img, 0);
|
|
|
|
|
|
|
|
tex->set_frame_texture(i, imgt);
|
|
|
|
tex->set_frame_delay(i, per_frame_time * frames);
|
|
|
|
}
|
|
|
|
|
|
|
|
tex->set_frames(_images.size());
|
|
|
|
|
|
|
|
return tex;
|
|
|
|
}
|
|
|
|
|
2022-12-10 18:34:58 +01:00
|
|
|
void GIFLoader::gif_frame(void *data, struct GIF_WHDR *whdr) {
|
|
|
|
uint32_t *pict, *prev, x, y, yoff, iter, ifin, dsrc, ddst;
|
|
|
|
//uint8_t head[18] = { 0 };
|
|
|
|
GIFLoader *loader = (GIFLoader *)data;
|
|
|
|
|
2022-12-10 19:06:41 +01:00
|
|
|
#define RGBA(i) ((whdr->bptr[i] == whdr->tran) ? 0 : ((uint32_t)(whdr->cpal[whdr->bptr[i]].R << ((GIF_BIGE) ? 24 : 0)) | (uint32_t)(whdr->cpal[whdr->bptr[i]].G << ((GIF_BIGE) ? 16 : 8)) | (uint32_t)(whdr->cpal[whdr->bptr[i]].B << ((GIF_BIGE) ? 8 : 16)) | ((GIF_BIGE) ? 0xFF : 0xFF000000)))
|
2022-12-10 18:34:58 +01:00
|
|
|
|
|
|
|
if (!whdr->ifrm) {
|
2022-12-10 19:06:41 +01:00
|
|
|
//first frame, alloc
|
2022-12-10 18:34:58 +01:00
|
|
|
ddst = (uint32_t)(whdr->xdim * whdr->ydim);
|
|
|
|
loader->pictd.resize(ddst * sizeof(uint32_t));
|
|
|
|
loader->prevd.resize(ddst * sizeof(uint32_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
PoolByteArray::Write pictw = loader->pictd.write();
|
|
|
|
|
|
|
|
/** [TODO:] the frame is assumed to be inside global bounds,
|
|
|
|
however it might exceed them in some GIFs; fix me. **/
|
|
|
|
pict = (uint32_t *)pictw.ptr();
|
|
|
|
ddst = (uint32_t)(whdr->xdim * whdr->fryo + whdr->frxo);
|
|
|
|
ifin = (!(iter = (whdr->intr) ? 0 : 4)) ? 4 : 5; /** interlacing support **/
|
|
|
|
for (dsrc = (uint32_t)-1; iter < ifin; iter++) {
|
|
|
|
for (yoff = 16U >> ((iter > 1) ? iter : 1), y = (8 >> iter) & 7; y < (uint32_t)whdr->fryd; y += yoff) {
|
|
|
|
for (x = 0; x < (uint32_t)whdr->frxd; x++) {
|
|
|
|
if (whdr->tran != (long)whdr->bptr[++dsrc]) {
|
2022-12-10 19:06:41 +01:00
|
|
|
pict[(uint32_t)whdr->xdim * y + x + ddst] = RGBA(dsrc);
|
2022-12-10 18:34:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pictw.release();
|
|
|
|
|
2022-12-10 19:09:10 +01:00
|
|
|
Array image_arr;
|
2022-12-10 21:27:17 +01:00
|
|
|
int aframe_time = whdr->time;
|
|
|
|
image_arr.push_back(aframe_time);
|
2022-12-10 19:09:10 +01:00
|
|
|
|
2022-12-10 18:34:58 +01:00
|
|
|
Ref<Image> img;
|
|
|
|
img.instance();
|
|
|
|
img->create(whdr->xdim, whdr->ydim, false, Image::FORMAT_RGBA8, loader->pictd);
|
2022-12-10 19:09:10 +01:00
|
|
|
image_arr.push_back(img);
|
|
|
|
|
|
|
|
loader->_images.push_back(image_arr);
|
2022-12-10 18:34:58 +01:00
|
|
|
|
|
|
|
pict = (uint32_t *)pictw.ptr();
|
|
|
|
|
|
|
|
pictw = loader->pictd.write();
|
|
|
|
PoolByteArray::Write prevdw = loader->prevd.write();
|
|
|
|
|
|
|
|
if ((whdr->mode == GIF_PREV) && !loader->last) {
|
|
|
|
whdr->frxd = whdr->xdim;
|
|
|
|
whdr->fryd = whdr->ydim;
|
|
|
|
whdr->mode = GIF_BKGD;
|
|
|
|
ddst = 0;
|
|
|
|
} else {
|
|
|
|
loader->last = (whdr->mode == GIF_PREV) ? loader->last : (unsigned long)(whdr->ifrm + 1);
|
|
|
|
pict = (uint32_t *)((whdr->mode == GIF_PREV) ? pictw.ptr() : prevdw.ptr());
|
|
|
|
prev = (uint32_t *)((whdr->mode == GIF_PREV) ? prevdw.ptr() : pictw.ptr());
|
|
|
|
|
|
|
|
for (x = (uint32_t)(whdr->xdim * whdr->ydim); --x; pict[x - 1] = prev[x - 1]) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (whdr->mode == GIF_BKGD) { /** cutting a hole for the next frame **/
|
|
|
|
for (whdr->bptr[0] = (uint8_t)((whdr->tran >= 0) ? whdr->tran : whdr->bkgd), y = 0, pict = (uint32_t *)pictw.ptr(); y < (uint32_t)whdr->fryd; y++) {
|
|
|
|
for (x = 0; x < (uint32_t)whdr->frxd; x++) {
|
2022-12-10 19:06:41 +01:00
|
|
|
pict[(uint32_t)whdr->xdim * y + x + ddst] = RGBA(0);
|
2022-12-10 18:34:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-10 19:06:41 +01:00
|
|
|
#undef RGBA
|
2022-12-10 18:34:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void GIFLoader::load_gif(const String &file) {
|
|
|
|
_images.clear();
|
|
|
|
|
|
|
|
FileAccess *f = FileAccess::open(file, FileAccess::READ);
|
|
|
|
ERR_FAIL_COND(!f);
|
|
|
|
|
|
|
|
//gif_load seems to need +2 bytes at the end
|
|
|
|
data.resize(f->get_len() + 2);
|
|
|
|
f->get_buffer(data.ptrw(), data.size() - 2);
|
|
|
|
data.set(data.size() - 2, 0);
|
|
|
|
data.set(data.size() - 1, 0);
|
|
|
|
|
|
|
|
memdelete(f);
|
|
|
|
|
|
|
|
GIF_Load((void *)data.ptrw(), (long)data.size(), gif_frame, 0, (void *)this, 0L);
|
|
|
|
|
|
|
|
data.clear();
|
2022-12-10 17:14:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
GIFLoader::GIFLoader() {
|
2022-12-10 18:34:58 +01:00
|
|
|
size = 0;
|
|
|
|
last = 0;
|
2022-12-10 17:14:50 +01:00
|
|
|
}
|
|
|
|
GIFLoader::~GIFLoader() {
|
|
|
|
}
|
|
|
|
|
|
|
|
void GIFLoader::_bind_methods() {
|
2022-12-10 18:34:58 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("get_images"), &GIFLoader::get_images);
|
2022-12-10 19:25:06 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("create_texture", "fps"), &GIFLoader::create_texture, 60);
|
|
|
|
ClassDB::bind_method(D_METHOD("load_gif", "file"), &GIFLoader::load_gif);
|
2022-12-10 17:14:50 +01:00
|
|
|
}
|