Skip to content

Commit

Permalink
Add GIF import
Browse files Browse the repository at this point in the history
Add GIF file loading
Add `ResourceFormatLoader` for `AnimatedTexture`
Add `ImageFrames` resource for handling image sequence
Add `ImageFramesLoader` for resource loading image sequences
Add `ResourceFormatLoader` for `ImageFrames`
  • Loading branch information
Spartan322 committed Nov 12, 2024
1 parent ac1a497 commit 89ca9c5
Show file tree
Hide file tree
Showing 48 changed files with 5,697 additions and 0 deletions.
5 changes: 5 additions & 0 deletions COPYRIGHT.txt
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ Comment: The FreeType Project
Copyright: 1996-2023, David Turner, Robert Wilhelm, and Werner Lemberg.
License: FTL

Files: ./thirdparty/giflib/
Comment: giflib
Copyright: 1997-2024, Eric S. Raymond
License: Expat

Files: ./thirdparty/glad/
Comment: glad
Copyright: 2013-2022, David Herberth
Expand Down
10 changes: 10 additions & 0 deletions core/io/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr;
ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_ktx_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_gif_mem_loader_func = nullptr;

// External VRAM compression function pointers.

Expand Down Expand Up @@ -3649,6 +3650,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer);
ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer);
ClassDB::bind_method(D_METHOD("load_ktx_from_buffer", "buffer"), &Image::load_ktx_from_buffer);
ClassDB::bind_method(D_METHOD("load_gif_from_buffer", "buffer"), &Image::load_gif_from_buffer);

ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0));
ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0));
Expand Down Expand Up @@ -4091,6 +4093,14 @@ Error Image::load_ktx_from_buffer(const Vector<uint8_t> &p_array) {
return _load_from_buffer(p_array, _ktx_mem_loader_func);
}

Error Image::load_gif_from_buffer(const Vector<uint8_t> &p_array) {
ERR_FAIL_NULL_V_MSG(
_gif_mem_loader_func,
ERR_UNAVAILABLE,
"The GIF module isn't enabled. Recompile the Redot editor or export template binary with the `module_gif_enabled=yes` SCons option.");
return _load_from_buffer(p_array, _gif_mem_loader_func);
}

