mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-08-09 00:15:33 +02:00
Support animated GIFs
This commit is contained in:
parent
0ee00d1137
commit
f98b91ff9a
12 changed files with 2514 additions and 16 deletions
|
@ -436,6 +436,131 @@ RB_METHOD(bitmapGetMega){
|
||||||
return rb_bool_new(b->isMega());
|
return rb_bool_new(b->isMega());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RB_METHOD(bitmapGetAnimated){
|
||||||
|
RB_UNUSED_PARAM;
|
||||||
|
|
||||||
|
rb_check_argc(argc, 0);
|
||||||
|
|
||||||
|
Bitmap *b = getPrivateData<Bitmap>(self);
|
||||||
|
|
||||||
|
return rb_bool_new(b->isAnimated());
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_METHOD(bitmapGetPlaying){
|
||||||
|
RB_UNUSED_PARAM;
|
||||||
|
|
||||||
|
rb_check_argc(argc, 0);
|
||||||
|
|
||||||
|
Bitmap *b = getPrivateData<Bitmap>(self);
|
||||||
|
|
||||||
|
return rb_bool_new(b->isPlaying());
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_METHOD(bitmapSetPlaying){
|
||||||
|
RB_UNUSED_PARAM;
|
||||||
|
|
||||||
|
bool play;
|
||||||
|
|
||||||
|
rb_get_args(argc, argv, "b", &play RB_ARG_END);
|
||||||
|
|
||||||
|
Bitmap *b = getPrivateData<Bitmap>(self);
|
||||||
|
|
||||||
|
(play) ? b->play() : b->stop();
|
||||||
|
|
||||||
|
return RUBY_Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_METHOD(bitmapGotoStop){
|
||||||
|
RB_UNUSED_PARAM;
|
||||||
|
|
||||||
|
int frame;
|
||||||
|
|
||||||
|
rb_get_args(argc, argv, "i", &frame RB_ARG_END);
|
||||||
|
|
||||||
|
Bitmap *b = getPrivateData<Bitmap>(self);
|
||||||
|
|
||||||
|
b->gotoAndStop(frame);
|
||||||
|
|
||||||
|
return RUBY_Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_METHOD(bitmapGotoPlay){
|
||||||
|
RB_UNUSED_PARAM;
|
||||||
|
|
||||||
|
int frame;
|
||||||
|
|
||||||
|
rb_get_args(argc, argv, "i", &frame RB_ARG_END);
|
||||||
|
|
||||||
|
Bitmap *b = getPrivateData<Bitmap>(self);
|
||||||
|
|
||||||
|
b->gotoAndPlay(frame);
|
||||||
|
|
||||||
|
return RUBY_Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_METHOD(bitmapFrames){
|
||||||
|
RB_UNUSED_PARAM;
|
||||||
|
|
||||||
|
rb_check_argc(argc, 0);
|
||||||
|
|
||||||
|
Bitmap *b = getPrivateData<Bitmap>(self);
|
||||||
|
|
||||||
|
return INT2NUM(b->numFrames());
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_METHOD(bitmapCurrentFrame){
|
||||||
|
RB_UNUSED_PARAM;
|
||||||
|
|
||||||
|
rb_check_argc(argc, 0);
|
||||||
|
|
||||||
|
Bitmap *b = getPrivateData<Bitmap>(self);
|
||||||
|
|
||||||
|
return INT2NUM(b->currentFrameI());
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_METHOD(bitmapSetFPS){
|
||||||
|
RB_UNUSED_PARAM;
|
||||||
|
|
||||||
|
float fps;
|
||||||
|
rb_get_args(argc, argv, "f", &fps RB_ARG_END);
|
||||||
|
|
||||||
|
Bitmap *b = getPrivateData<Bitmap>(self);
|
||||||
|
|
||||||
|
b->setAnimationFPS(fps);
|
||||||
|
|
||||||
|
return RUBY_Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_METHOD(bitmapGetFPS){
|
||||||
|
RB_UNUSED_PARAM;
|
||||||
|
|
||||||
|
rb_check_argc(argc, 0);
|
||||||
|
|
||||||
|
Bitmap *b = getPrivateData<Bitmap>(self);
|
||||||
|
return rb_float_new(b->getAnimationFPS());
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_METHOD(bitmapSetLooping){
|
||||||
|
RB_UNUSED_PARAM;
|
||||||
|
|
||||||
|
bool loop;
|
||||||
|
rb_get_args(argc, argv, "b", &loop RB_ARG_END);
|
||||||
|
|
||||||
|
Bitmap *b = getPrivateData<Bitmap>(self);
|
||||||
|
b->setLooping(loop);
|
||||||
|
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_METHOD(bitmapGetLooping){
|
||||||
|
RB_UNUSED_PARAM;
|
||||||
|
|
||||||
|
rb_check_argc(argc, 0);
|
||||||
|
|
||||||
|
Bitmap *b = getPrivateData<Bitmap>(self);
|
||||||
|
return rb_bool_new(b->getLooping());
|
||||||
|
}
|
||||||
|
|
||||||
RB_METHOD(bitmapGetMaxSize){
|
RB_METHOD(bitmapGetMaxSize){
|
||||||
RB_UNUSED_PARAM;
|
RB_UNUSED_PARAM;
|
||||||
|
|
||||||
|
@ -501,5 +626,19 @@ void bitmapBindingInit() {
|
||||||
_rb_define_method(klass, "mega?", bitmapGetMega);
|
_rb_define_method(klass, "mega?", bitmapGetMega);
|
||||||
rb_define_singleton_method(klass, "max_size", RUBY_METHOD_FUNC(bitmapGetMaxSize), -1);
|
rb_define_singleton_method(klass, "max_size", RUBY_METHOD_FUNC(bitmapGetMaxSize), -1);
|
||||||
|
|
||||||
|
_rb_define_method(klass, "animated?", bitmapGetAnimated);
|
||||||
|
_rb_define_method(klass, "playing", bitmapGetPlaying);
|
||||||
|
_rb_define_method(klass, "playing=", bitmapSetPlaying);
|
||||||
|
_rb_define_method(klass, "goto_and_stop", bitmapGotoStop);
|
||||||
|
_rb_define_method(klass, "goto_and_play", bitmapGotoPlay);
|
||||||
|
_rb_define_method(klass, "frame_count", bitmapFrames);
|
||||||
|
_rb_define_method(klass, "current_frame", bitmapCurrentFrame);
|
||||||
|
_rb_define_method(klass, "frame_rate", bitmapGetFPS);
|
||||||
|
|
||||||
|
// For some reason Ruby says "screw this function in particular"
|
||||||
|
//_rb_define_method(klass, "frame_rate=", bitmapSetFPS);
|
||||||
|
_rb_define_method(klass, "looping", bitmapGetLooping);
|
||||||
|
_rb_define_method(klass, "looping=", bitmapSetLooping);
|
||||||
|
|
||||||
INIT_PROP_BIND(Bitmap, Font, "font");
|
INIT_PROP_BIND(Bitmap, Font, "font");
|
||||||
}
|
}
|
||||||
|
|
|
@ -286,6 +286,14 @@
|
||||||
3B97F77625E6182100A569B5 /* libSDL2_sound.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B97F77525E6182100A569B5 /* libSDL2_sound.a */; };
|
3B97F77625E6182100A569B5 /* libSDL2_sound.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B97F77525E6182100A569B5 /* libSDL2_sound.a */; };
|
||||||
3B97F77725E6182100A569B5 /* libSDL2_sound.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B97F77525E6182100A569B5 /* libSDL2_sound.a */; };
|
3B97F77725E6182100A569B5 /* libSDL2_sound.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B97F77525E6182100A569B5 /* libSDL2_sound.a */; };
|
||||||
3BA08E9B256638C900449CFF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46925651C1B003DAD8A /* AudioToolbox.framework */; };
|
3BA08E9B256638C900449CFF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46925651C1B003DAD8A /* AudioToolbox.framework */; };
|
||||||
|
3BA69454263DAB53004194EB /* libnsgif.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA6944E263DAB53004194EB /* libnsgif.c */; };
|
||||||
|
3BA69455263DAB53004194EB /* libnsgif.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA6944E263DAB53004194EB /* libnsgif.c */; };
|
||||||
|
3BA69456263DAB53004194EB /* libnsgif.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA6944E263DAB53004194EB /* libnsgif.c */; };
|
||||||
|
3BA69457263DAB53004194EB /* libnsgif.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA6944E263DAB53004194EB /* libnsgif.c */; };
|
||||||
|
3BA69458263DAB53004194EB /* lzw.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA6944F263DAB53004194EB /* lzw.c */; };
|
||||||
|
3BA69459263DAB53004194EB /* lzw.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA6944F263DAB53004194EB /* lzw.c */; };
|
||||||
|
3BA6945A263DAB53004194EB /* lzw.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA6944F263DAB53004194EB /* lzw.c */; };
|
||||||
|
3BA6945B263DAB53004194EB /* lzw.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA6944F263DAB53004194EB /* lzw.c */; };
|
||||||
3BC65CCD2584EDC60063AFF1 /* tilemapvx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B10ED7D2568E95D00372D13 /* tilemapvx.cpp */; };
|
3BC65CCD2584EDC60063AFF1 /* tilemapvx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B10ED7D2568E95D00372D13 /* tilemapvx.cpp */; };
|
||||||
3BC65CCF2584EDC60063AFF1 /* rgssad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B10ED382568E95D00372D13 /* rgssad.cpp */; };
|
3BC65CCF2584EDC60063AFF1 /* rgssad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B10ED382568E95D00372D13 /* rgssad.cpp */; };
|
||||||
3BC65CD02584EDC60063AFF1 /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B10ED462568E95D00372D13 /* input.cpp */; };
|
3BC65CD02584EDC60063AFF1 /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B10ED462568E95D00372D13 /* input.cpp */; };
|
||||||
|
@ -972,6 +980,11 @@
|
||||||
3B97F77525E6182100A569B5 /* libSDL2_sound.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libSDL2_sound.a; path = "Dependencies/build-macosx-x86_64/lib/libSDL2_sound.a"; sourceTree = "<group>"; };
|
3B97F77525E6182100A569B5 /* libSDL2_sound.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libSDL2_sound.a; path = "Dependencies/build-macosx-x86_64/lib/libSDL2_sound.a"; sourceTree = "<group>"; };
|
||||||
3BA08EA4256641ED00449CFF /* Assets.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Assets.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
|
3BA08EA4256641ED00449CFF /* Assets.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Assets.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3BA08EA6256641EE00449CFF /* Assets.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Assets.plist; sourceTree = "<group>"; };
|
3BA08EA6256641EE00449CFF /* Assets.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Assets.plist; sourceTree = "<group>"; };
|
||||||
|
3BA6944E263DAB53004194EB /* libnsgif.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libnsgif.c; sourceTree = "<group>"; };
|
||||||
|
3BA6944F263DAB53004194EB /* lzw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lzw.c; sourceTree = "<group>"; };
|
||||||
|
3BA69451263DAB53004194EB /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = "<group>"; };
|
||||||
|
3BA69452263DAB53004194EB /* lzw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzw.h; sourceTree = "<group>"; };
|
||||||
|
3BA69453263DAB53004194EB /* libnsgif.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libnsgif.h; sourceTree = "<group>"; };
|
||||||
3BC65D442584EDC60063AFF1 /* Z.arm.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Z.arm.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
3BC65D442584EDC60063AFF1 /* Z.arm.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Z.arm.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3BC65D4A2584EED10063AFF1 /* libvorbis.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libvorbis.a; path = "Dependencies/build-macosx-arm64/lib/libvorbis.a"; sourceTree = "<group>"; };
|
3BC65D4A2584EED10063AFF1 /* libvorbis.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libvorbis.a; path = "Dependencies/build-macosx-arm64/lib/libvorbis.a"; sourceTree = "<group>"; };
|
||||||
3BC65D4B2584EED10063AFF1 /* libpixman-1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libpixman-1.a"; path = "Dependencies/build-macosx-arm64/lib/libpixman-1.a"; sourceTree = "<group>"; };
|
3BC65D4B2584EED10063AFF1 /* libpixman-1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libpixman-1.a"; path = "Dependencies/build-macosx-arm64/lib/libpixman-1.a"; sourceTree = "<group>"; };
|
||||||
|
@ -1407,6 +1420,7 @@
|
||||||
3B10ED6F2568E95D00372D13 /* display */ = {
|
3B10ED6F2568E95D00372D13 /* display */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
3BA6944D263DAB53004194EB /* libnsgif */,
|
||||||
3B10ED7E2568E95D00372D13 /* gl */,
|
3B10ED7E2568E95D00372D13 /* gl */,
|
||||||
3B10ED702568E95D00372D13 /* tilemap.h */,
|
3B10ED702568E95D00372D13 /* tilemap.h */,
|
||||||
3B10ED712568E95D00372D13 /* tilemap-common.h */,
|
3B10ED712568E95D00372D13 /* tilemap-common.h */,
|
||||||
|
@ -1605,6 +1619,26 @@
|
||||||
path = misc;
|
path = misc;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
3BA6944D263DAB53004194EB /* libnsgif */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3BA6944E263DAB53004194EB /* libnsgif.c */,
|
||||||
|
3BA6944F263DAB53004194EB /* lzw.c */,
|
||||||
|
3BA69450263DAB53004194EB /* utils */,
|
||||||
|
3BA69452263DAB53004194EB /* lzw.h */,
|
||||||
|
3BA69453263DAB53004194EB /* libnsgif.h */,
|
||||||
|
);
|
||||||
|
path = libnsgif;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
3BA69450263DAB53004194EB /* utils */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3BA69451263DAB53004194EB /* log.h */,
|
||||||
|
);
|
||||||
|
path = utils;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
3BC65D492584EE690063AFF1 /* ARM64 */ = {
|
3BC65D492584EE690063AFF1 /* ARM64 */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -2041,6 +2075,7 @@
|
||||||
3B1C239425A19C600075EF5D /* etc.cpp in Sources */,
|
3B1C239425A19C600075EF5D /* etc.cpp in Sources */,
|
||||||
3B1C239525A19C600075EF5D /* shader.cpp in Sources */,
|
3B1C239525A19C600075EF5D /* shader.cpp in Sources */,
|
||||||
3B1C239625A19C600075EF5D /* tilemap.cpp in Sources */,
|
3B1C239625A19C600075EF5D /* tilemap.cpp in Sources */,
|
||||||
|
3BA6945B263DAB53004194EB /* lzw.c in Sources */,
|
||||||
3B1C239825A19C600075EF5D /* window.cpp in Sources */,
|
3B1C239825A19C600075EF5D /* window.cpp in Sources */,
|
||||||
3B1C239A25A19C600075EF5D /* input-binding.cpp in Sources */,
|
3B1C239A25A19C600075EF5D /* input-binding.cpp in Sources */,
|
||||||
3B1C239B25A19C600075EF5D /* keybindings.cpp in Sources */,
|
3B1C239B25A19C600075EF5D /* keybindings.cpp in Sources */,
|
||||||
|
@ -2054,6 +2089,7 @@
|
||||||
3B1C23A525A19C600075EF5D /* tilemapvx-binding.cpp in Sources */,
|
3B1C23A525A19C600075EF5D /* tilemapvx-binding.cpp in Sources */,
|
||||||
3B1C23A625A19C600075EF5D /* window-binding.cpp in Sources */,
|
3B1C23A625A19C600075EF5D /* window-binding.cpp in Sources */,
|
||||||
3B1C23A725A19C600075EF5D /* midisource.cpp in Sources */,
|
3B1C23A725A19C600075EF5D /* midisource.cpp in Sources */,
|
||||||
|
3BA69457263DAB53004194EB /* libnsgif.c in Sources */,
|
||||||
3B1C23A825A19C600075EF5D /* graphics-binding.cpp in Sources */,
|
3B1C23A825A19C600075EF5D /* graphics-binding.cpp in Sources */,
|
||||||
3B1C23A925A19C600075EF5D /* plane.cpp in Sources */,
|
3B1C23A925A19C600075EF5D /* plane.cpp in Sources */,
|
||||||
3B1C23AA25A19C600075EF5D /* tilequad.cpp in Sources */,
|
3B1C23AA25A19C600075EF5D /* tilequad.cpp in Sources */,
|
||||||
|
@ -2093,6 +2129,7 @@
|
||||||
3B522DDE259C1E53003301C4 /* http-binding.cpp in Sources */,
|
3B522DDE259C1E53003301C4 /* http-binding.cpp in Sources */,
|
||||||
3BC65CCD2584EDC60063AFF1 /* tilemapvx.cpp in Sources */,
|
3BC65CCD2584EDC60063AFF1 /* tilemapvx.cpp in Sources */,
|
||||||
3BC65CCF2584EDC60063AFF1 /* rgssad.cpp in Sources */,
|
3BC65CCF2584EDC60063AFF1 /* rgssad.cpp in Sources */,
|
||||||
|
3BA6945A263DAB53004194EB /* lzw.c in Sources */,
|
||||||
3BC65CD02584EDC60063AFF1 /* input.cpp in Sources */,
|
3BC65CD02584EDC60063AFF1 /* input.cpp in Sources */,
|
||||||
3BC65CD12584EDC60063AFF1 /* tilemap-binding.cpp in Sources */,
|
3BC65CD12584EDC60063AFF1 /* tilemap-binding.cpp in Sources */,
|
||||||
3BC65CD22584EDC60063AFF1 /* audio.cpp in Sources */,
|
3BC65CD22584EDC60063AFF1 /* audio.cpp in Sources */,
|
||||||
|
@ -2105,6 +2142,7 @@
|
||||||
3BC65CD82584EDC60063AFF1 /* bitmap-binding.cpp in Sources */,
|
3BC65CD82584EDC60063AFF1 /* bitmap-binding.cpp in Sources */,
|
||||||
3BC65CD92584EDC60063AFF1 /* vorbissource.cpp in Sources */,
|
3BC65CD92584EDC60063AFF1 /* vorbissource.cpp in Sources */,
|
||||||
3BC65CDB2584EDC60063AFF1 /* filesystem-binding.cpp in Sources */,
|
3BC65CDB2584EDC60063AFF1 /* filesystem-binding.cpp in Sources */,
|
||||||
|
3BA69456263DAB53004194EB /* libnsgif.c in Sources */,
|
||||||
3BC65CDD2584EDC60063AFF1 /* glstate.cpp in Sources */,
|
3BC65CDD2584EDC60063AFF1 /* glstate.cpp in Sources */,
|
||||||
3BC65CDE2584EDC60063AFF1 /* gl-fun.cpp in Sources */,
|
3BC65CDE2584EDC60063AFF1 /* gl-fun.cpp in Sources */,
|
||||||
3BC65CDF2584EDC60063AFF1 /* sprite-binding.cpp in Sources */,
|
3BC65CDF2584EDC60063AFF1 /* sprite-binding.cpp in Sources */,
|
||||||
|
@ -2169,6 +2207,7 @@
|
||||||
3B522DDC259C1E53003301C4 /* http-binding.cpp in Sources */,
|
3B522DDC259C1E53003301C4 /* http-binding.cpp in Sources */,
|
||||||
3BC65D8E2584F3AD0063AFF1 /* tilemapvx.cpp in Sources */,
|
3BC65D8E2584F3AD0063AFF1 /* tilemapvx.cpp in Sources */,
|
||||||
3BC65D902584F3AD0063AFF1 /* rgssad.cpp in Sources */,
|
3BC65D902584F3AD0063AFF1 /* rgssad.cpp in Sources */,
|
||||||
|
3BA69458263DAB53004194EB /* lzw.c in Sources */,
|
||||||
3BC65D912584F3AD0063AFF1 /* input.cpp in Sources */,
|
3BC65D912584F3AD0063AFF1 /* input.cpp in Sources */,
|
||||||
3BC65D922584F3AD0063AFF1 /* tilemap-binding.cpp in Sources */,
|
3BC65D922584F3AD0063AFF1 /* tilemap-binding.cpp in Sources */,
|
||||||
3BC65D932584F3AD0063AFF1 /* audio.cpp in Sources */,
|
3BC65D932584F3AD0063AFF1 /* audio.cpp in Sources */,
|
||||||
|
@ -2181,6 +2220,7 @@
|
||||||
3BC65D992584F3AD0063AFF1 /* bitmap-binding.cpp in Sources */,
|
3BC65D992584F3AD0063AFF1 /* bitmap-binding.cpp in Sources */,
|
||||||
3BC65D9A2584F3AD0063AFF1 /* vorbissource.cpp in Sources */,
|
3BC65D9A2584F3AD0063AFF1 /* vorbissource.cpp in Sources */,
|
||||||
3BC65D9C2584F3AD0063AFF1 /* filesystem-binding.cpp in Sources */,
|
3BC65D9C2584F3AD0063AFF1 /* filesystem-binding.cpp in Sources */,
|
||||||
|
3BA69454263DAB53004194EB /* libnsgif.c in Sources */,
|
||||||
3BC65D9E2584F3AD0063AFF1 /* glstate.cpp in Sources */,
|
3BC65D9E2584F3AD0063AFF1 /* glstate.cpp in Sources */,
|
||||||
3BC65D9F2584F3AD0063AFF1 /* gl-fun.cpp in Sources */,
|
3BC65D9F2584F3AD0063AFF1 /* gl-fun.cpp in Sources */,
|
||||||
3BC65DA02584F3AD0063AFF1 /* sprite-binding.cpp in Sources */,
|
3BC65DA02584F3AD0063AFF1 /* sprite-binding.cpp in Sources */,
|
||||||
|
@ -2245,6 +2285,7 @@
|
||||||
3B522DDD259C1E53003301C4 /* http-binding.cpp in Sources */,
|
3B522DDD259C1E53003301C4 /* http-binding.cpp in Sources */,
|
||||||
3B10EDC22568E95E00372D13 /* tilemapvx.cpp in Sources */,
|
3B10EDC22568E95E00372D13 /* tilemapvx.cpp in Sources */,
|
||||||
3B10EDA72568E95E00372D13 /* rgssad.cpp in Sources */,
|
3B10EDA72568E95E00372D13 /* rgssad.cpp in Sources */,
|
||||||
|
3BA69459263DAB53004194EB /* lzw.c in Sources */,
|
||||||
3B10EDA82568E95E00372D13 /* input.cpp in Sources */,
|
3B10EDA82568E95E00372D13 /* input.cpp in Sources */,
|
||||||
3B10EE022568E96A00372D13 /* tilemap-binding.cpp in Sources */,
|
3B10EE022568E96A00372D13 /* tilemap-binding.cpp in Sources */,
|
||||||
3B10EDB72568E95E00372D13 /* audio.cpp in Sources */,
|
3B10EDB72568E95E00372D13 /* audio.cpp in Sources */,
|
||||||
|
@ -2257,6 +2298,7 @@
|
||||||
3B10EDFF2568E96A00372D13 /* bitmap-binding.cpp in Sources */,
|
3B10EDFF2568E96A00372D13 /* bitmap-binding.cpp in Sources */,
|
||||||
3B10EDBA2568E95E00372D13 /* vorbissource.cpp in Sources */,
|
3B10EDBA2568E95E00372D13 /* vorbissource.cpp in Sources */,
|
||||||
3B10EDF62568E96A00372D13 /* filesystem-binding.cpp in Sources */,
|
3B10EDF62568E96A00372D13 /* filesystem-binding.cpp in Sources */,
|
||||||
|
3BA69455263DAB53004194EB /* libnsgif.c in Sources */,
|
||||||
3B10EDC92568E95E00372D13 /* glstate.cpp in Sources */,
|
3B10EDC92568E95E00372D13 /* glstate.cpp in Sources */,
|
||||||
3B10EDCC2568E95E00372D13 /* gl-fun.cpp in Sources */,
|
3B10EDCC2568E95E00372D13 /* gl-fun.cpp in Sources */,
|
||||||
3B10EDFB2568E96A00372D13 /* sprite-binding.cpp in Sources */,
|
3B10EDFB2568E96A00372D13 /* sprite-binding.cpp in Sources */,
|
||||||
|
|
|
@ -43,14 +43,40 @@
|
||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "eventthread.h"
|
#include "eventthread.h"
|
||||||
|
#include "graphics.h"
|
||||||
|
#include "system.h"
|
||||||
|
#include "util/util.h"
|
||||||
|
|
||||||
|
#include "debugwriter.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "libnsgif/libnsgif.h"
|
||||||
|
}
|
||||||
|
|
||||||
#define GUARD_MEGA \
|
#define GUARD_MEGA \
|
||||||
{ \
|
{ \
|
||||||
if (p->megaSurface) \
|
if (p->megaSurface) \
|
||||||
throw Exception(Exception::MKXPError, \
|
throw Exception(Exception::MKXPError, \
|
||||||
"Operation not supported for mega surfaces"); \
|
"Operation not supported for mega surfaces / animated bitmaps"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define GUARD_ANIMATED \
|
||||||
|
{ \
|
||||||
|
if (p->animation.enabled) \
|
||||||
|
throw Exception(Exception::MKXPError, \
|
||||||
|
"Operation not supported for animated bitmaps"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GUARD_UNANIMATED \
|
||||||
|
{ \
|
||||||
|
if (!p->animation.enabled) \
|
||||||
|
throw Exception(Exception::MKXPError, \
|
||||||
|
"Operation not supported for static bitmaps"); \
|
||||||
|
}
|
||||||
|
|
||||||
#define OUTLINE_SIZE 1
|
#define OUTLINE_SIZE 1
|
||||||
|
|
||||||
/* Normalize (= ensure width and
|
/* Normalize (= ensure width and
|
||||||
|
@ -74,10 +100,97 @@ static IntRect normalizedRect(const IntRect &rect)
|
||||||
return norm;
|
return norm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// libnsgif loading callbacks, taken pretty much straight from their tests
|
||||||
|
|
||||||
|
static void *gif_bitmap_create(int width, int height)
|
||||||
|
{
|
||||||
|
/* ensure a stupidly large bitmap is not created */
|
||||||
|
return calloc(width * height, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gif_bitmap_set_opaque(void *bitmap, bool opaque)
|
||||||
|
{
|
||||||
|
(void) opaque; /* unused */
|
||||||
|
(void) bitmap; /* unused */
|
||||||
|
assert(bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool gif_bitmap_test_opaque(void *bitmap)
|
||||||
|
{
|
||||||
|
(void) bitmap; /* unused */
|
||||||
|
assert(bitmap);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned char *gif_bitmap_get_buffer(void *bitmap)
|
||||||
|
{
|
||||||
|
assert(bitmap);
|
||||||
|
return (unsigned char *)bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gif_bitmap_destroy(void *bitmap)
|
||||||
|
{
|
||||||
|
assert(bitmap);
|
||||||
|
free(bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gif_bitmap_modified(void *bitmap)
|
||||||
|
{
|
||||||
|
(void) bitmap; /* unused */
|
||||||
|
assert(bitmap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
|
||||||
struct BitmapPrivate
|
struct BitmapPrivate
|
||||||
{
|
{
|
||||||
Bitmap *self;
|
Bitmap *self;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
|
||||||
|
bool enabled;
|
||||||
|
bool playing;
|
||||||
|
bool loop;
|
||||||
|
std::vector<TEXFBO> frames;
|
||||||
|
float fps;
|
||||||
|
int lastFrame;
|
||||||
|
unsigned long long startTime;
|
||||||
|
|
||||||
|
inline int currentFrameI() {
|
||||||
|
if (!playing) return lastFrame;
|
||||||
|
int i = lastFrame + ((shState->runTime() - startTime) / ((1 / fps) * 1000000));
|
||||||
|
int r = (loop) ? fmod(i, frames.size()) : (i > frames.size() - 1) ? frames.size() - 1 : i;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEXFBO ¤tFrame() {
|
||||||
|
return frames[currentFrameI()];
|
||||||
|
}
|
||||||
|
|
||||||
|
void play() {
|
||||||
|
playing = true;
|
||||||
|
startTime = shState->runTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
lastFrame = currentFrameI();
|
||||||
|
playing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seek(int frame) {
|
||||||
|
lastFrame = clamp(frame, 0, (int)frames.size());
|
||||||
|
}
|
||||||
|
} animation;
|
||||||
|
|
||||||
TEXFBO gl;
|
TEXFBO gl;
|
||||||
|
|
||||||
Font *font;
|
Font *font;
|
||||||
|
@ -109,6 +222,15 @@ struct BitmapPrivate
|
||||||
{
|
{
|
||||||
format = SDL_AllocFormat(SDL_PIXELFORMAT_ABGR8888);
|
format = SDL_AllocFormat(SDL_PIXELFORMAT_ABGR8888);
|
||||||
|
|
||||||
|
animation.width = -1;
|
||||||
|
animation.height = -1;
|
||||||
|
animation.enabled = false;
|
||||||
|
animation.playing = false;
|
||||||
|
animation.loop = true;
|
||||||
|
animation.startTime = 0;
|
||||||
|
animation.fps = -1;
|
||||||
|
animation.lastFrame = 0;
|
||||||
|
|
||||||
font = &shState->defaultFont();
|
font = &shState->defaultFont();
|
||||||
pixman_region_init(&tainted);
|
pixman_region_init(&tainted);
|
||||||
}
|
}
|
||||||
|
@ -168,13 +290,19 @@ struct BitmapPrivate
|
||||||
|
|
||||||
void bindTexture(ShaderBase &shader)
|
void bindTexture(ShaderBase &shader)
|
||||||
{
|
{
|
||||||
|
if (animation.enabled) {
|
||||||
|
TEXFBO cframe = animation.currentFrame();
|
||||||
|
TEX::bind(cframe.tex);
|
||||||
|
shader.setTexSize(Vec2i(cframe.width, cframe.height));
|
||||||
|
return;
|
||||||
|
}
|
||||||
TEX::bind(gl.tex);
|
TEX::bind(gl.tex);
|
||||||
shader.setTexSize(Vec2i(gl.width, gl.height));
|
shader.setTexSize(Vec2i(gl.width, gl.height));
|
||||||
}
|
}
|
||||||
|
|
||||||
void bindFBO()
|
void bindFBO()
|
||||||
{
|
{
|
||||||
FBO::bind(gl.fbo);
|
FBO::bind((animation.enabled) ? animation.currentFrame().fbo : gl.fbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pushSetViewport(ShaderBase &shader) const
|
void pushSetViewport(ShaderBase &shader) const
|
||||||
|
@ -235,16 +363,90 @@ struct BitmapPrivate
|
||||||
|
|
||||||
struct BitmapOpenHandler : FileSystem::OpenHandler
|
struct BitmapOpenHandler : FileSystem::OpenHandler
|
||||||
{
|
{
|
||||||
SDL_Surface *surf;
|
std::vector<SDL_Surface*> surfaces;
|
||||||
|
float animation_rate;
|
||||||
|
|
||||||
|
// Filled if errors from GIF reading are needed
|
||||||
|
std::string error;
|
||||||
|
|
||||||
BitmapOpenHandler()
|
BitmapOpenHandler()
|
||||||
: surf(0)
|
: animation_rate(-1)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool tryRead(SDL_RWops &ops, const char *ext)
|
bool tryRead(SDL_RWops &ops, const char *ext)
|
||||||
{
|
{
|
||||||
surf = IMG_LoadTyped_RW(&ops, 1, ext);
|
if (IMG_isGIF(&ops)) {
|
||||||
return surf != 0;
|
// Use libnsgif to initialise the gif data
|
||||||
|
gif_animation gif {};
|
||||||
|
|
||||||
|
gif_bitmap_callback_vt gif_bitmap_callbacks = {
|
||||||
|
gif_bitmap_create,
|
||||||
|
gif_bitmap_destroy,
|
||||||
|
gif_bitmap_get_buffer,
|
||||||
|
gif_bitmap_set_opaque,
|
||||||
|
gif_bitmap_test_opaque,
|
||||||
|
gif_bitmap_modified
|
||||||
|
};
|
||||||
|
|
||||||
|
gif_create(&gif, &gif_bitmap_callbacks);
|
||||||
|
|
||||||
|
size_t data_size = ops.size(&ops);
|
||||||
|
|
||||||
|
auto data = new unsigned char[data_size];
|
||||||
|
ops.seek(&ops, 0, RW_SEEK_SET);
|
||||||
|
ops.read(&ops, data, data_size, 1);
|
||||||
|
|
||||||
|
int status;
|
||||||
|
do {
|
||||||
|
status = gif_initialise(&gif, data_size, data);
|
||||||
|
if (status != GIF_OK && status != GIF_WORKING) {
|
||||||
|
gif_finalise(&gif);
|
||||||
|
delete 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;
|
||||||
|
|
||||||
|
// Read every frame
|
||||||
|
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) + ")";
|
||||||
|
for (SDL_Surface *s : surfaces)
|
||||||
|
SDL_FreeSurface(s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image_width == -1 || !image_height == -1) {
|
||||||
|
image_width = gif.width;
|
||||||
|
image_height = gif.height;
|
||||||
|
}
|
||||||
|
else if (gif.width != image_width || gif.height != image_height) {
|
||||||
|
error = "Failed to read GIF (Varying frame size)";
|
||||||
|
for (SDL_Surface *s : surfaces)
|
||||||
|
SDL_FreeSurface(s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animation_rate == -1 && gif.frames[gif.decoded_frame].frame_delay) {
|
||||||
|
animation_rate = 1 / ((float)gif.frames[gif.decoded_frame].frame_delay / 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Surface *s = SDL_CreateRGBSurfaceWithFormat(0, gif.width, gif.height, 32, SDL_PIXELFORMAT_ABGR8888);
|
||||||
|
SDL_SetSurfaceBlendMode(s, SDL_BLENDMODE_NONE);
|
||||||
|
memcpy(s->pixels, gif.frame_image, gif.width * gif.height * 4);
|
||||||
|
surfaces.push_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
gif_finalise(&gif);
|
||||||
|
delete data;
|
||||||
|
} else {
|
||||||
|
surfaces.push_back(IMG_LoadTyped_RW(&ops, 1, ext));
|
||||||
|
}
|
||||||
|
return (surfaces.size() > 0 && error.empty());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -252,11 +454,59 @@ Bitmap::Bitmap(const char *filename)
|
||||||
{
|
{
|
||||||
BitmapOpenHandler handler;
|
BitmapOpenHandler handler;
|
||||||
shState->fileSystem().openRead(handler, filename);
|
shState->fileSystem().openRead(handler, filename);
|
||||||
SDL_Surface *imgSurf = handler.surf;
|
|
||||||
|
|
||||||
if (!imgSurf)
|
if (!handler.error.empty()) {
|
||||||
|
// 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) {
|
||||||
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) {
|
||||||
|
p = new BitmapPrivate(this);
|
||||||
|
p->animation.enabled = true;
|
||||||
|
p->animation.width = handler.surfaces[0]->w;
|
||||||
|
p->animation.height = handler.surfaces[0]->h;
|
||||||
|
|
||||||
|
if (p->animation.width >= glState.caps.maxTexSize || p->animation.height > glState.caps.maxTexSize)
|
||||||
|
{
|
||||||
|
throw new Exception(Exception::MKXPError, "Animation too large (%ix%i, max %ix%i)",
|
||||||
|
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;
|
||||||
|
|
||||||
|
for (SDL_Surface* s : handler.surfaces)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
TEX::bind(texfbo.tex);
|
||||||
|
TEX::uploadImage(p->animation.width, p->animation.height, s->pixels, GL_RGBA);
|
||||||
|
|
||||||
|
p->animation.frames.push_back(texfbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SDL_Surface *s : handler.surfaces)
|
||||||
|
SDL_FreeSurface(s);
|
||||||
|
|
||||||
|
p->addTaintedArea(rect());
|
||||||
|
p->animation.play();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Surface *imgSurf = handler.surfaces[0];
|
||||||
|
|
||||||
|
|
||||||
p->ensureFormat(imgSurf, SDL_PIXELFORMAT_ABGR8888);
|
p->ensureFormat(imgSurf, SDL_PIXELFORMAT_ABGR8888);
|
||||||
|
|
||||||
|
@ -356,6 +606,7 @@ Bitmap::Bitmap(void *pixeldata, int width, int height)
|
||||||
Bitmap::Bitmap(const Bitmap &other)
|
Bitmap::Bitmap(const Bitmap &other)
|
||||||
{
|
{
|
||||||
other.ensureNonMega();
|
other.ensureNonMega();
|
||||||
|
other.ensureNonAnimated();
|
||||||
|
|
||||||
p = new BitmapPrivate(this);
|
p = new BitmapPrivate(this);
|
||||||
|
|
||||||
|
@ -376,6 +627,9 @@ int Bitmap::width() const
|
||||||
if (p->megaSurface)
|
if (p->megaSurface)
|
||||||
return p->megaSurface->w;
|
return p->megaSurface->w;
|
||||||
|
|
||||||
|
if (p->animation.enabled)
|
||||||
|
return p->animation.width;
|
||||||
|
|
||||||
return p->gl.width;
|
return p->gl.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,13 +640,22 @@ int Bitmap::height() const
|
||||||
if (p->megaSurface)
|
if (p->megaSurface)
|
||||||
return p->megaSurface->h;
|
return p->megaSurface->h;
|
||||||
|
|
||||||
|
if (p->animation.enabled)
|
||||||
|
return p->animation.height;
|
||||||
|
|
||||||
return p->gl.height;
|
return p->gl.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bitmap::isMega() const{
|
bool Bitmap::isMega() const{
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
return p->megaSurface;
|
return p->megaSurface || p->animation.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bitmap::isAnimated() const {
|
||||||
|
guardDisposed();
|
||||||
|
|
||||||
|
return p->animation.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
IntRect Bitmap::rect() const
|
IntRect Bitmap::rect() const
|
||||||
|
@ -431,7 +694,10 @@ void Bitmap::stretchBlt(const IntRect &destRect,
|
||||||
{
|
{
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
// Don't need this, right? This function is fine with megasurfaces it seems
|
||||||
|
//GUARD_MEGA;
|
||||||
|
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
if (source.isDisposed())
|
if (source.isDisposed())
|
||||||
return;
|
return;
|
||||||
|
@ -581,6 +847,7 @@ void Bitmap::fillRect(const IntRect &rect, const Vec4 &color)
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
p->fillRect(rect, color);
|
p->fillRect(rect, color);
|
||||||
|
|
||||||
|
@ -609,6 +876,7 @@ void Bitmap::gradientFillRect(const IntRect &rect,
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
SimpleColorShader &shader = shState->shaders().simpleColor;
|
SimpleColorShader &shader = shState->shaders().simpleColor;
|
||||||
shader.bind();
|
shader.bind();
|
||||||
|
@ -655,6 +923,7 @@ void Bitmap::clearRect(const IntRect &rect)
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
p->fillRect(rect, Vec4());
|
p->fillRect(rect, Vec4());
|
||||||
|
|
||||||
|
@ -666,6 +935,7 @@ void Bitmap::blur()
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
Quad &quad = shState->gpQuad();
|
Quad &quad = shState->gpQuad();
|
||||||
FloatRect rect(0, 0, width(), height());
|
FloatRect rect(0, 0, width(), height());
|
||||||
|
@ -711,6 +981,7 @@ void Bitmap::radialBlur(int angle, int divisions)
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
angle = clamp<int>(angle, 0, 359);
|
angle = clamp<int>(angle, 0, 359);
|
||||||
divisions = clamp<int>(divisions, 2, 100);
|
divisions = clamp<int>(divisions, 2, 100);
|
||||||
|
@ -806,6 +1077,7 @@ void Bitmap::clear()
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
p->bindFBO();
|
p->bindFBO();
|
||||||
|
|
||||||
|
@ -833,6 +1105,7 @@ Color Bitmap::getPixel(int x, int y) const
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
if (x < 0 || y < 0 || x >= width() || y >= height())
|
if (x < 0 || y < 0 || x >= width() || y >= height())
|
||||||
return Vec4();
|
return Vec4();
|
||||||
|
@ -863,6 +1136,7 @@ void Bitmap::setPixel(int x, int y, const Color &color)
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
uint8_t pixel[] =
|
uint8_t pixel[] =
|
||||||
{
|
{
|
||||||
|
@ -896,6 +1170,7 @@ bool Bitmap::getRaw(void *output, int output_size)
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
FBO::bind(p->gl.fbo);
|
FBO::bind(p->gl.fbo);
|
||||||
glReadPixels(0,0,width(),height(),GL_RGBA,GL_UNSIGNED_BYTE,output);
|
glReadPixels(0,0,width(),height(),GL_RGBA,GL_UNSIGNED_BYTE,output);
|
||||||
|
@ -910,6 +1185,7 @@ void Bitmap::replaceRaw(void *pixel_data, int size)
|
||||||
if (size != w*h*4) return;
|
if (size != w*h*4) return;
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
TEX::bind(p->gl.tex);
|
TEX::bind(p->gl.tex);
|
||||||
TEX::uploadImage(w, h, pixel_data, GL_RGBA);
|
TEX::uploadImage(w, h, pixel_data, GL_RGBA);
|
||||||
|
@ -923,6 +1199,7 @@ void Bitmap::saveToFile(const char *filename)
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
SDL_Surface *surf = SDL_CreateRGBSurface(0, width(), height(),p->format->BitsPerPixel, p->format->Rmask,p->format->Gmask,p->format->Bmask,p->format->Amask);
|
SDL_Surface *surf = SDL_CreateRGBSurface(0, width(), height(),p->format->BitsPerPixel, p->format->Rmask,p->format->Gmask,p->format->Bmask,p->format->Amask);
|
||||||
|
|
||||||
|
@ -972,6 +1249,7 @@ void Bitmap::hueChange(int hue)
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
if ((hue % 360) == 0)
|
if ((hue % 360) == 0)
|
||||||
return;
|
return;
|
||||||
|
@ -1119,6 +1397,7 @@ void Bitmap::drawText(const IntRect &rect, const char *str, int align)
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
std::string fixed = fixupString(str);
|
std::string fixed = fixupString(str);
|
||||||
str = fixed.c_str();
|
str = fixed.c_str();
|
||||||
|
@ -1380,6 +1659,7 @@ IntRect Bitmap::textSize(const char *str)
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
|
||||||
TTF_Font *font = p->font->getSdlFont();
|
TTF_Font *font = p->font->getSdlFont();
|
||||||
|
|
||||||
|
@ -1415,7 +1695,7 @@ void Bitmap::setInitFont(Font *value)
|
||||||
|
|
||||||
TEXFBO &Bitmap::getGLTypes()
|
TEXFBO &Bitmap::getGLTypes()
|
||||||
{
|
{
|
||||||
return p->gl;
|
return (p->animation.enabled) ? p->animation.currentFrame() : p->gl;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Surface *Bitmap::megaSurface() const
|
SDL_Surface *Bitmap::megaSurface() const
|
||||||
|
@ -1431,6 +1711,94 @@ void Bitmap::ensureNonMega() const
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bitmap::ensureNonAnimated() const
|
||||||
|
{
|
||||||
|
if (isDisposed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
GUARD_ANIMATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitmap::stop()
|
||||||
|
{
|
||||||
|
GUARD_UNANIMATED;
|
||||||
|
if (!p->animation.playing) return;
|
||||||
|
|
||||||
|
p->animation.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitmap::play()
|
||||||
|
{
|
||||||
|
GUARD_UNANIMATED;
|
||||||
|
if (p->animation.playing) return;
|
||||||
|
p->animation.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bitmap::isPlaying()
|
||||||
|
{
|
||||||
|
GUARD_UNANIMATED;
|
||||||
|
return (p->animation.playing);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitmap::gotoAndStop(int frame)
|
||||||
|
{
|
||||||
|
GUARD_UNANIMATED;
|
||||||
|
|
||||||
|
p->animation.stop();
|
||||||
|
p->animation.seek(frame);
|
||||||
|
}
|
||||||
|
void Bitmap::gotoAndPlay(int frame)
|
||||||
|
{
|
||||||
|
GUARD_UNANIMATED;
|
||||||
|
|
||||||
|
p->animation.stop();
|
||||||
|
p->animation.seek(frame);
|
||||||
|
p->animation.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Bitmap::numFrames()
|
||||||
|
{
|
||||||
|
GUARD_UNANIMATED;
|
||||||
|
return p->animation.frames.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Bitmap::currentFrameI() const
|
||||||
|
{
|
||||||
|
GUARD_UNANIMATED;
|
||||||
|
return p->animation.currentFrameI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitmap::setAnimationFPS(float FPS)
|
||||||
|
{
|
||||||
|
GUARD_UNANIMATED;
|
||||||
|
|
||||||
|
bool restart = p->animation.playing;
|
||||||
|
p->animation.stop();
|
||||||
|
p->animation.fps = FPS;
|
||||||
|
if (restart) p->animation.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Bitmap::getAnimationFPS()
|
||||||
|
{
|
||||||
|
GUARD_UNANIMATED;
|
||||||
|
|
||||||
|
return p->animation.fps;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitmap::setLooping(bool loop)
|
||||||
|
{
|
||||||
|
GUARD_UNANIMATED;
|
||||||
|
|
||||||
|
p->animation.loop = loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bitmap::getLooping()
|
||||||
|
{
|
||||||
|
GUARD_UNANIMATED;
|
||||||
|
|
||||||
|
return p->animation.loop;
|
||||||
|
}
|
||||||
|
|
||||||
void Bitmap::bindTex(ShaderBase &shader)
|
void Bitmap::bindTex(ShaderBase &shader)
|
||||||
{
|
{
|
||||||
p->bindTexture(shader);
|
p->bindTexture(shader);
|
||||||
|
@ -1449,6 +1817,10 @@ void Bitmap::releaseResources()
|
||||||
{
|
{
|
||||||
if (p->megaSurface)
|
if (p->megaSurface)
|
||||||
SDL_FreeSurface(p->megaSurface);
|
SDL_FreeSurface(p->megaSurface);
|
||||||
|
else if (p->animation.enabled) {
|
||||||
|
for (TEXFBO &tex : p->animation.frames)
|
||||||
|
shState->texPool().release(tex);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
shState->texPool().release(p->gl);
|
shState->texPool().release(p->gl);
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ public:
|
||||||
int width() const;
|
int width() const;
|
||||||
int height() const;
|
int height() const;
|
||||||
bool isMega() const;
|
bool isMega() const;
|
||||||
|
bool isAnimated() const;
|
||||||
|
|
||||||
IntRect rect() const;
|
IntRect rect() const;
|
||||||
|
|
||||||
|
@ -116,6 +117,22 @@ public:
|
||||||
TEXFBO &getGLTypes();
|
TEXFBO &getGLTypes();
|
||||||
SDL_Surface *megaSurface() const;
|
SDL_Surface *megaSurface() const;
|
||||||
void ensureNonMega() const;
|
void ensureNonMega() const;
|
||||||
|
void ensureNonAnimated() const;
|
||||||
|
|
||||||
|
// GIF functions
|
||||||
|
void stop();
|
||||||
|
void play();
|
||||||
|
bool isPlaying();
|
||||||
|
void gotoAndStop(int frame);
|
||||||
|
void gotoAndPlay(int frame);
|
||||||
|
int numFrames();
|
||||||
|
int currentFrameI() const;
|
||||||
|
|
||||||
|
void setAnimationFPS(float FPS);
|
||||||
|
float getAnimationFPS();
|
||||||
|
|
||||||
|
void setLooping(bool loop);
|
||||||
|
bool getLooping();
|
||||||
|
|
||||||
/* Binds the backing texture and sets the correct
|
/* Binds the backing texture and sets the correct
|
||||||
* texture size uniform in shader */
|
* texture size uniform in shader */
|
||||||
|
|
1228
src/display/libnsgif/libnsgif.c
Normal file
1228
src/display/libnsgif/libnsgif.c
Normal file
File diff suppressed because it is too large
Load diff
192
src/display/libnsgif/libnsgif.h
Normal file
192
src/display/libnsgif/libnsgif.h
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>
|
||||||
|
* Copyright 2008 Sean Fox <dyntryx@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
|
||||||
|
* Licenced under the MIT License,
|
||||||
|
* http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* Interface to progressive animated GIF file decoding.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LIBNSGIF_H_
|
||||||
|
#define _LIBNSGIF_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
/* Error return values */
|
||||||
|
typedef enum {
|
||||||
|
GIF_WORKING = 1,
|
||||||
|
GIF_OK = 0,
|
||||||
|
GIF_INSUFFICIENT_FRAME_DATA = -1,
|
||||||
|
GIF_FRAME_DATA_ERROR = -2,
|
||||||
|
GIF_INSUFFICIENT_DATA = -3,
|
||||||
|
GIF_DATA_ERROR = -4,
|
||||||
|
GIF_INSUFFICIENT_MEMORY = -5,
|
||||||
|
GIF_FRAME_NO_DISPLAY = -6,
|
||||||
|
GIF_END_OF_FRAME = -7
|
||||||
|
} gif_result;
|
||||||
|
|
||||||
|
/** GIF frame data */
|
||||||
|
typedef struct gif_frame {
|
||||||
|
/** whether the frame should be displayed/animated */
|
||||||
|
bool display;
|
||||||
|
/** delay (in cs) before animating the frame */
|
||||||
|
unsigned int frame_delay;
|
||||||
|
|
||||||
|
/* Internal members are listed below */
|
||||||
|
|
||||||
|
/** offset (in bytes) to the GIF frame data */
|
||||||
|
unsigned int frame_pointer;
|
||||||
|
/** whether the frame has previously been used */
|
||||||
|
bool virgin;
|
||||||
|
/** whether the frame is totally opaque */
|
||||||
|
bool opaque;
|
||||||
|
/** whether a forcable screen redraw is required */
|
||||||
|
bool redraw_required;
|
||||||
|
/** how the previous frame should be disposed; affects plotting */
|
||||||
|
unsigned char disposal_method;
|
||||||
|
/** whether we acknoledge transparency */
|
||||||
|
bool transparency;
|
||||||
|
/** the index designating a transparent pixel */
|
||||||
|
unsigned char transparency_index;
|
||||||
|
/** x co-ordinate of redraw rectangle */
|
||||||
|
unsigned int redraw_x;
|
||||||
|
/** y co-ordinate of redraw rectangle */
|
||||||
|
unsigned int redraw_y;
|
||||||
|
/** width of redraw rectangle */
|
||||||
|
unsigned int redraw_width;
|
||||||
|
/** height of redraw rectangle */
|
||||||
|
unsigned int redraw_height;
|
||||||
|
} gif_frame;
|
||||||
|
|
||||||
|
/* API for Bitmap callbacks */
|
||||||
|
typedef void* (*gif_bitmap_cb_create)(int width, int height);
|
||||||
|
typedef void (*gif_bitmap_cb_destroy)(void *bitmap);
|
||||||
|
typedef unsigned char* (*gif_bitmap_cb_get_buffer)(void *bitmap);
|
||||||
|
typedef void (*gif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
|
||||||
|
typedef bool (*gif_bitmap_cb_test_opaque)(void *bitmap);
|
||||||
|
typedef void (*gif_bitmap_cb_modified)(void *bitmap);
|
||||||
|
|
||||||
|
/** Bitmap callbacks function table */
|
||||||
|
typedef struct gif_bitmap_callback_vt {
|
||||||
|
/** Create a bitmap. */
|
||||||
|
gif_bitmap_cb_create bitmap_create;
|
||||||
|
/** Free a bitmap. */
|
||||||
|
gif_bitmap_cb_destroy bitmap_destroy;
|
||||||
|
/** Return a pointer to the pixel data in a bitmap. */
|
||||||
|
gif_bitmap_cb_get_buffer bitmap_get_buffer;
|
||||||
|
|
||||||
|
/* Members below are optional */
|
||||||
|
|
||||||
|
/** Sets whether a bitmap should be plotted opaque. */
|
||||||
|
gif_bitmap_cb_set_opaque bitmap_set_opaque;
|
||||||
|
/** Tests whether a bitmap has an opaque alpha channel. */
|
||||||
|
gif_bitmap_cb_test_opaque bitmap_test_opaque;
|
||||||
|
/** The bitmap image has changed, so flush any persistant cache. */
|
||||||
|
gif_bitmap_cb_modified bitmap_modified;
|
||||||
|
} gif_bitmap_callback_vt;
|
||||||
|
|
||||||
|
/** GIF animation data */
|
||||||
|
typedef struct gif_animation {
|
||||||
|
/** LZW decode context */
|
||||||
|
void *lzw_ctx;
|
||||||
|
/** callbacks for bitmap functions */
|
||||||
|
gif_bitmap_callback_vt bitmap_callbacks;
|
||||||
|
/** pointer to GIF data */
|
||||||
|
unsigned char *gif_data;
|
||||||
|
/** width of GIF (may increase during decoding) */
|
||||||
|
unsigned int width;
|
||||||
|
/** heigth of GIF (may increase during decoding) */
|
||||||
|
unsigned int height;
|
||||||
|
/** number of frames decoded */
|
||||||
|
unsigned int frame_count;
|
||||||
|
/** number of frames partially decoded */
|
||||||
|
unsigned int frame_count_partial;
|
||||||
|
/** decoded frames */
|
||||||
|
gif_frame *frames;
|
||||||
|
/** current frame decoded to bitmap */
|
||||||
|
int decoded_frame;
|
||||||
|
/** currently decoded image; stored as bitmap from bitmap_create callback */
|
||||||
|
void *frame_image;
|
||||||
|
/** number of times to loop animation */
|
||||||
|
int loop_count;
|
||||||
|
|
||||||
|
/* Internal members are listed below */
|
||||||
|
|
||||||
|
/** current index into GIF data */
|
||||||
|
unsigned int buffer_position;
|
||||||
|
/** total number of bytes of GIF data available */
|
||||||
|
unsigned int buffer_size;
|
||||||
|
/** current number of frame holders */
|
||||||
|
unsigned int frame_holders;
|
||||||
|
/** index in the colour table for the background colour */
|
||||||
|
unsigned int background_index;
|
||||||
|
/** image aspect ratio (ignored) */
|
||||||
|
unsigned int aspect_ratio;
|
||||||
|
/** size of colour table (in entries) */
|
||||||
|
unsigned int colour_table_size;
|
||||||
|
/** whether the GIF has a global colour table */
|
||||||
|
bool global_colours;
|
||||||
|
/** global colour table */
|
||||||
|
unsigned int *global_colour_table;
|
||||||
|
/** local colour table */
|
||||||
|
unsigned int *local_colour_table;
|
||||||
|
|
||||||
|
/** previous frame for GIF_FRAME_RESTORE */
|
||||||
|
void *prev_frame;
|
||||||
|
/** previous frame index */
|
||||||
|
int prev_index;
|
||||||
|
/** previous frame width */
|
||||||
|
unsigned prev_width;
|
||||||
|
/** previous frame height */
|
||||||
|
unsigned prev_height;
|
||||||
|
} gif_animation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises necessary gif_animation members.
|
||||||
|
*/
|
||||||
|
void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises any workspace held by the animation and attempts to decode
|
||||||
|
* any information that hasn't already been decoded.
|
||||||
|
* If an error occurs, all previously decoded frames are retained.
|
||||||
|
*
|
||||||
|
* @return Error return value.
|
||||||
|
* - GIF_FRAME_DATA_ERROR for GIF frame data error
|
||||||
|
* - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to process
|
||||||
|
* any more frames
|
||||||
|
* - GIF_INSUFFICIENT_MEMORY for memory error
|
||||||
|
* - GIF_DATA_ERROR for GIF error
|
||||||
|
* - GIF_INSUFFICIENT_DATA for insufficient data to do anything
|
||||||
|
* - GIF_OK for successful decoding
|
||||||
|
* - GIF_WORKING for successful decoding if more frames are expected
|
||||||
|
*/
|
||||||
|
gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a GIF frame.
|
||||||
|
*
|
||||||
|
* @return Error return value. If a frame does not contain any image data,
|
||||||
|
* GIF_OK is returned and gif->current_error is set to
|
||||||
|
* GIF_FRAME_NO_DISPLAY
|
||||||
|
* - GIF_FRAME_DATA_ERROR for GIF frame data error
|
||||||
|
* - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
|
||||||
|
* - GIF_DATA_ERROR for GIF error (invalid frame header)
|
||||||
|
* - GIF_INSUFFICIENT_DATA for insufficient data to do anything
|
||||||
|
* - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
|
||||||
|
* - GIF_OK for successful decoding
|
||||||
|
*/
|
||||||
|
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases any workspace held by a gif
|
||||||
|
*/
|
||||||
|
void gif_finalise(gif_animation *gif);
|
||||||
|
|
||||||
|
#endif
|
377
src/display/libnsgif/lzw.c
Normal file
377
src/display/libnsgif/lzw.c
Normal file
|
@ -0,0 +1,377 @@
|
||||||
|
/*
|
||||||
|
* This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
|
||||||
|
* Licensed under the MIT License,
|
||||||
|
* http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*
|
||||||
|
* Copyright 2017 Michael Drake <michael.drake@codethink.co.uk>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "lzw.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief LZW decompression (implementation)
|
||||||
|
*
|
||||||
|
* Decoder for GIF LZW data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context for reading LZW data.
|
||||||
|
*
|
||||||
|
* LZW data is split over multiple sub-blocks. Each sub-block has a
|
||||||
|
* byte at the start, which says the sub-block size, and then the data.
|
||||||
|
* Zero-size sub-blocks have no data, and the biggest sub-block size is
|
||||||
|
* 255, which means there are 255 bytes of data following the sub-block
|
||||||
|
* size entry.
|
||||||
|
*
|
||||||
|
* Note that an individual LZW code can be split over up to three sub-blocks.
|
||||||
|
*/
|
||||||
|
struct lzw_read_ctx {
|
||||||
|
const uint8_t *data; /**< Pointer to start of input data */
|
||||||
|
uint32_t data_len; /**< Input data length */
|
||||||
|
uint32_t data_sb_next; /**< Offset to sub-block size */
|
||||||
|
|
||||||
|
const uint8_t *sb_data; /**< Pointer to current sub-block in data */
|
||||||
|
uint32_t sb_bit; /**< Current bit offset in sub-block */
|
||||||
|
uint32_t sb_bit_count; /**< Bit count in sub-block */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LZW dictionary entry.
|
||||||
|
*
|
||||||
|
* Records in the dictionary are composed of 1 or more entries.
|
||||||
|
* Entries point to previous entries which can be followed to compose
|
||||||
|
* the complete record. To compose the record in reverse order, take
|
||||||
|
* the `last_value` from each entry, and move to the previous entry.
|
||||||
|
* If the previous_entry's index is < the current clear_code, then it
|
||||||
|
* is the last entry in the record.
|
||||||
|
*/
|
||||||
|
struct lzw_dictionary_entry {
|
||||||
|
uint8_t last_value; /**< Last value for record ending at entry. */
|
||||||
|
uint8_t first_value; /**< First value for entry's record. */
|
||||||
|
uint16_t previous_entry; /**< Offset in dictionary to previous entry. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LZW decompression context.
|
||||||
|
*/
|
||||||
|
struct lzw_ctx {
|
||||||
|
/** Input reading context */
|
||||||
|
struct lzw_read_ctx input;
|
||||||
|
|
||||||
|
uint32_t previous_code; /**< Code read from input previously. */
|
||||||
|
uint32_t previous_code_first; /**< First value of previous code. */
|
||||||
|
|
||||||
|
uint32_t initial_code_size; /**< Starting LZW code size. */
|
||||||
|
uint32_t current_code_size; /**< Current LZW code size. */
|
||||||
|
uint32_t current_code_size_max; /**< Max code value for current size. */
|
||||||
|
|
||||||
|
uint32_t clear_code; /**< Special Clear code value */
|
||||||
|
uint32_t eoi_code; /**< Special End of Information code value */
|
||||||
|
|
||||||
|
uint32_t current_entry; /**< Next position in table to fill. */
|
||||||
|
|
||||||
|
/** Output value stack. */
|
||||||
|
uint8_t stack_base[1 << LZW_CODE_MAX];
|
||||||
|
|
||||||
|
/** LZW decode dictionary. Generated during decode. */
|
||||||
|
struct lzw_dictionary_entry table[1 << LZW_CODE_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Exported function, documented in lzw.h */
|
||||||
|
lzw_result lzw_context_create(struct lzw_ctx **ctx)
|
||||||
|
{
|
||||||
|
struct lzw_ctx *c = malloc(sizeof(*c));
|
||||||
|
if (c == NULL) {
|
||||||
|
return LZW_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ctx = c;
|
||||||
|
return LZW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Exported function, documented in lzw.h */
|
||||||
|
void lzw_context_destroy(struct lzw_ctx *ctx)
|
||||||
|
{
|
||||||
|
free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advance the context to the next sub-block in the input data.
|
||||||
|
*
|
||||||
|
* \param[in] ctx LZW reading context, updated on success.
|
||||||
|
* \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
|
||||||
|
*/
|
||||||
|
static lzw_result lzw__block_advance(struct lzw_read_ctx *ctx)
|
||||||
|
{
|
||||||
|
uint32_t block_size;
|
||||||
|
uint32_t next_block_pos = ctx->data_sb_next;
|
||||||
|
const uint8_t *data_next = ctx->data + next_block_pos;
|
||||||
|
|
||||||
|
if (next_block_pos >= ctx->data_len) {
|
||||||
|
return LZW_NO_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
block_size = *data_next;
|
||||||
|
|
||||||
|
if ((next_block_pos + block_size) >= ctx->data_len) {
|
||||||
|
return LZW_NO_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->sb_bit = 0;
|
||||||
|
ctx->sb_bit_count = block_size * 8;
|
||||||
|
|
||||||
|
if (block_size == 0) {
|
||||||
|
ctx->data_sb_next += 1;
|
||||||
|
return LZW_OK_EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->sb_data = data_next + 1;
|
||||||
|
ctx->data_sb_next += block_size + 1;
|
||||||
|
|
||||||
|
return LZW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next LZW code of given size from the raw input data.
|
||||||
|
*
|
||||||
|
* Reads codes from the input data stream coping with GIF data sub-blocks.
|
||||||
|
*
|
||||||
|
* \param[in] ctx LZW reading context, updated.
|
||||||
|
* \param[in] code_size Size of LZW code to get from data.
|
||||||
|
* \param[out] code_out Returns an LZW code on success.
|
||||||
|
* \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
|
||||||
|
*/
|
||||||
|
static inline lzw_result lzw__next_code(
|
||||||
|
struct lzw_read_ctx *ctx,
|
||||||
|
uint8_t code_size,
|
||||||
|
uint32_t *code_out)
|
||||||
|
{
|
||||||
|
uint32_t code = 0;
|
||||||
|
uint8_t current_bit = ctx->sb_bit & 0x7;
|
||||||
|
uint8_t byte_advance = (current_bit + code_size) >> 3;
|
||||||
|
|
||||||
|
assert(byte_advance <= 2);
|
||||||
|
|
||||||
|
if (ctx->sb_bit + code_size <= ctx->sb_bit_count) {
|
||||||
|
/* Fast path: code fully inside this sub-block */
|
||||||
|
const uint8_t *data = ctx->sb_data + (ctx->sb_bit >> 3);
|
||||||
|
switch (byte_advance) {
|
||||||
|
case 2: code |= data[2] << 16; /* Fall through */
|
||||||
|
case 1: code |= data[1] << 8; /* Fall through */
|
||||||
|
case 0: code |= data[0] << 0;
|
||||||
|
}
|
||||||
|
ctx->sb_bit += code_size;
|
||||||
|
} else {
|
||||||
|
/* Slow path: code spans sub-blocks */
|
||||||
|
uint8_t byte = 0;
|
||||||
|
uint8_t bits_remaining_0 = (code_size < (8 - current_bit)) ?
|
||||||
|
code_size : (8 - current_bit);
|
||||||
|
uint8_t bits_remaining_1 = code_size - bits_remaining_0;
|
||||||
|
uint8_t bits_used[3] = {
|
||||||
|
[0] = bits_remaining_0,
|
||||||
|
[1] = bits_remaining_1 < 8 ? bits_remaining_1 : 8,
|
||||||
|
[2] = bits_remaining_1 - 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const uint8_t *data = ctx->sb_data;
|
||||||
|
lzw_result res;
|
||||||
|
|
||||||
|
/* Get any data from end of this sub-block */
|
||||||
|
while (byte <= byte_advance &&
|
||||||
|
ctx->sb_bit < ctx->sb_bit_count) {
|
||||||
|
code |= data[ctx->sb_bit >> 3] << (byte << 3);
|
||||||
|
ctx->sb_bit += bits_used[byte];
|
||||||
|
byte++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we have all we need */
|
||||||
|
if (byte > byte_advance) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move to next sub-block */
|
||||||
|
res = lzw__block_advance(ctx);
|
||||||
|
if (res != LZW_OK) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*code_out = (code >> current_bit) & ((1 << code_size) - 1);
|
||||||
|
return LZW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear LZW code dictionary.
|
||||||
|
*
|
||||||
|
* \param[in] ctx LZW reading context, updated.
|
||||||
|
* \param[out] stack_pos_out Returns current stack position.
|
||||||
|
* \return LZW_OK or error code.
|
||||||
|
*/
|
||||||
|
static lzw_result lzw__clear_codes(
|
||||||
|
struct lzw_ctx *ctx,
|
||||||
|
const uint8_t ** const stack_pos_out)
|
||||||
|
{
|
||||||
|
uint32_t code;
|
||||||
|
uint8_t *stack_pos;
|
||||||
|
|
||||||
|
/* Reset dictionary building context */
|
||||||
|
ctx->current_code_size = ctx->initial_code_size + 1;
|
||||||
|
ctx->current_code_size_max = (1 << ctx->current_code_size) - 1;;
|
||||||
|
ctx->current_entry = (1 << ctx->initial_code_size) + 2;
|
||||||
|
|
||||||
|
/* There might be a sequence of clear codes, so process them all */
|
||||||
|
do {
|
||||||
|
lzw_result res = lzw__next_code(&ctx->input,
|
||||||
|
ctx->current_code_size, &code);
|
||||||
|
if (res != LZW_OK) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} while (code == ctx->clear_code);
|
||||||
|
|
||||||
|
/* The initial code must be from the initial dictionary. */
|
||||||
|
if (code > ctx->clear_code) {
|
||||||
|
return LZW_BAD_ICODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Record this initial code as "previous" code, needed during decode. */
|
||||||
|
ctx->previous_code = code;
|
||||||
|
ctx->previous_code_first = code;
|
||||||
|
|
||||||
|
/* Reset the stack, and add first non-clear code added as first item. */
|
||||||
|
stack_pos = ctx->stack_base;
|
||||||
|
*stack_pos++ = code;
|
||||||
|
|
||||||
|
*stack_pos_out = stack_pos;
|
||||||
|
return LZW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Exported function, documented in lzw.h */
|
||||||
|
lzw_result lzw_decode_init(
|
||||||
|
struct lzw_ctx *ctx,
|
||||||
|
const uint8_t *compressed_data,
|
||||||
|
uint32_t compressed_data_len,
|
||||||
|
uint32_t compressed_data_pos,
|
||||||
|
uint8_t code_size,
|
||||||
|
const uint8_t ** const stack_base_out,
|
||||||
|
const uint8_t ** const stack_pos_out)
|
||||||
|
{
|
||||||
|
struct lzw_dictionary_entry *table = ctx->table;
|
||||||
|
|
||||||
|
/* Initialise the input reading context */
|
||||||
|
ctx->input.data = compressed_data;
|
||||||
|
ctx->input.data_len = compressed_data_len;
|
||||||
|
ctx->input.data_sb_next = compressed_data_pos;
|
||||||
|
|
||||||
|
ctx->input.sb_bit = 0;
|
||||||
|
ctx->input.sb_bit_count = 0;
|
||||||
|
|
||||||
|
/* Initialise the dictionary building context */
|
||||||
|
ctx->initial_code_size = code_size;
|
||||||
|
|
||||||
|
ctx->clear_code = (1 << code_size) + 0;
|
||||||
|
ctx->eoi_code = (1 << code_size) + 1;
|
||||||
|
|
||||||
|
/* Initialise the standard dictionary entries */
|
||||||
|
for (uint32_t i = 0; i < ctx->clear_code; ++i) {
|
||||||
|
table[i].first_value = i;
|
||||||
|
table[i].last_value = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
*stack_base_out = ctx->stack_base;
|
||||||
|
return lzw__clear_codes(ctx, stack_pos_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Exported function, documented in lzw.h */
|
||||||
|
lzw_result lzw_decode(struct lzw_ctx *ctx,
|
||||||
|
const uint8_t ** const stack_pos_out)
|
||||||
|
{
|
||||||
|
lzw_result res;
|
||||||
|
uint32_t code_new;
|
||||||
|
uint32_t code_out;
|
||||||
|
uint8_t last_value;
|
||||||
|
uint8_t *stack_pos = ctx->stack_base;
|
||||||
|
uint32_t clear_code = ctx->clear_code;
|
||||||
|
uint32_t current_entry = ctx->current_entry;
|
||||||
|
struct lzw_dictionary_entry * const table = ctx->table;
|
||||||
|
|
||||||
|
/* Get a new code from the input */
|
||||||
|
res = lzw__next_code(&ctx->input, ctx->current_code_size, &code_new);
|
||||||
|
if (res != LZW_OK) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle the new code */
|
||||||
|
if (code_new == clear_code) {
|
||||||
|
/* Got Clear code */
|
||||||
|
return lzw__clear_codes(ctx, stack_pos_out);
|
||||||
|
|
||||||
|
} else if (code_new == ctx->eoi_code) {
|
||||||
|
/* Got End of Information code */
|
||||||
|
return LZW_EOI_CODE;
|
||||||
|
|
||||||
|
} else if (code_new > current_entry) {
|
||||||
|
/* Code is invalid */
|
||||||
|
return LZW_BAD_CODE;
|
||||||
|
|
||||||
|
} else if (code_new < current_entry) {
|
||||||
|
/* Code is in table */
|
||||||
|
code_out = code_new;
|
||||||
|
last_value = table[code_new].first_value;
|
||||||
|
} else {
|
||||||
|
/* Code not in table */
|
||||||
|
*stack_pos++ = ctx->previous_code_first;
|
||||||
|
code_out = ctx->previous_code;
|
||||||
|
last_value = ctx->previous_code_first;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add to the dictionary, only if there's space */
|
||||||
|
if (current_entry < (1 << LZW_CODE_MAX)) {
|
||||||
|
struct lzw_dictionary_entry *entry = table + current_entry;
|
||||||
|
entry->last_value = last_value;
|
||||||
|
entry->first_value = ctx->previous_code_first;
|
||||||
|
entry->previous_entry = ctx->previous_code;
|
||||||
|
ctx->current_entry++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure code size is increased, if needed. */
|
||||||
|
if (current_entry == ctx->current_code_size_max) {
|
||||||
|
if (ctx->current_code_size < LZW_CODE_MAX) {
|
||||||
|
ctx->current_code_size++;
|
||||||
|
ctx->current_code_size_max =
|
||||||
|
(1 << ctx->current_code_size) - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store details of this code as "previous code" to the context. */
|
||||||
|
ctx->previous_code_first = table[code_new].first_value;
|
||||||
|
ctx->previous_code = code_new;
|
||||||
|
|
||||||
|
/* Put rest of data for this code on output stack.
|
||||||
|
* Note, in the case of "code not in table", the last entry of the
|
||||||
|
* current code has already been placed on the stack above. */
|
||||||
|
while (code_out > clear_code) {
|
||||||
|
struct lzw_dictionary_entry *entry = table + code_out;
|
||||||
|
*stack_pos++ = entry->last_value;
|
||||||
|
code_out = entry->previous_entry;
|
||||||
|
}
|
||||||
|
*stack_pos++ = table[code_out].last_value;
|
||||||
|
|
||||||
|
*stack_pos_out = stack_pos;
|
||||||
|
return LZW_OK;
|
||||||
|
}
|
105
src/display/libnsgif/lzw.h
Normal file
105
src/display/libnsgif/lzw.h
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
|
||||||
|
* Licensed under the MIT License,
|
||||||
|
* http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*
|
||||||
|
* Copyright 2017 Michael Drake <michael.drake@codethink.co.uk>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LZW_H_
|
||||||
|
#define LZW_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief LZW decompression (interface)
|
||||||
|
*
|
||||||
|
* Decoder for GIF LZW data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** Maximum LZW code size in bits */
|
||||||
|
#define LZW_CODE_MAX 12
|
||||||
|
|
||||||
|
|
||||||
|
/* Declare lzw internal context structure */
|
||||||
|
struct lzw_ctx;
|
||||||
|
|
||||||
|
|
||||||
|
/** LZW decoding response codes */
|
||||||
|
typedef enum lzw_result {
|
||||||
|
LZW_OK, /**< Success */
|
||||||
|
LZW_OK_EOD, /**< Success; reached zero-length sub-block */
|
||||||
|
LZW_NO_MEM, /**< Error: Out of memory */
|
||||||
|
LZW_NO_DATA, /**< Error: Out of data */
|
||||||
|
LZW_EOI_CODE, /**< Error: End of Information code */
|
||||||
|
LZW_BAD_ICODE, /**< Error: Bad initial LZW code */
|
||||||
|
LZW_BAD_CODE, /**< Error: Bad LZW code */
|
||||||
|
} lzw_result;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an LZW decompression context.
|
||||||
|
*
|
||||||
|
* \param[out] ctx Returns an LZW decompression context. Caller owned,
|
||||||
|
* free with lzw_context_destroy().
|
||||||
|
* \return LZW_OK on success, or appropriate error code otherwise.
|
||||||
|
*/
|
||||||
|
lzw_result lzw_context_create(
|
||||||
|
struct lzw_ctx **ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy an LZW decompression context.
|
||||||
|
*
|
||||||
|
* \param[in] ctx The LZW decompression context to destroy.
|
||||||
|
*/
|
||||||
|
void lzw_context_destroy(
|
||||||
|
struct lzw_ctx *ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise an LZW decompression context for decoding.
|
||||||
|
*
|
||||||
|
* Caller owns neither `stack_base_out` or `stack_pos_out`.
|
||||||
|
*
|
||||||
|
* \param[in] ctx The LZW decompression context to initialise.
|
||||||
|
* \param[in] compressed_data The compressed data.
|
||||||
|
* \param[in] compressed_data_len Byte length of compressed data.
|
||||||
|
* \param[in] compressed_data_pos Start position in data. Must be position
|
||||||
|
* of a size byte at sub-block start.
|
||||||
|
* \param[in] code_size The initial LZW code size to use.
|
||||||
|
* \param[out] stack_base_out Returns base of decompressed data stack.
|
||||||
|
* \param[out] stack_pos_out Returns current stack position.
|
||||||
|
* There are `stack_pos_out - stack_base_out`
|
||||||
|
* current stack entries.
|
||||||
|
* \return LZW_OK on success, or appropriate error code otherwise.
|
||||||
|
*/
|
||||||
|
lzw_result lzw_decode_init(
|
||||||
|
struct lzw_ctx *ctx,
|
||||||
|
const uint8_t *compressed_data,
|
||||||
|
uint32_t compressed_data_len,
|
||||||
|
uint32_t compressed_data_pos,
|
||||||
|
uint8_t code_size,
|
||||||
|
const uint8_t ** const stack_base_out,
|
||||||
|
const uint8_t ** const stack_pos_out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill the LZW stack with decompressed data
|
||||||
|
*
|
||||||
|
* Ensure anything on the stack is used before calling this, as anything
|
||||||
|
* on the stack before this call will be trampled.
|
||||||
|
*
|
||||||
|
* Caller does not own `stack_pos_out`.
|
||||||
|
*
|
||||||
|
* \param[in] ctx LZW reading context, updated.
|
||||||
|
* \param[out] stack_pos_out Returns current stack position.
|
||||||
|
* Use with `stack_base_out` value from previous
|
||||||
|
* lzw_decode_init() call.
|
||||||
|
* There are `stack_pos_out - stack_base_out`
|
||||||
|
* current stack entries.
|
||||||
|
* \return LZW_OK on success, or appropriate error code otherwise.
|
||||||
|
*/
|
||||||
|
lzw_result lzw_decode(
|
||||||
|
struct lzw_ctx *ctx,
|
||||||
|
const uint8_t ** const stack_pos_out);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
21
src/display/libnsgif/utils/log.h
Normal file
21
src/display/libnsgif/utils/log.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2003 James Bursa <bursa@users.sourceforge.net>
|
||||||
|
* Copyright 2004 John Tytgat <John.Tytgat@aaug.net>
|
||||||
|
*
|
||||||
|
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
||||||
|
* Licenced under the MIT License,
|
||||||
|
* http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifndef _LIBNSGIF_LOG_H_
|
||||||
|
#define _LIBNSGIF_LOG_H_
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
# define LOG(x) ((void) 0)
|
||||||
|
#else
|
||||||
|
# define LOG(x) do { fprintf(stderr, x), fputc('\n', stderr); } while (0)
|
||||||
|
#endif /* NDEBUG */
|
||||||
|
|
||||||
|
#endif /* _LIBNSGIF_LOG_H_ */
|
|
@ -522,6 +522,7 @@ struct TilemapPrivate
|
||||||
void buildAtlas()
|
void buildAtlas()
|
||||||
{
|
{
|
||||||
updateAutotileInfo();
|
updateAutotileInfo();
|
||||||
|
tileset->ensureNonAnimated();
|
||||||
|
|
||||||
TileAtlas::BlitVec blits = TileAtlas::calcBlits(atlas.efTilesetH, atlas.size);
|
TileAtlas::BlitVec blits = TileAtlas::calcBlits(atlas.efTilesetH, atlas.size);
|
||||||
|
|
||||||
|
@ -542,6 +543,7 @@ struct TilemapPrivate
|
||||||
{
|
{
|
||||||
const uint8_t atInd = atlas.usableATs[i];
|
const uint8_t atInd = atlas.usableATs[i];
|
||||||
Bitmap *autotile = autotiles[atInd];
|
Bitmap *autotile = autotiles[atInd];
|
||||||
|
autotile->ensureNonAnimated();
|
||||||
|
|
||||||
int atW = autotile->width();
|
int atW = autotile->width();
|
||||||
int atH = autotile->height();
|
int atH = autotile->height();
|
||||||
|
|
|
@ -68,7 +68,7 @@ global_args += '-DMKXPZ_ALCDEVICE=' + alcdev_struct
|
||||||
global_include_dirs += include_directories('.',
|
global_include_dirs += include_directories('.',
|
||||||
'audio',
|
'audio',
|
||||||
'crypto',
|
'crypto',
|
||||||
'display', 'display/gl',
|
'display', 'display/gl', 'display/libnsgif', 'display/libnsgif/utils',
|
||||||
'etc',
|
'etc',
|
||||||
'filesystem', 'filesystem/ghc',
|
'filesystem', 'filesystem/ghc',
|
||||||
'input',
|
'input',
|
||||||
|
@ -130,6 +130,9 @@ main_source = files(
|
||||||
'display/window.cpp',
|
'display/window.cpp',
|
||||||
'display/windowvx.cpp',
|
'display/windowvx.cpp',
|
||||||
|
|
||||||
|
'display/libnsgif/libnsgif.c',
|
||||||
|
'display/libnsgif/lzw.c',
|
||||||
|
|
||||||
'display/gl/gl-debug.cpp',
|
'display/gl/gl-debug.cpp',
|
||||||
'display/gl/gl-fun.cpp',
|
'display/gl/gl-fun.cpp',
|
||||||
'display/gl/gl-meta.cpp',
|
'display/gl/gl-meta.cpp',
|
||||||
|
|
Loading…
Add table
Reference in a new issue