mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-08-05 14:35:56 +02:00
Decode GIF frames within Bitmap constructor
This commit is contained in:
parent
380bd1d249
commit
5c2d0ac167
1 changed files with 67 additions and 33 deletions
|
@ -373,21 +373,25 @@ struct BitmapPrivate
|
||||||
|
|
||||||
struct BitmapOpenHandler : FileSystem::OpenHandler
|
struct BitmapOpenHandler : FileSystem::OpenHandler
|
||||||
{
|
{
|
||||||
std::vector<SDL_Surface*> surfaces;
|
// Non-GIF
|
||||||
float animation_rate;
|
SDL_Surface *surface;
|
||||||
|
|
||||||
// Filled if errors from GIF reading are needed
|
// GIF
|
||||||
std::string error;
|
std::string error;
|
||||||
|
gif_animation *gif;
|
||||||
|
unsigned char *gif_data;
|
||||||
|
size_t gif_data_size;
|
||||||
|
|
||||||
|
|
||||||
BitmapOpenHandler()
|
BitmapOpenHandler()
|
||||||
: animation_rate(-1)
|
: surface(0), gif(0), gif_data(0), gif_data_size(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool tryRead(SDL_RWops &ops, const char *ext)
|
bool tryRead(SDL_RWops &ops, const char *ext)
|
||||||
{
|
{
|
||||||
if (IMG_isGIF(&ops)) {
|
if (IMG_isGIF(&ops)) {
|
||||||
// Use libnsgif to initialise the gif data
|
// Use libnsgif to initialise the gif data
|
||||||
gif_animation gif {};
|
gif = new gif_animation;
|
||||||
|
|
||||||
gif_bitmap_callback_vt gif_bitmap_callbacks = {
|
gif_bitmap_callback_vt gif_bitmap_callbacks = {
|
||||||
gif_bitmap_create,
|
gif_bitmap_create,
|
||||||
|
@ -398,30 +402,38 @@ struct BitmapOpenHandler : FileSystem::OpenHandler
|
||||||
gif_bitmap_modified
|
gif_bitmap_modified
|
||||||
};
|
};
|
||||||
|
|
||||||
gif_create(&gif, &gif_bitmap_callbacks);
|
gif_create(gif, &gif_bitmap_callbacks);
|
||||||
|
|
||||||
size_t data_size = ops.size(&ops);
|
gif_data_size = ops.size(&ops);
|
||||||
|
|
||||||
auto data = new unsigned char[data_size];
|
gif_data = new unsigned char[gif_data_size];
|
||||||
ops.seek(&ops, 0, RW_SEEK_SET);
|
ops.seek(&ops, 0, RW_SEEK_SET);
|
||||||
ops.read(&ops, data, data_size, 1);
|
ops.read(&ops, gif_data, gif_data_size, 1);
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
do {
|
do {
|
||||||
status = gif_initialise(&gif, data_size, data);
|
status = gif_initialise(gif, gif_data_size, gif_data);
|
||||||
if (status != GIF_OK && status != GIF_WORKING) {
|
if (status != GIF_OK && status != GIF_WORKING) {
|
||||||
gif_finalise(&gif);
|
gif_finalise(gif);
|
||||||
delete data;
|
delete gif;
|
||||||
|
delete gif_data;
|
||||||
error = "Failed to initialize GIF (Error " + std::to_string(status) + ")";
|
error = "Failed to initialize GIF (Error " + std::to_string(status) + ")";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} while (status != GIF_OK);
|
} while (status != GIF_OK);
|
||||||
|
|
||||||
int image_width = -1;
|
// Decode the first frame
|
||||||
int image_height = -1;
|
status = gif_decode_frame(gif, 0);
|
||||||
|
if (status != GIF_OK && status != GIF_WORKING) {
|
||||||
|
error = "Failed to decode first GIF frame. (Error " + std::to_string(status) + ")";
|
||||||
|
gif_finalise(gif);
|
||||||
|
delete gif;
|
||||||
|
delete gif_data;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/*
|
||||||
// Read every frame
|
// Read every frame
|
||||||
for (int i = 0; i < gif.frame_count; i++) {
|
for (int i = 0; i < gif->frame_count; i++) {
|
||||||
int status = gif_decode_frame(&gif, i);
|
int status = gif_decode_frame(&gif, i);
|
||||||
if (status != GIF_OK && status != GIF_WORKING) {
|
if (status != GIF_OK && status != GIF_WORKING) {
|
||||||
error = "Failed to read GIF frame " + std::to_string(i + 1) + " (Error " + std::to_string(status) + ")";
|
error = "Failed to read GIF frame " + std::to_string(i + 1) + " (Error " + std::to_string(status) + ")";
|
||||||
|
@ -452,10 +464,11 @@ struct BitmapOpenHandler : FileSystem::OpenHandler
|
||||||
|
|
||||||
gif_finalise(&gif);
|
gif_finalise(&gif);
|
||||||
delete data;
|
delete data;
|
||||||
|
*/
|
||||||
} else {
|
} else {
|
||||||
surfaces.push_back(IMG_LoadTyped_RW(&ops, 1, ext));
|
surface = IMG_LoadTyped_RW(&ops, 1, ext);
|
||||||
}
|
}
|
||||||
return (surfaces.size() > 0 && error.empty());
|
return (surface || gif);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -468,16 +481,16 @@ Bitmap::Bitmap(const char *filename)
|
||||||
// Not loaded with SDL, but I want it to be caught with the same exception type
|
// Not loaded with SDL, but I want it to be caught with the same exception type
|
||||||
throw Exception(Exception::SDLError, "Error loading image '%s': %s", filename, handler.error.c_str());
|
throw Exception(Exception::SDLError, "Error loading image '%s': %s", filename, handler.error.c_str());
|
||||||
}
|
}
|
||||||
else if (handler.surfaces.size() < 1) {
|
else if (!handler.gif && !handler.surface) {
|
||||||
throw Exception(Exception::SDLError, "Error loading image '%s': %s",
|
throw Exception(Exception::SDLError, "Error loading image '%s': %s",
|
||||||
filename, SDL_GetError());
|
filename, SDL_GetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handler.surfaces.size() > 1) {
|
if (handler.gif) {
|
||||||
p = new BitmapPrivate(this);
|
p = new BitmapPrivate(this);
|
||||||
p->animation.enabled = true;
|
p->animation.enabled = true;
|
||||||
p->animation.width = handler.surfaces[0]->w;
|
p->animation.width = handler.gif->width;
|
||||||
p->animation.height = handler.surfaces[0]->h;
|
p->animation.height = handler.gif->height;
|
||||||
|
|
||||||
if (p->animation.width >= glState.caps.maxTexSize || p->animation.height > glState.caps.maxTexSize)
|
if (p->animation.width >= glState.caps.maxTexSize || p->animation.height > glState.caps.maxTexSize)
|
||||||
{
|
{
|
||||||
|
@ -485,35 +498,56 @@ Bitmap::Bitmap(const char *filename)
|
||||||
p->animation.width, p->animation.height, glState.caps.maxTexSize, glState.caps.maxTexSize);
|
p->animation.width, p->animation.height, glState.caps.maxTexSize, glState.caps.maxTexSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
p->animation.fps = (handler.animation_rate == -1) ? shState->graphics().getFrameRate() : handler.animation_rate;
|
// Guess framerate based on the first frame's delay
|
||||||
|
p->animation.fps = 1 / ((float)handler.gif->frames[handler.gif->decoded_frame].frame_delay / 100);
|
||||||
|
if (p->animation.fps < 0) p->animation.fps = shState->graphics().getFrameRate();
|
||||||
|
|
||||||
for (SDL_Surface* s : handler.surfaces)
|
// Loop gif (Either it's looping or it's not, at the moment)
|
||||||
{
|
p->animation.loop = handler.gif->loop_count >= 0;
|
||||||
|
|
||||||
|
while (handler.gif->decoded_frame < handler.gif->frame_count - 1) {
|
||||||
TEXFBO texfbo;
|
TEXFBO texfbo;
|
||||||
try {
|
try {
|
||||||
texfbo = shState->texPool().request(p->animation.width, p->animation.height);
|
texfbo = shState->texPool().request(p->animation.width, p->animation.height);
|
||||||
}
|
}
|
||||||
catch (const Exception &e)
|
catch (const Exception &e)
|
||||||
{
|
{
|
||||||
for (SDL_Surface *s : handler.surfaces)
|
for (TEXFBO &frame : p->animation.frames)
|
||||||
SDL_FreeSurface(s);
|
shState->texPool().release(frame);
|
||||||
|
|
||||||
|
gif_finalise(handler.gif);
|
||||||
|
delete handler.gif;
|
||||||
|
delete handler.gif_data;
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
TEX::bind(texfbo.tex);
|
|
||||||
TEX::uploadImage(p->animation.width, p->animation.height, s->pixels, GL_RGBA);
|
|
||||||
|
|
||||||
|
TEX::bind(texfbo.tex);
|
||||||
|
TEX::uploadImage(p->animation.width, p->animation.height, handler.gif->frame_image, GL_RGBA);
|
||||||
p->animation.frames.push_back(texfbo);
|
p->animation.frames.push_back(texfbo);
|
||||||
|
|
||||||
|
int status = gif_decode_frame(handler.gif, handler.gif->decoded_frame + 1);
|
||||||
|
if (status != GIF_OK && status != GIF_WORKING) {
|
||||||
|
for (TEXFBO &frame : p->animation.frames)
|
||||||
|
shState->texPool().release(frame);
|
||||||
|
|
||||||
|
gif_finalise(handler.gif);
|
||||||
|
delete handler.gif;
|
||||||
|
delete handler.gif_data;
|
||||||
|
|
||||||
|
throw Exception(Exception::MKXPError, "Failed to decode frame GIF frame %i out of %i (Error %i)",
|
||||||
|
handler.gif->decoded_frame, handler.gif->frame_count, status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (SDL_Surface *s : handler.surfaces)
|
gif_finalise(handler.gif);
|
||||||
SDL_FreeSurface(s);
|
delete handler.gif;
|
||||||
|
delete handler.gif_data;
|
||||||
p->addTaintedArea(rect());
|
p->addTaintedArea(rect());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Surface *imgSurf = handler.surfaces[0];
|
SDL_Surface *imgSurf = handler.surface;
|
||||||
|
|
||||||
|
|
||||||
p->ensureFormat(imgSurf, SDL_PIXELFORMAT_ABGR8888);
|
p->ensureFormat(imgSurf, SDL_PIXELFORMAT_ABGR8888);
|
||||||
|
|
Loading…
Add table
Reference in a new issue