mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-08-04 14:05:32 +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
|
||||
{
|
||||
std::vector<SDL_Surface*> surfaces;
|
||||
float animation_rate;
|
||||
// Non-GIF
|
||||
SDL_Surface *surface;
|
||||
|
||||
// Filled if errors from GIF reading are needed
|
||||
// GIF
|
||||
std::string error;
|
||||
gif_animation *gif;
|
||||
unsigned char *gif_data;
|
||||
size_t gif_data_size;
|
||||
|
||||
|
||||
BitmapOpenHandler()
|
||||
: animation_rate(-1)
|
||||
: surface(0), gif(0), gif_data(0), gif_data_size(0)
|
||||
{}
|
||||
|
||||
bool tryRead(SDL_RWops &ops, const char *ext)
|
||||
{
|
||||
if (IMG_isGIF(&ops)) {
|
||||
// Use libnsgif to initialise the gif data
|
||||
gif_animation gif {};
|
||||
gif = new gif_animation;
|
||||
|
||||
gif_bitmap_callback_vt gif_bitmap_callbacks = {
|
||||
gif_bitmap_create,
|
||||
|
@ -398,30 +402,38 @@ struct BitmapOpenHandler : FileSystem::OpenHandler
|
|||
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.read(&ops, data, data_size, 1);
|
||||
ops.read(&ops, gif_data, gif_data_size, 1);
|
||||
|
||||
int status;
|
||||
do {
|
||||
status = gif_initialise(&gif, data_size, data);
|
||||
status = gif_initialise(gif, gif_data_size, gif_data);
|
||||
if (status != GIF_OK && status != GIF_WORKING) {
|
||||
gif_finalise(&gif);
|
||||
delete data;
|
||||
gif_finalise(gif);
|
||||
delete gif;
|
||||
delete gif_data;
|
||||
error = "Failed to initialize GIF (Error " + std::to_string(status) + ")";
|
||||
return false;
|
||||
}
|
||||
} while (status != GIF_OK);
|
||||
|
||||
int image_width = -1;
|
||||
int image_height = -1;
|
||||
|
||||
// Decode the first frame
|
||||
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
|
||||
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);
|
||||
if (status != GIF_OK && status != GIF_WORKING) {
|
||||
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);
|
||||
delete data;
|
||||
*/
|
||||
} 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
|
||||
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",
|
||||
filename, SDL_GetError());
|
||||
}
|
||||
|
||||
if (handler.surfaces.size() > 1) {
|
||||
if (handler.gif) {
|
||||
p = new BitmapPrivate(this);
|
||||
p->animation.enabled = true;
|
||||
p->animation.width = handler.surfaces[0]->w;
|
||||
p->animation.height = handler.surfaces[0]->h;
|
||||
p->animation.width = handler.gif->width;
|
||||
p->animation.height = handler.gif->height;
|
||||
|
||||
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.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;
|
||||
try {
|
||||
texfbo = shState->texPool().request(p->animation.width, p->animation.height);
|
||||
}
|
||||
catch (const Exception &e)
|
||||
{
|
||||
for (SDL_Surface *s : handler.surfaces)
|
||||
SDL_FreeSurface(s);
|
||||
for (TEXFBO &frame : p->animation.frames)
|
||||
shState->texPool().release(frame);
|
||||
|
||||
gif_finalise(handler.gif);
|
||||
delete handler.gif;
|
||||
delete handler.gif_data;
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
SDL_FreeSurface(s);
|
||||
|
||||
gif_finalise(handler.gif);
|
||||
delete handler.gif;
|
||||
delete handler.gif_data;
|
||||
p->addTaintedArea(rect());
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Surface *imgSurf = handler.surfaces[0];
|
||||
SDL_Surface *imgSurf = handler.surface;
|
||||
|
||||
|
||||
p->ensureFormat(imgSurf, SDL_PIXELFORMAT_ABGR8888);
|
||||
|
|
Loading…
Add table
Reference in a new issue