aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc3
-rwxr-xr-xemscripten.py14
-rw-r--r--src/intertyper.js2
-rw-r--r--src/jsifier.js23
-rw-r--r--src/library.js98
-rw-r--r--src/library_sdl.js9
-rw-r--r--src/modules.js19
-rw-r--r--src/relooper/Relooper.cpp2
-rw-r--r--src/settings.js4
-rw-r--r--tests/cases/sillyfuncast.ll23
-rw-r--r--tests/cases/sillyfuncast2.ll21
-rwxr-xr-xtests/runner.py48
-rw-r--r--tests/sdl_rotozoom.c3
-rw-r--r--tests/sdl_rotozoom.pngbin437956 -> 431921 bytes
14 files changed, 218 insertions, 51 deletions
diff --git a/emcc b/emcc
index bffe8d5d..895aab87 100755
--- a/emcc
+++ b/emcc
@@ -1077,6 +1077,9 @@ try:
shared.Settings.LINKABLE = 1 # TODO: add FORCE_DCE option for the brave people that do want to dce here and in side modules
debug_level = max(debug_level, 2)
+ if shared.Settings.DLOPEN_SUPPORT:
+ shared.Settings.LINKABLE = 1
+
## Compile source code to bitcode
logging.debug('compiling to bitcode')
diff --git a/emscripten.py b/emscripten.py
index 9a2abb9a..3e3538e9 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -9,7 +9,7 @@ header files (so that the JS compiler can see the constants in those
headers, for the libc implementation in JS).
'''
-import os, sys, json, optparse, subprocess, re, time, multiprocessing, functools
+import os, sys, json, optparse, subprocess, re, time, multiprocessing, string
from tools import shared
from tools import jsrun, cache as cache_module, tempfiles
@@ -625,6 +625,18 @@ Runtime.stackRestore = function(top) { asm['stackRestore'](top) };
// EMSCRIPTEN_END_FUNCS
''']
+ # Create symbol table for self-dlopen
+ if settings.get('DLOPEN_SUPPORT'):
+ symbol_table = { k:v+forwarded_json['Runtime']['GLOBAL_BASE']
+ for k,v in forwarded_json['Variables']['indexedGlobals'].iteritems()
+ if forwarded_json['Variables']['globals'][k]['named'] }
+ for raw in last_forwarded_json['Functions']['tables'].itervalues():
+ if raw == '': continue
+ table = map(string.strip, raw[raw.find('[')+1:raw.find(']')].split(","))
+ symbol_table.update(map(lambda x: (x[1], x[0]),
+ filter(lambda x: x[1] != '0', enumerate(table))))
+ outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table))
+
for funcs_js_item in funcs_js: # do this loop carefully to save memory
funcs_js_item = indexize(funcs_js_item)
funcs_js_item = blockaddrsize(funcs_js_item)
diff --git a/src/intertyper.js b/src/intertyper.js
index 94d937e1..abfbdacb 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -503,6 +503,7 @@ function intertyper(data, sidePass, baseLineNums) {
// variable
var ident = item.tokens[0].text;
var private_ = findTokenText(item, 'private') >= 0 || findTokenText(item, 'internal') >= 0;
+ var named = findTokenText(item, 'unnamed_addr') < 0;
cleanOutTokens(LLVM.GLOBAL_MODIFIERS, item.tokens, [2, 3]);
var external = false;
if (item.tokens[2].text === 'external') {
@@ -516,6 +517,7 @@ function intertyper(data, sidePass, baseLineNums) {
type: item.tokens[2].text,
external: external,
private_: private_,
+ named: named,
lineNum: item.lineNum
};
if (!NAMED_GLOBALS) {
diff --git a/src/jsifier.js b/src/jsifier.js
index 82b78d0a..30cea99b 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -286,6 +286,8 @@ function JSify(data, functionsOnly, givenFunctions) {
allocator = 'ALLOC_NONE';
}
+ Variables.globals[item.ident].named = item.named;
+
if (ASM_JS && (MAIN_MODULE || SIDE_MODULE) && !item.private_ && !NAMED_GLOBALS && isIndexableGlobal(item.ident)) {
// We need this to be named (and it normally would not be), so that it can be linked to and used from other modules
Variables.globals[item.ident].linkable = 1;
@@ -602,6 +604,8 @@ function JSify(data, functionsOnly, givenFunctions) {
var associatedSourceFile = "NO_SOURCE";
}
+ if (DLOPEN_SUPPORT) Functions.getIndex(func.ident);
+
func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n';
if (PGO) {
@@ -1470,7 +1474,6 @@ function JSify(data, functionsOnly, givenFunctions) {
}
args = args.concat(varargs);
- var argsText = args.join(', ');
// Inline if either we inline whenever we can (and we can), or if there is no noninlined version
var inline = LibraryManager.library[simpleIdent + '__inline'];
@@ -1492,9 +1495,27 @@ function JSify(data, functionsOnly, givenFunctions) {
}
}
+ if (callIdent in Functions.implementedFunctions) {
+ // LLVM sometimes bitcasts for no reason. We must call using the exact same type as the actual function is generated as.
+ var numArgs = Functions.implementedFunctions[callIdent].length - 1;
+ if (numArgs !== args.length) {
+ if (VERBOSE) warnOnce('Fixing function call arguments based on signature, on ' + [callIdent, args.length, numArgs]);
+ while (args.length > numArgs) { args.pop(); argsTypes.pop() }
+ while (args.length < numArgs) { args.push('0'); argsTypes.push('i32') }
+ }
+ }
+
var returnType = 'void';
if ((byPointer || ASM_JS) && hasReturn) {
returnType = getReturnType(type);
+ if (callIdent in Functions.implementedFunctions) {
+ // LLVM sometimes bitcasts for no reason. We must call using the exact same type as the actual function is generated as
+ var trueType = Functions.getSignatureReturnType(Functions.implementedFunctions[callIdent]);
+ if (trueType !== returnType && !isIdenticallyImplemented(trueType, returnType)) {
+ if (VERBOSE) warnOnce('Fixing function call based on return type from signature, on ' + [callIdent, returnType, trueType]);
+ returnType = trueType;
+ }
+ }
}
if (byPointer) {
diff --git a/src/library.js b/src/library.js
index b6e8b908..5aa370a7 100644
--- a/src/library.js
+++ b/src/library.js
@@ -5735,10 +5735,14 @@ LibraryManager.library = {
rintf: 'rint',
lrint: 'rint',
lrintf: 'rint',
+#if USE_TYPED_ARRAYS == 2
llrint: function(x) {
x = (x < 0) ? -Math.round(-x) : Math.round(x);
{{{ makeStructuralReturn(splitI64('x')) }}};
},
+#else
+ llrint: 'rint',
+#endif
llrintf: 'llrint',
nearbyint: 'rint',
nearbyintf: 'rint',
@@ -5881,7 +5885,7 @@ LibraryManager.library = {
dlopen: function(filename, flag) {
// void *dlopen(const char *file, int mode);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html
- filename = (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename);
+ filename = filename === 0 ? '__self__' : (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename);
if (DLFCN_DATA.loadedLibNames[filename]) {
// Already loaded; increment ref count and return.
@@ -5890,48 +5894,55 @@ LibraryManager.library = {
return handle;
}
- var target = FS.findObject(filename);
- if (!target || target.isFolder || target.isDevice) {
- DLFCN_DATA.errorMsg = 'Could not find dynamic lib: ' + filename;
- return 0;
+ if (filename === '__self__') {
+ var handle = -1;
+ var lib_module = Module;
+ var cached_functions = SYMBOL_TABLE;
} else {
- FS.forceLoadFile(target);
- var lib_data = intArrayToString(target.contents);
- }
+ var target = FS.findObject(filename);
+ if (!target || target.isFolder || target.isDevice) {
+ DLFCN_DATA.errorMsg = 'Could not find dynamic lib: ' + filename;
+ return 0;
+ } else {
+ FS.forceLoadFile(target);
+ var lib_data = intArrayToString(target.contents);
+ }
- try {
- var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length);
- } catch (e) {
+ try {
+ var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length);
+ } catch (e) {
#if ASSERTIONS
- Module.printErr('Error in loading dynamic library: ' + e);
+ Module.printErr('Error in loading dynamic library: ' + e);
#endif
- DLFCN_DATA.errorMsg = 'Could not evaluate dynamic lib: ' + filename;
- return 0;
- }
+ DLFCN_DATA.errorMsg = 'Could not evaluate dynamic lib: ' + filename;
+ return 0;
+ }
- // Not all browsers support Object.keys().
- var handle = 1;
- for (var key in DLFCN_DATA.loadedLibs) {
- if (DLFCN_DATA.loadedLibs.hasOwnProperty(key)) handle++;
- }
+ // Not all browsers support Object.keys().
+ var handle = 1;
+ for (var key in DLFCN_DATA.loadedLibs) {
+ if (DLFCN_DATA.loadedLibs.hasOwnProperty(key)) handle++;
+ }
+
+ // We don't care about RTLD_NOW and RTLD_LAZY.
+ if (flag & 256) { // RTLD_GLOBAL
+ for (var ident in lib_module) {
+ if (lib_module.hasOwnProperty(ident)) {
+ Module[ident] = lib_module[ident];
+ }
+ }
+ }
+ var cached_functions = {};
+ }
DLFCN_DATA.loadedLibs[handle] = {
refcount: 1,
name: filename,
module: lib_module,
- cached_functions: {}
+ cached_functions: cached_functions
};
DLFCN_DATA.loadedLibNames[filename] = handle;
- // We don't care about RTLD_NOW and RTLD_LAZY.
- if (flag & 256) { // RTLD_GLOBAL
- for (var ident in lib_module) {
- if (lib_module.hasOwnProperty(ident)) {
- Module[ident] = lib_module[ident];
- }
- }
- }
-
return handle;
},
// int dlclose(void* handle);
@@ -5963,13 +5974,15 @@ LibraryManager.library = {
return 0;
} else {
var lib = DLFCN_DATA.loadedLibs[handle];
- if (!lib.module.hasOwnProperty(symbol)) {
- DLFCN_DATA.errorMsg = ('Tried to lookup unknown symbol "' + symbol +
- '" in dynamic lib: ' + lib.name);
- return 0;
+ // self-dlopen means that lib.module is not a superset of
+ // cached_functions, so check the latter first
+ if (lib.cached_functions.hasOwnProperty(symbol)) {
+ return lib.cached_functions[symbol];
} else {
- if (lib.cached_functions.hasOwnProperty(symbol)) {
- return lib.cached_functions[symbol];
+ if (!lib.module.hasOwnProperty(symbol)) {
+ DLFCN_DATA.errorMsg = ('Tried to lookup unknown symbol "' + symbol +
+ '" in dynamic lib: ' + lib.name);
+ return 0;
} else {
var result = lib.module[symbol];
if (typeof result == 'function') {
@@ -6332,14 +6345,13 @@ LibraryManager.library = {
var fixup = function(value, min, max) {
return (typeof value !== 'number' || isNaN(value)) ? min : (value>=min ? (value<=max ? value: max): min);
};
-
return {
- year: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_year', 'i32') }}} + 1900 , 1970, 9999),
- month: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_mon', 'i32') }}}, 0, 11),
- day: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_mday', 'i32') }}}, 1, 31),
- hour: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_hour', 'i32') }}}, 0, 23),
- min: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_min', 'i32') }}}, 0, 59),
- sec: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_sec', 'i32') }}}, 0, 59)
+ year: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_year', 'i32', 0, 0, 1) }}} + 1900 , 1970, 9999),
+ month: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_mon', 'i32', 0, 0, 1) }}}, 0, 11),
+ day: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_mday', 'i32', 0, 0, 1) }}}, 1, 31),
+ hour: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_hour', 'i32', 0, 0, 1) }}}, 0, 23),
+ min: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_min', 'i32', 0, 0, 1) }}}, 0, 59),
+ sec: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_sec', 'i32', 0, 0, 1) }}}, 0, 59)
};
};
diff --git a/src/library_sdl.js b/src/library_sdl.js
index b7d73862..d14e433b 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -1096,7 +1096,11 @@ var LibrarySDL = {
return ret;
},
+ rotozoomSurface__deps: ['zoomSurface'],
rotozoomSurface: function(src, angle, zoom, smooth) {
+ if (angle % 360 === 0) {
+ return _zoomSurface(src, zoom, zoom, smooth);
+ }
var srcData = SDL.surfaces[src];
var w = srcData.width * zoom;
var h = srcData.height * zoom;
@@ -1760,6 +1764,11 @@ var LibrarySDL = {
return Math.floor(fontData.size*0.02); // XXX
},
+ TTF_FontHeight: function(font) {
+ var fontData = SDL.fonts[font];
+ return fontData.size;
+ },
+
// SDL gfx
$SDL_gfx: {
diff --git a/src/modules.js b/src/modules.js
index a6aa2644..9f419234 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -252,12 +252,26 @@ var Functions = {
for (var i = 0; i < argTypes.length; i++) {
var type = argTypes[i];
if (!type) break; // varargs
- sig += isIntImplemented(type) ? (getBits(type) == 64 ? 'ii' : 'i') : 'f'; // legalized i64s will be i32s
+ if (type in Runtime.FLOAT_TYPES) {
+ sig += 'f';
+ } else {
+ var chunks = getNumIntChunks(type);
+ for (var j = 0; j < chunks; j++) sig += 'i';
+ }
}
if (hasVarArgs) sig += 'i';
return sig;
},
+ getSignatureReturnType: function(sig) {
+ switch(sig[0]) {
+ case 'v': return 'void';
+ case 'i': return 'i32';
+ case 'f': return 'double';
+ default: throw 'what is this sig? ' + sig;
+ }
+ },
+
// Mark a function as needing indexing. Python will coordinate them all
getIndex: function(ident, doNotCreate, sig) {
if (doNotCreate && !(ident in this.indexedFunctions)) {
@@ -449,7 +463,8 @@ var PassManager = {
Types: Types,
Variables: Variables,
Functions: Functions,
- EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS // needed for asm.js global constructors (ctors)
+ EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS, // needed for asm.js global constructors (ctors)
+ Runtime: { GLOBAL_BASE: Runtime.GLOBAL_BASE }
}));
} else if (phase == 'funcs') {
print('\n//FORWARDED_DATA:' + JSON.stringify({
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index ca9c6ab1..a457914b 100644
--- a/src/relooper/Relooper.cpp
+++ b/src/relooper/Relooper.cpp
@@ -180,7 +180,7 @@ void Block::Render(bool InLoop) {
Block *DefaultTarget(NULL); // The block we branch to without checking the condition, if none of the other conditions held.
- // We must do this here, because blocks can be split and even comparing their Ids is not enough. We must check the conditions.
+ // Find the default target, the one without a condition
for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin(); iter != ProcessedBranchesOut.end(); iter++) {
if (!iter->second->Condition) {
assert(!DefaultTarget); // Must be exactly one default
diff --git a/src/settings.js b/src/settings.js
index 7f9dca3b..10e93975 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -327,6 +327,10 @@ var LINKABLE = 0; // If set to 1, this file can be linked with others, either as
// LINKABLE of 0 is very useful in that we can reduce the size of the
// generated code very significantly, by removing everything not actually used.
+var DLOPEN_SUPPORT = 0; // Whether to support dlopen(NULL, ...) which enables dynamic access to the
+ // module's functions and globals. Implies LINKABLE=1, because we do not want
+ // dead code elimination.
+
var RUNTIME_TYPE_INFO = 0; // Whether to expose type info to the script at run time. This
// increases the size of the generated script, but allows you
// to more easily perform operations from handwritten JS on
diff --git a/tests/cases/sillyfuncast.ll b/tests/cases/sillyfuncast.ll
new file mode 100644
index 00000000..36c26720
--- /dev/null
+++ b/tests/cases/sillyfuncast.ll
@@ -0,0 +1,23 @@
+; ModuleID = 'tests/hello_world.bc'
+target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"
+target triple = "i386-pc-linux-gnu"
+
+@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*]
+
+define void @doit() {
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32]
+ ret void
+}
+
+define i32 @main() {
+entry:
+ %retval = alloca i32, align 4 ; [#uses=1 type=i32*]
+ store i32 0, i32* %retval
+ %58 = tail call i32 bitcast (void ()* @doit to i32 ()*)() nounwind
+ ret i32 1
+}
+
+declare i32 @printf(i8*, ...)
+
+
+
diff --git a/tests/cases/sillyfuncast2.ll b/tests/cases/sillyfuncast2.ll
new file mode 100644
index 00000000..f72ebe28
--- /dev/null
+++ b/tests/cases/sillyfuncast2.ll
@@ -0,0 +1,21 @@
+; ModuleID = 'tests/hello_world.bc'
+target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"
+target triple = "i386-pc-linux-gnu"
+
+@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*]
+
+define void @doit(i32 %one, i32 %two) {
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32]
+ ret void
+}
+
+define i32 @main() {
+entry:
+ %retval = alloca i32, align 4 ; [#uses=1 type=i32*]
+ store i32 0, i32* %retval
+ call i32 bitcast (void (i32, i32)* @doit to i32 (i32, i64)*)(i32 0, i64 0) nounwind
+ ret i32 1
+}
+
+declare i32 @printf(i8*, ...)
+
diff --git a/tests/runner.py b/tests/runner.py
index b40c52ea..339ce68c 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -6021,6 +6021,50 @@ def process(filename):
self.do_run(src, '100\n200\n13\n42\n',
post_build=add_pre_run_and_checks)
+ def test_dlfcn_self(self):
+ if Settings.USE_TYPED_ARRAYS == 1: return self.skip('Does not work with USE_TYPED_ARRAYS=1')
+ Settings.DLOPEN_SUPPORT = 1
+
+ src = r'''
+#include <stdio.h>
+#include <dlfcn.h>
+
+int global = 123;
+
+extern "C" __attribute__((noinline)) void foo(int x) {
+ printf("%d\n", x);
+}
+
+extern "C" __attribute__((noinline)) void repeatable() {
+ void* self = dlopen(NULL, RTLD_LAZY);
+ int* global_ptr = (int*)dlsym(self, "global");
+ void (*foo_ptr)(int) = (void (*)(int))dlsym(self, "foo");
+ foo_ptr(*global_ptr);
+ dlclose(self);
+}
+
+int main() {
+ repeatable();
+ repeatable();
+ return 0;
+}'''
+ def post(filename):
+ with open(filename) as f:
+ for line in f:
+ if 'var SYMBOL_TABLE' in line:
+ table = line
+ break
+ else:
+ raise Exception('Could not find symbol table!')
+ import json
+ table = json.loads(table[table.find('{'):table.rfind('}')+1])
+ actual = list(sorted(table.keys()))
+ # ensure there aren't too many globals; we don't want unnamed_addr
+ assert actual == ['_foo', '_global', '_main', '_repeatable'], \
+ "Symbol table does not match: %s" % actual
+
+ self.do_run(src, '123\n123', post_build=(None, post))
+
def test_rand(self):
return self.skip('rand() is now random') # FIXME
@@ -9790,7 +9834,7 @@ def process(filename):
Settings.CORRECT_ROUNDINGS = 0
self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-6**5*') # JS floor operations, always to the negative. This is an undetected error here!
self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # We get these right, since they are 32-bit and we can shortcut using the |0 trick
- self.do_run(src.replace('TYPE', 'unsigned int'), '*-3**2**-6**5*') # We fail, since no fast shortcut for 32-bit unsigneds
+ self.do_run(src.replace('TYPE', 'unsigned int'), '*-2**2**-6**5*')
Settings.CORRECT_ROUNDINGS = 1
Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well
@@ -9804,7 +9848,7 @@ def process(filename):
Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:13"] # Fix just the last mistake
self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-5**5*')
self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Here we are lucky and also get the first one right
- self.do_run(src.replace('TYPE', 'unsigned int'), '*-3**2**-5**5*') # No such luck here
+ self.do_run(src.replace('TYPE', 'unsigned int'), '*-2**2**-5**5*')
# And reverse the check with = 2
if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1
diff --git a/tests/sdl_rotozoom.c b/tests/sdl_rotozoom.c
index e81258d9..cdbdcc6f 100644
--- a/tests/sdl_rotozoom.c
+++ b/tests/sdl_rotozoom.c
@@ -6,7 +6,7 @@
#include "emscripten.h"
#endif
-const int numSprites = 8;
+const int numSprites = 9;
SDL_Surface *screen;
SDL_Surface *sprite[numSprites];
@@ -41,6 +41,7 @@ int main(int argc, char **argv) {
sprite[5] = rotozoomSurface(sprite[1], 45, 0.5, SMOOTHING_ON);
sprite[6] = zoomSurface(sprite[0], -0.5, 0.5, SMOOTHING_ON);
sprite[7] = zoomSurface(sprite[0], -0.5, -0.5, SMOOTHING_ON);
+ sprite[8] = rotozoomSurface(sprite[1], 0, 0.5, SMOOTHING_ON);
mainloop();
diff --git a/tests/sdl_rotozoom.png b/tests/sdl_rotozoom.png
index 5c5dec2f..288dd303 100644
--- a/tests/sdl_rotozoom.png
+++ b/tests/sdl_rotozoom.png
Binary files differ