aboutsummaryrefslogtreecommitdiff
path: root/src/library_sdl.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/library_sdl.js')
-rw-r--r--src/library_sdl.js337
1 files changed, 278 insertions, 59 deletions
diff --git a/src/library_sdl.js b/src/library_sdl.js
index af71da26..4477e457 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -21,11 +21,14 @@ var LibrarySDL = {
version: null,
surfaces: {},
+ // A pool of freed canvas elements. Reusing them avoids GC pauses.
+ canvasPool: [],
events: [],
fonts: [null],
// The currently preloaded audio elements ready to be played
audios: [null],
+ rwops: [null],
// The currently playing audio element. There's only one music track.
music: {
audio: null,
@@ -246,7 +249,7 @@ var LibrarySDL = {
},
translateRGBAToCSSRGBA: function(r, g, b, a) {
- return 'rgba(' + r + ',' + g + ',' + b + ',' + (a/255) + ')';
+ return 'rgba(' + (r&0xff) + ',' + (g&0xff) + ',' + (b&0xff) + ',' + (a&0xff)/255 + ')';
},
translateRGBAToColor: function(r, g, b, a) {
@@ -273,6 +276,8 @@ var LibrarySDL = {
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*5', '0', 'buffer', 'void*') }}} // SDL_Surface.pixels
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*6', '0', '0', 'i32*') }}} // SDL_Surface.offset
+ {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*14', '0', '1', 'i32') }}}
+
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.format', '0', '-2042224636', 'i32') }}} // SDL_PIXELFORMAT_RGBA8888
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.palette', '0', '0', 'i32') }}} // TODO
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BitsPerPixel', '0', 'bpp * 8', 'i8') }}}
@@ -288,7 +293,11 @@ var LibrarySDL = {
SDL.GL = SDL.GL || useWebGL;
var canvas;
if (!usePageCanvas) {
- canvas = document.createElement('canvas');
+ if (SDL.canvasPool.length > 0) {
+ canvas = SDL.canvasPool.pop();
+ } else {
+ canvas = document.createElement('canvas');
+ }
canvas.width = width;
canvas.height = height;
} else {
@@ -355,8 +364,17 @@ var LibrarySDL = {
},
freeSurface: function(surf) {
- _free(SDL.surfaces[surf].buffer);
- _free(SDL.surfaces[surf].pixelFormat);
+ var refcountPointer = surf + Runtime.QUANTUM_SIZE * 14;
+ var refcount = {{{ makeGetValue('refcountPointer', '0', 'i32') }}};
+ if (refcount > 1) {
+ {{{ makeSetValue('refcountPointer', '0', 'refcount - 1', 'i32') }}};
+ return;
+ }
+
+ var info = SDL.surfaces[surf];
+ if (!info.usePageCanvas && info.canvas) SDL.canvasPool.push(info.canvas);
+ _free(info.buffer);
+ _free(info.pixelFormat);
_free(surf);
SDL.surfaces[surf] = null;
},
@@ -931,7 +949,10 @@ var LibrarySDL = {
// TODO
},
- SDL_GetKeyboardState: function() {
+ SDL_GetKeyboardState: function(numKeys) {
+ if (numKeys) {
+ {{{ makeSetValue('numKeys', 0, 0x10000, 'i32') }}};
+ }
return SDL.keyboardState;
},
@@ -1062,6 +1083,7 @@ var LibrarySDL = {
surfData.ctx.fillStyle = SDL.translateColorToCSSRGBA(color);
surfData.ctx.fillRect(r.x, r.y, r.w, r.h);
surfData.ctx.restore();
+ return 0;
},
SDL_BlitSurface__deps: ['SDL_UpperBlit'],
@@ -1071,15 +1093,27 @@ var LibrarySDL = {
zoomSurface: function(src, x, y, smooth) {
var srcData = SDL.surfaces[src];
- var w = srcData.width*x;
- var h = srcData.height*y;
- var ret = SDL.makeSurface(w, h, srcData.flags, false, 'zoomSurface');
+ var w = srcData.width * x;
+ var h = srcData.height * y;
+ var ret = SDL.makeSurface(Math.abs(w), Math.abs(h), srcData.flags, false, 'zoomSurface');
var dstData = SDL.surfaces[ret];
- dstData.ctx.drawImage(srcData.canvas, 0, 0, w, h);
+ if (x >= 0 && y >= 0) dstData.ctx.drawImage(srcData.canvas, 0, 0, w, h);
+ else {
+ dstData.ctx.save();
+ dstData.ctx.scale(x < 0 ? -1 : 1, y < 0 ? -1 : 1);
+ dstData.ctx.drawImage(srcData.canvas, w < 0 ? w : 0, h < 0 ? h : 0, Math.abs(w), Math.abs(h));
+ // XXX I think this should work according to the spec, but currently
+ // fails on FF: dstData.ctx.drawImage(srcData.canvas, 0, 0, w, h);
+ dstData.ctx.restore();
+ }
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;
@@ -1096,6 +1130,14 @@ var LibrarySDL = {
SDL.surfaces[surf].alpha = alpha;
},
+ SDL_SetColorKey: function(surf, flag, key) {
+ // SetColorKey assigns one color to be rendered as transparent. I don't
+ // think the canvas API allows for anything like this, and iterating through
+ // each pixel to replace that color seems prohibitively expensive.
+ Runtime.warnOnce('SDL_SetColorKey is a no-op for performance reasons');
+ return 0;
+ },
+
SDL_GetTicks: function() {
return Math.floor(Date.now() - SDL.startTime);
},
@@ -1196,9 +1238,24 @@ var LibrarySDL = {
return flags; // We support JPG, PNG, TIF because browsers do
},
- IMG_Load__deps: ['SDL_LockSurface'],
- IMG_Load: function(filename) {
- filename = FS.standardizePath(Pointer_stringify(filename));
+ IMG_Load_RW__deps: ['SDL_LockSurface'],
+ IMG_Load_RW: function(rwopsID, freesrc) {
+ var rwops = SDL.rwops[rwopsID];
+
+ if (rwops === undefined) {
+ return 0;
+ }
+
+ var filename = rwops.filename;
+
+ if (filename === undefined) {
+ Runtime.warnOnce('Only file names that have been preloaded are supported for IMG_Load_RW.');
+ // TODO. Support loading image data from embedded files, similarly to Mix_LoadWAV_RW
+ // TODO. Support loading image data from byte arrays, similarly to Mix_LoadWAV_RW
+ return 0;
+ }
+
+ filename = FS.standardizePath(filename);
if (filename[0] == '/') {
// Convert the path to relative
filename = filename.substr(1);
@@ -1230,8 +1287,14 @@ var LibrarySDL = {
return surf;
},
SDL_LoadBMP: 'IMG_Load',
- SDL_LoadBMP_RW: 'IMG_Load',
- IMG_Load_RW: 'IMG_Load',
+ SDL_LoadBMP_RW: 'IMG_Load_RW',
+ IMG_Load__deps: ['IMG_Load_RW', 'SDL_RWFromFile', 'SDL_FreeRW'],
+ IMG_Load: function(filename){
+ var rwops = _SDL_RWFromFile(filename);
+ var result = _IMG_Load_RW(rwops);
+ _SDL_FreeRW(rwops);
+ return result;
+ },
// SDL_Audio
@@ -1367,22 +1430,62 @@ var LibrarySDL = {
return 0; // error
},
- Mix_LoadWAV_RW: function(filename, freesrc) {
- filename = FS.standardizePath(Pointer_stringify(filename));
- var raw = Module["preloadedAudios"][filename];
- if (!raw) {
- if (raw === null) Module.printErr('Trying to reuse preloaded audio, but freePreloadedMediaOnUse is set!');
- Runtime.warnOnce('Cannot find preloaded audio ' + filename);
+ Mix_LoadWAV_RW: function(rwopsID, freesrc) {
+ var rwops = SDL.rwops[rwopsID];
+
+ if (rwops === undefined)
+ return 0;
+
+ var filename = '';
+ var audio;
+ var bytes;
+
+ if (rwops.filename !== undefined) {
+ filename = rwops.filename;
+ filename = FS.standardizePath(filename);
+ var raw = Module["preloadedAudios"][filename];
+ if (!raw) {
+ if (raw === null) Module.printErr('Trying to reuse preloaded audio, but freePreloadedMediaOnUse is set!');
+ Runtime.warnOnce('Cannot find preloaded audio ' + filename);
+
+ // see if we can read the file-contents from the in-memory FS
+ var fileObject = FS.findObject(filename);
+
+ if (fileObject === null) Module.printErr('Couldn\'t find file for: ' + filename);
+
+ // We found the file. Load the contents
+ if (fileObject && !fileObject.isFolder && fileObject.read) {
+ bytes = fileObject.contents
+ } else {
+ return 0;
+ }
+ }
+ if (Module['freePreloadedMediaOnUse']) {
+ Module["preloadedAudios"][filename] = null;
+ }
+ audio = raw;
+ }
+ else if (rwops.bytes !== undefined) {
+ bytes = HEAPU8.subarray(rwops.bytes, rwops.bytes + rwops.count);
+ }
+ else {
return 0;
}
- if (Module['freePreloadedMediaOnUse']) {
- Module["preloadedAudios"][filename] = null;
+
+ // Here, we didn't find a preloaded audio but we either were passed a filepath for
+ // which we loaded bytes, or we were passed some bytes
+ if (audio === undefined && bytes) {
+ var blob = new Blob([new Uint8Array(bytes)], {type: rwops.mimetype});
+ var url = URL.createObjectURL(blob);
+ audio = new Audio();
+ audio.src = url;
}
+
var id = SDL.audios.length;
// Keep the loaded audio in the audio arrays, ready for playback
SDL.audios.push({
source: filename,
- audio: raw
+ audio: audio
});
return id;
},
@@ -1512,15 +1615,20 @@ var LibrarySDL = {
},
Mix_HaltChannel: function(channel) {
- var info = SDL.channels[channel];
- if (info.audio) {
- info.audio.pause();
- info.audio = null;
- } else {
- Module.printErr('No Audio for channel: ' + channel);
+ function halt(channel) {
+ var info = SDL.channels[channel];
+ if (info.audio) {
+ info.audio.pause();
+ info.audio = null;
+ }
+ if (SDL.channelFinished) {
+ Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
+ }
}
- if (SDL.channelFinished) {
- Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
+ if (channel != -1) {
+ halt(channel);
+ } else {
+ for (var i = 0; i < SDL.channels.length; ++i) halt(i);
}
return 0;
},
@@ -1537,8 +1645,14 @@ var LibrarySDL = {
return SDL.setGetVolume(SDL.music, volume);
},
- Mix_LoadMUS: 'Mix_LoadWAV_RW',
Mix_LoadMUS_RW: 'Mix_LoadWAV_RW',
+ Mix_LoadMUS__deps: ['Mix_LoadMUS_RW', 'SDL_RWFromFile', 'SDL_FreeRW'],
+ Mix_LoadMUS: function(filename) {
+ var rwops = _SDL_RWFromFile(filename);
+ var result = _Mix_LoadMUS_RW(rwops);
+ _SDL_FreeRW(rwops);
+ return result;
+ },
Mix_FreeMusic: 'Mix_FreeChunk',
@@ -1707,6 +1821,7 @@ var LibrarySDL = {
},
TTF_RenderText_Blended: 'TTF_RenderText_Solid', // XXX ignore blending vs. solid
TTF_RenderText_Shaded: 'TTF_RenderText_Solid', // XXX ignore blending vs. solid
+ TTF_RenderUTF8_Solid: 'TTF_RenderText_Solid',
TTF_SizeText: function(font, text, w, h) {
var fontData = SDL.fonts[font];
@@ -1729,37 +1844,121 @@ 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: {
+ drawRectangle: function(surf, x1, y1, x2, y2, action, cssColor) {
+ x1 = x1 << 16 >> 16;
+ y1 = y1 << 16 >> 16;
+ x2 = x2 << 16 >> 16;
+ y2 = y2 << 16 >> 16;
+ var surfData = SDL.surfaces[surf];
+ assert(!surfData.locked); // but we could unlock and re-lock if we must..
+ // TODO: if ctx does not change, leave as is, and also do not re-set xStyle etc.
+ var x = x1 < x2 ? x1 : x2;
+ var y = y1 < y2 ? y1 : y2;
+ var w = Math.abs(x2 - x1);
+ var h = Math.abs(y2 - y1);
+ surfData.ctx.save();
+ surfData.ctx[action + 'Style'] = cssColor;
+ surfData.ctx[action + 'Rect'](x, y, w, h);
+ surfData.ctx.restore();
+ },
+ drawLine: function(surf, x1, y1, x2, y2, cssColor) {
+ x1 = x1 << 16 >> 16;
+ y1 = y1 << 16 >> 16;
+ x2 = x2 << 16 >> 16;
+ y2 = y2 << 16 >> 16;
+ var surfData = SDL.surfaces[surf];
+ assert(!surfData.locked); // but we could unlock and re-lock if we must..
+ surfData.ctx.save();
+ surfData.ctx.strokeStyle = cssColor;
+ surfData.ctx.beginPath();
+ surfData.ctx.moveTo(x1, y1);
+ surfData.ctx.lineTo(x2, y2);
+ surfData.ctx.stroke();
+ surfData.ctx.restore();
+ },
+ // See http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
+ drawEllipse: function(surf, x, y, rx, ry, action, cssColor) {
+ x = x << 16 >> 16;
+ y = y << 16 >> 16;
+ rx = rx << 16 >> 16;
+ ry = ry << 16 >> 16;
+ var surfData = SDL.surfaces[surf];
+ assert(!surfData.locked); // but we could unlock and re-lock if we must..
+
+ surfData.ctx.save();
+ surfData.ctx.beginPath();
+ surfData.ctx.translate(x, y);
+ surfData.ctx.scale(rx, ry);
+ surfData.ctx.arc(0, 0, 1, 0, 2 * Math.PI);
+ surfData.ctx.restore();
+
+ surfData.ctx.save();
+ surfData.ctx[action + 'Style'] = cssColor;
+ surfData.ctx[action]();
+ surfData.ctx.restore();
+ },
+ // the gfx library uses something different from the rest of SDL...
+ translateColorToCSSRGBA: function(rgba) {
+ return 'rgba(' + (rgba>>>24) + ',' + (rgba>>16 & 0xff) + ',' + (rgba>>8 & 0xff) + ',' + (rgba&0xff) + ')';
+ }
+ },
+
+ boxColor__deps: ['$SDL_gfx'],
+ boxColor: function(surf, x1, y1, x2, y2, color) {
+ return SDL_gfx.drawRectangle(surf, x1, y1, x2, y2, 'fill', SDL_gfx.translateColorToCSSRGBA(color));
+ },
+
+ boxRGBA__deps: ['$SDL_gfx'],
boxRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
- var surfData = SDL.surfaces[surf];
- assert(!surfData.locked); // but we could unlock and re-lock if we must..
- // TODO: if ctx does not change, leave as is, and also do not re-set xStyle etc.
- surfData.ctx.save();
- surfData.ctx.fillStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
- surfData.ctx.fillRect(x1, y1, x2-x1, y2-y1);
- surfData.ctx.restore();
+ return SDL_gfx.drawRectangle(surf, x1, y1, x2, y2, 'fill', SDL.translateRGBAToCSSRGBA(r, g, b, a));
+ },
+
+ rectangleColor__deps: ['$SDL_gfx'],
+ rectangleColor: function(surf, x1, y1, x2, y2, color) {
+ return SDL_gfx.drawRectangle(surf, x1, y1, x2, y2, 'stroke', SDL_gfx.translateColorToCSSRGBA(color));
},
+ rectangleRGBA__deps: ['$SDL_gfx'],
rectangleRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
- var surfData = SDL.surfaces[surf];
- assert(!surfData.locked); // but we could unlock and re-lock if we must..
- surfData.ctx.save();
- surfData.ctx.strokeStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
- surfData.ctx.strokeRect(x1, y1, x2-x1, y2-y1);
- surfData.ctx.restore();
+ return SDL_gfx.drawRectangle(surf, x1, y1, x2, y2, 'stroke', SDL.translateRGBAToCSSRGBA(r, g, b, a));
+ },
+
+ ellipseColor__deps: ['$SDL_gfx'],
+ ellipseColor: function(surf, x, y, rx, ry, color) {
+ return SDL_gfx.drawEllipse(surf, x, y, rx, ry, 'stroke', SDL_gfx.translateColorToCSSRGBA(color));
+ },
+
+ ellipseRGBA__deps: ['$SDL_gfx'],
+ ellipseRGBA: function(surf, x, y, rx, ry, r, g, b, a) {
+ return SDL_gfx.drawEllipse(surf, x, y, rx, ry, 'stroke', SDL.translateRGBAToCSSRGBA(r, g, b, a));
},
+ filledEllipseColor__deps: ['$SDL_gfx'],
+ filledEllipseColor: function(surf, x, y, rx, ry, color) {
+ return SDL_gfx.drawEllipse(surf, x, y, rx, ry, 'fill', SDL_gfx.translateColorToCSSRGBA(color));
+ },
+
+ filledEllipseRGBA__deps: ['$SDL_gfx'],
+ filledEllipseRGBA: function(surf, x, y, rx, ry, r, g, b, a) {
+ return SDL_gfx.drawEllipse(surf, x, y, rx, ry, 'fill', SDL.translateRGBAToCSSRGBA(r, g, b, a));
+ },
+
+ lineColor__deps: ['$SDL_gfx'],
+ lineColor: function(surf, x1, y1, x2, y2, color) {
+ return SDL_gfx.drawLine(surf, x1, y1, x2, y2, SDL_gfx.translateColorToCSSRGBA(color));
+ },
+
+ lineRGBA__deps: ['$SDL_gfx'],
lineRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
- var surfData = SDL.surfaces[surf];
- assert(!surfData.locked); // but we could unlock and re-lock if we must..
- surfData.ctx.save();
- surfData.ctx.strokeStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
- surfData.ctx.beginPath();
- surfData.ctx.moveTo(x1, y1);
- surfData.ctx.lineTo(x2, y2);
- surfData.ctx.stroke();
- surfData.ctx.restore();
+ return SDL_gfx.drawLine(surf, x1, y1, x2, y2, SDL.translateRGBAToCSSRGBA(r, g, b, a));
},
pixelRGBA__deps: ['boxRGBA'],
@@ -1845,14 +2044,35 @@ var LibrarySDL = {
return -1;
},
+ // Joysticks
+
+ SDL_NumJoysticks: function() { return 0 },
+
+ SDL_JoystickOpen: function(deviceIndex) { return 0 },
+
+ SDL_JoystickGetButton: function(joystick, button) { return 0 },
+
// Misc
SDL_InitSubSystem: function(flags) { return 0 },
+ SDL_RWFromConstMem: function(mem, size) {
+ var id = SDL.rwops.length; // TODO: recycle ids when they are null
+ SDL.rwops.push({ bytes: mem, count: size });
+ return id;
+ },
- SDL_NumJoysticks: function() { return 0 },
-
- SDL_RWFromFile: function(filename, mode) {
- return filename; // XXX We just forward the filename
+ SDL_RWFromFile: function(_name, mode) {
+ var id = SDL.rwops.length; // TODO: recycle ids when they are null
+ var name = Pointer_stringify(_name)
+ SDL.rwops.push({ filename: name, mimetype: Browser.getMimetype(name) });
+ return id;
+ },
+
+ SDL_FreeRW: function(rwopsID) {
+ SDL.rwops[rwopsID] = null;
+ while (SDL.rwops.length > 0 && SDL.rwops[SDL.rwops.length-1] === null) {
+ SDL.rwops.pop();
+ }
},
SDL_EnableUNICODE: function(on) {
@@ -1879,7 +2099,6 @@ var LibrarySDL = {
SDL_GetThreadID: function() { throw 'SDL_GetThreadID' },
SDL_ThreadID: function() { throw 'SDL_ThreadID' },
SDL_AllocRW: function() { throw 'SDL_AllocRW: TODO' },
- SDL_FreeRW: function() { throw 'SDL_FreeRW: TODO' },
SDL_CondBroadcast: function() { throw 'SDL_CondBroadcast: TODO' },
SDL_CondWaitTimeout: function() { throw 'SDL_CondWaitTimeout: TODO' },
SDL_WM_IconifyWindow: function() { throw 'SDL_WM_IconifyWindow TODO' },