void Image::convert_rg_to_ra_rgba8() {
ERR_FAIL_COND(format != FORMAT_RGBA8);
ERR_FAIL_COND(data.is_empty());
Expand Down
2 changes: 2 additions & 0 deletions core/io/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ class Image : public Resource {
static ImageMemLoadFunc _bmp_mem_loader_func;
static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func;
static ImageMemLoadFunc _ktx_mem_loader_func;
static ImageMemLoadFunc _gif_mem_loader_func;

// External VRAM compression function pointers.

Expand Down Expand Up @@ -405,6 +406,7 @@ class Image : public Resource {
Error load_tga_from_buffer(const Vector<uint8_t> &p_array);
Error load_bmp_from_buffer(const Vector<uint8_t> &p_array);
Error load_ktx_from_buffer(const Vector<uint8_t> &p_array);
Error load_gif_from_buffer(const Vector<uint8_t> &p_array);

Error load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale = 1.0);
Error load_svg_from_string(const String &p_svg_str, float scale = 1.0);
Expand Down
137 changes: 137 additions & 0 deletions core/io/image_frames.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/**************************************************************************/
/* image_frames.cpp */
/**************************************************************************/
/* This file is part of: */
/* REDOT ENGINE */
/* https://redotengine.org */
/**************************************************************************/
/* Copyright (c) 2024-present Redot Engine contributors */
/* (see REDOT_AUTHORS.md) */
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#include "image_frames.h"
#include "core/io/image_frames_loader.h"
#include "core/io/resource_loader.h"

ImageFramesMemLoadFunc ImageFrames::_gif_mem_loader_func = nullptr;

void ImageFrames::set_frame_count(int p_frames) {
ERR_FAIL_COND(p_frames < 1);

frames.resize(p_frames);
}

int ImageFrames::get_frame_count() const {
return frames.size();
}

void ImageFrames::set_frame_image(int p_frame, Ref<Image> p_image) {
ERR_FAIL_INDEX(p_frame, frames.size());

frames.write[p_frame].image = p_image;
}

Ref<Image> ImageFrames::get_frame_image(int p_frame) const {
ERR_FAIL_INDEX_V(p_frame, frames.size(), Ref<Image>());

return frames[p_frame].image;
}

void ImageFrames::set_frame_delay(int p_frame, float p_delay) {
ERR_FAIL_INDEX(p_frame, frames.size());

frames.write[p_frame].delay = p_delay;
}

float ImageFrames::get_frame_delay(int p_frame) const {
ERR_FAIL_INDEX_V(p_frame, frames.size(), 0);

return frames[p_frame].delay;
}

Error ImageFrames::_load_from_buffer(const Vector<uint8_t> &p_array, ImageFramesMemLoadFunc p_loader, int p_max_frames) {
int buffer_size = p_array.size();

ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER);
ERR_FAIL_NULL_V(p_loader, ERR_INVALID_PARAMETER);

const uint8_t *r = p_array.ptr();

Ref<ImageFrames> img_frames = p_loader(r, buffer_size, p_max_frames);
ERR_FAIL_COND_V(!img_frames.is_valid(), ERR_PARSE_ERROR);

copy_internals_from(img_frames);

return OK;
}

Error ImageFrames::load(const String &p_path) {
#ifdef DEBUG_ENABLED
if (p_path.begins_with("res://") && ResourceLoader::exists(p_path)) {
WARN_PRINT("Loaded resource as image frames file, this will not work on export: '" + p_path + "'. Instead, import the image frames file as an ImageFrames resource and load it normally as a resource.");
}
#endif
return ImageFramesLoader::load_image_frames(p_path, this);
}

Ref<ImageFrames> ImageFrames::load_from_file(const String &p_path) {
#ifdef DEBUG_ENABLED
if (p_path.begins_with("res://") && ResourceLoader::exists(p_path)) {
WARN_PRINT("Loaded resource as image frames file, this will not work on export: '" + p_path + "'. Instead, import the image frames file as an ImageFrames resource and load it normally as a resource.");
}
#endif
Ref<ImageFrames> img_frames;
img_frames.instantiate();
Error err = ImageFramesLoader::load_image_frames(p_path, img_frames);
if (err != OK) {
ERR_FAIL_V_MSG(Ref<ImageFrames>(), vformat("Failed to load image frames. Error %d", err));
}
return img_frames;
}

Error ImageFrames::load_gif_from_buffer(const PackedByteArray &p_array, int p_max_frames) {
ERR_FAIL_NULL_V_MSG(
_gif_mem_loader_func,
ERR_UNAVAILABLE,
"The GIF module isn't enabled. Recompile the Redot editor or export template binary with the `module_gif_enabled=yes` SCons option.");
return _load_from_buffer(p_array, _gif_mem_loader_func, p_max_frames);
}

void ImageFrames::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_frame_count", "frames"), &ImageFrames::set_frame_count);
ClassDB::bind_method(D_METHOD("get_frame_count"), &ImageFrames::get_frame_count);

ClassDB::bind_method(D_METHOD("set_frame_image", "frame", "image"), &ImageFrames::set_frame_image);
ClassDB::bind_method(D_METHOD("get_frame_image", "frame"), &ImageFrames::get_frame_image);

ClassDB::bind_method(D_METHOD("set_frame_delay", "frame", "delay"), &ImageFrames::set_frame_delay);
ClassDB::bind_method(D_METHOD("get_frame_delay", "frame"), &ImageFrames::get_frame_delay);

ClassDB::bind_method(D_METHOD("load", "path"), &ImageFrames::load);
ClassDB::bind_static_method("ImageFrames", D_METHOD("load_from_file", "path"), &ImageFrames::load_from_file);

ClassDB::bind_method(D_METHOD("load_gif_from_buffer", "buffer", "max_frames"), &ImageFrames::load_gif_from_buffer);

ADD_PROPERTY(PropertyInfo(Variant::INT, "frame_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_frame_count", "get_frame_count");
}
82 changes: 82 additions & 0 deletions core/io/image_frames.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**************************************************************************/
/* image_frames.h */
/**************************************************************************/
/* This file is part of: */
/* REDOT ENGINE */
/* https://redotengine.org */
/**************************************************************************/
/* Copyright (c) 2024-present Redot Engine contributors */
/* (see REDOT_AUTHORS.md) */
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#ifndef IMAGE_FRAMES_H
#define IMAGE_FRAMES_H

#include "core/io/image.h"
#include "core/io/resource.h"
#include "core/variant/variant.h"

class ImageFrames;
typedef Ref<ImageFrames> (*ImageFramesMemLoadFunc)(const uint8_t *p_png, int p_size, int p_max_frames);

class ImageFrames : public Resource {
GDCLASS(ImageFrames, Resource);

public:
static ImageFramesMemLoadFunc _gif_mem_loader_func;

private:
struct Frame {
Ref<Image> image;
float delay = 1.0;
};

Vector<Frame> frames;

Error _load_from_buffer(const Vector<uint8_t> &p_array, ImageFramesMemLoadFunc p_loader, int p_max_frames);

void copy_internals_from(const Ref<ImageFrames> &p_frames) {
ERR_FAIL_COND_MSG(p_frames.is_null(), "Cannot copy image internals: invalid ImageFrames object.");
frames = p_frames->frames;
}

protected:
static void _bind_methods();

public:
void set_frame_count(int p_frames);
int get_frame_count() const;

void set_frame_image(int p_frame, Ref<Image> p_image);
Ref<Image> get_frame_image(int p_frame) const;

void set_frame_delay(int p_frame, float p_delay);
float get_frame_delay(int p_frame) const;

Error load(const String &p_path);
static Ref<ImageFrames> load_from_file(const String &p_path);
Error load_gif_from_buffer(const PackedByteArray &p_array, int p_max_frames = 0);
};

#endif // IMAGE_FRAMES_H
Loading

0 comments on commit 89ca9c5

Please sign in to comment.