Save Empscripten webGL canvas as image in JS - javascript

I have an Emscripten-driven webGL canvas that I need to save as an image from a Javascript handler. Let's say there's a simple JS "Save" button.
<script type="text/javascript">
var Exporter = {
preRun: [],
postRun: [],
save: function() {
var c=Module.canvas;
var d=c.toDataURL("image/png");
var w=window.open('about:blank','image from canvas');
w.document.write("<img src='"+d+"' alt='from canvas'/>");
}
};
</script>
<input type="button" value="Save" onclick="Exporter.save()" />
By default, the webGL context has preserveDrawingBuffer set to false, so the resulting image is blank.
For the image to show the rendered webGL scene, I need to add preserveDrawingBuffer: true to the attributes passed in the getContext call inside my compiled Empscripten code. I can do this by hand editing the compiled empscripten js code; the resulting image is then correct, but I'd like to avoid this hack - I'd have to do it after each recompile.
Is there and easier and cleaner way to add preserveDrawingBuffer to the webGLContextAttributes from outside? i.e. as a compile option for emcc, some SDL parameter inside the C code or from Javascript in the hosting page?
UPDATE
See below for the solution; unrelated issue I encountered was that the saved image had lower bit depth and anti-aliased lines looked pretty bad. Using c.toDataURL( "image/jpeg" ) solved that.

Well, first off, all of emscripten and all of it's libraries are open source so you can just go change them.
In particular copy library_gl.js to your project folder and then remove -lGL and add --js-library library_gl.js to your build script, you can then hack your local library_gl.js to do whatever you want.
Otherwise I don't know SDL at all but you can just get the context yourself before your call the emscripten code. A canvas can only have one context, if you call getContext again for the same type of context you'll get the same context. In other words if your JavaScript creates the context first the emscripten code will get the same context
so this should work
theCanvasElement.getContext("webgl", {preserveDrawingBuffer: true});
... now execute emscripten and have it use `theCanvasElement`
If you can't even do that you can override getContext
HTMLCanvasElement.prototype.getContext = (function(oldGetContextFn) {
return function(type, attrs) {
attrs = attrs || {};
if (type === "webgl") {
attrs.preserveDrawingBuffer = true;
}
return oldGetContextFn.call(this, type, attrs);
};
}(HTMLCanvasElement.prototype.getContext));

Related

How can I retrieve a variable from javascript to python with dash?

I'm trying to display a set of columns of images in python using dash. In order to calculate how many columns I can fit in a window I would like to know the viewer width in pixels. I couldn't find a way to do this in dash so I'm trying to run javascript in the assets directory to return window.innerWidth. I think this can be done with dash_extensions but I'm not sure how to use the object returned from dash_leaflet. I'm new to javascript so tried to stick to the documentation's example as closely as possible. In my assets folder, I have the following javascript:
window.myNamespace = Object.assign({}, window.myNamespace, {
mySubNamespace: {
pointToLayer: function() {
return window.innerWidth
}
}
});
I'm trying to retrieve the variable from python using:
ns = Namespace("myNamespace", "mySubNamespace")
geojson = dl.GeoJSON(options=dict(pointToLayer=ns("pointToLayer")))
This apparently returns a dash_extensions.javascript.Namespace object but I'm not sure how to use it to retrieve the window width. Is there a way to do this?

Accessing elements added to DOM with ng-bind-html

This probably has been answered before but I have tried for 48 hours now to find an answer with now luck. Posting this is a last resort.
So I have this Tetris clone and it works as seen here: http://www.crawfordcomputing.com/portfolio/Jetris.php
But it does not work in this single page angular app:
http://www.crawfordcomputing.com/AkadineWebOS/
I am re-coding the whole site to be a AngularJS single page app. To this end, I load the html via ng-bind-html and the compile directive found here: AngularJS directive in ng-bind-html.
The html string I am binding is exactly: EDIT: tried making canvas a childnode, did not work.<div><canvas id='gamewindow' width='780px' height='560px'></canvas></div>
The necessary scripts are loaded via the function found here: Single page application - load js file dynamically based on partial view
Note: html5jetris.js is actually an object: var jetris = { startgame: function(), ...};
Using firebug, I can see all added elements and the scripts in the DOM. I also am assuming that being a single page already loaded that I cannot use window.onload = jetris.startGame; after the closing ";" of var jetris = {};
1) I tried putting an alert("hello"); as second to last line (just before the call to startGame) and tried stepping through with firebug. It appears that html5jetris.js, while loaded, is not running.
2) I tried to access the canvas by id directly from address bar with javascript:getElementByID("gamewindow").innerHTML = "hello";
no success; this was several seconds after the ng-bind-html executed well past the 1-1.5 seconds it takes angular to load it.
3) if I pre-emptivly load all the js in my index.php, then firefox will step through it. But I only want to load the javascript when needed. This does not fix not being able to access the canvas by id.
4) by it self, Jetris works, just not loaded dynamically by AngularJS.
Note: Jetris does not require jQuery, even though it's there for Angular. It depends on my own utitlty.js (made as part of a javascript class, converted to object: var U = {};) and my own html5canvas.js (again, var canvas = {};) Since these are objects, the variables in them should not bump heads with anything jQuery or Angular.
Any ideas?
here is the directive:
.directive('compile', ['$compile', function ($compile) {
return function(scope, element, attrs) { //taken from Joël (Thank you!) source: https://stackoverflow.com/questions/26594153/angularjs-directive-in-ng-bind-html
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
}])
Jetris.php:
<?php //Jon Crawford AkadineWebOS pages/jetris.php
//describe window
$window = array();
$window['content'] = "<canvas id='gamewindow' width='780px' height='560px'></canvas>";
$window['title'] = "Jetris";
$window['x'] = 20;
$window['y'] = 250;
$window['id'] = 900;
$window['requireJS'] = array();
$window['requireJS'][0] = "scripts/html5Jetris.js";
$window['requireJS'][1] = "scripts/html5Canvas.js";
$window['requireJS'][2] = "scripts/utilities.js";
echo json_encode($window);
?>
This gets loaded to a div with title bar and exit button that is draggable. My menu system is icon like. My AkadineWebOS looks like a regular desktop. I have an about.php and a welcome.php that work just fine in my div-windows.
Ok so this is a special case I probably did not clarify my question. Here are the steps I used to accomplish my goal.
To load an HTML formated string to the DOM I used a $compile directive by Joël (Thank you!) source: AngularJS directive in ng-bind-html
To load external dependency script files, I used a load script function by Ben Thielker (Thank you!) source: Single page application - load js file dynamically based on partial view
Then I change the onload in my java game Jetris to: var externalJS = { run: function() { window.jetris.startGame(); } };
So that I will not have to re-code my angular app whenever I want to add a new game or other javascript app, this allows my app to check if var externalJS is undefined and if is is defined, it executes externalJS.run();. If the app's div-window is closed, this will effectively restart it when reopened.
Now since everything is loaded using Angular's HTTP module, you have to wait for Angulars promise to finish for everything to be loaded. If I try to run it to soon, I get a blank window and no game. To that end, I use
setTimeout(function() {
if (typeof externalJS != 'undefined'){
externalJS.run();
}},750);
in the HTTP module's successCallback to give it some time.
Here is a link to a working Alpha of the project, with all issues questioned here ironed out. You can drag the windows and icons, open more than one of the same window like a normal desktop unless a oneInstance flag is set like with Jetris, and you can play Jetris! Check it out! AkadineWebOS
Triva: I created the acronym Akadine as a online handle with a program that made random readable words (not real, just readable) years ago, and came up only recently with Advanced Kiosk And Dynamic Internet Navigation Environment.

Store/Import variables & functions Processing(JS)

Stack Overflow! I wanted to store variables in another file so that I would load the variables in file 1, and draw the scene in file 2, ex.
closet.js
var message = "Hello there";
drawer.js
draw = function() { text(message, 100, 100); };
So I would do something like that, but instead of importing the files like this;
<canvas data-processing-sources="closet.js drawer.js"></canvas>
I wanted to be able to include them in file 2, sort of like this;
closet.js
var message = "Hello there";
drawer.js
import("closet.js");
draw = function() {
text(message, 100, 100);
};
Is there a way to do this without including them in the HTML file itself?
Thanks in advance :)
Check out this question, which yours might even be a duplicate of. It lists several ways to do this in JavaScript, including using JQuery or dynamically adding the <script> tags to the <head> of your page.
But if you're using the Processing editor, you have another option: use classes by creating a new tab in the Processing editor. For all your purposes, you can treat this as a separate file. So lets say you created a separate Closet tab with a message variable. Your main sketch code might look like this:
Closet c = new Closet();
draw = function() {
text(c.message, 100, 100);
};
I think this is probably the way to go. It seems like you're trying to over-engineer a solution: either include the files in the html (this is what 99% of all JavaScript code does) or use a class (this is what 99% of all Processing code does).

Finding the variable of a Canvas Element in unobfuscated Javascript

I am working with a GameMaker Studio and exporting a game in HTML5.
A missing feature in GameMaker Studio is the ability to export an image of the screen to the photo album of iOS and Android devices.
My workaround idea is to use a 3rd party plugin for HTML5 that will allow me to save a Canvas.
However GameMaker obfuscates the final output of the javascript and canvas it creates.
All I need to do is find which global variable contains the Canvas and I can capture it.
I have gone through a sample of unobfuscated Javascript but I can't read the code well enough to know which global variable I am supposed to use to call the canvas in question (and I don't want to override the context of it will wipe the canvas).
I found where the Canvas is attached to the context. Can anyone help here?
A sample looks like this:
_j61.prototype._q61 = function () {
_C8 = document.getElementById("canvas").getContext("2d");
if (_C8) {
this._yr = new _e9._B9._ua();
this._yr._Gp(_C8);
this._yr._Jp(1.0 / this._xW);
this._yr._Rp(0.1);
this._yr._Mp(1.0);
this._yr._Bp(_e9._B9._ua._2q | _e9._B9._ua._3q);
this._Fm._xr(this._yr)
}
};
In my function (for example) what goes here?
HELPME.fillStyle = "#FF0000";
HELPME.fillRect(0,0,150,75);

Auto-load/include for JavaScript

I have file called common.js and it's included in each page of my site using <script />.
It will grow fast as my sites functionality will grow (I hope; I imagine). :)
Lets example I have a jQuery event:
$('#that').click(function() {
one_of_many_functions($(this));
}
For the moment, I have that one_of_many_functions() in common.js.
Is it somehow possible that JavaScript automatically loads file one_of_many_functions.js when such function is called, but it doesn't exist? Like auto-loader. :)
The second option I see is to do something like:
$('#that').click(function() {
include('one_of_many_functions');
one_of_many_functions($(this));
}
That not so automatically, but still - includes wanted file.
Is any of this possible? Thanks in an advice! :)
It is not possible to directly auto-load external javascripts on demand. It is, however, possible to implement a dynamic inclusion mechanism similar to the second route you mentioned.
There are some challenges though. When you "include" a new external script, you aren't going to be able to immediately use the included functionality, you'll have to wait until the script loads. This means that you'll have to fragment your code somewhat, which means that you'll have to make some decisions about what should just be included in the core vs. what can be included on demand.
You'll need to set up a central object that keeps track of which assets are already loaded. Here's a quick mockup of that:
var assets = {
assets: {},
include: function (asset_name, callback) {
if (typeof callback != 'function')
callback = function () { return false; };
if (typeof this.assets[asset_name] != 'undefined' )
return callback();
var html_doc = document.getElementsByTagName('head')[0];
var st = document.createElement('script');
st.setAttribute('language', 'javascript');
st.setAttribute('type', 'text/javascript');
st.setAttribute('src', asset_name);
st.onload = function () { assets._script_loaded(asset_name, callback); };
html_doc.appendChild(st);
},
_script_loaded: function (asset_name, callback) {
this.assets[asset_name] = true;
callback();
}
};
assets.inlude('myfile.js', function () {
/* do stuff that depends on myfile.js */
});
Sure it's possible -- but this can become painful to manage. In order to implement something like this, you're going to have to maintain an index of functions and their corresponding source file. As your project grows, this can be troublesome for a few reasons -- the 2 that stick out in my mind are:
A) You have the added responsibility of maintaining your index object/lookup mechanism so that your scripts know where to look when the function you're calling cannot be found.
B) This is one more thing that can go wrong when debugging your growing project.
I'm sure that someone else will mention this by the time I'm finished writing this, but your time would probably be better spent figuring out how to combine all of your code into a single .js file. The benefits to doing so are well-documented.
I have created something close to that a year ago. In fact, I have found this thread by search if that is something new on the field. You can see what I have created here: https://github.com/thiagomata/CanvasBox/blob/master/src/main/New.js
My project are, almost 100% OOP. So, I used this fact to focus my solution. I create this "Class" with the name "New" what is used to, first load and after instance the objects.
Here a example of someone using it:
var objSquare = New.Square(); // Square is loaded and after that instance is created
objSquare.x = objBox.width / 2;
objSquare.y = objBox.height / 2;
var objSomeExample = New.Stuff("some parameters can be sent too");
In this version I am not using some json with all js file position. The mapping is hardcore as you can see here:
New.prototype.arrMap = {
CanvasBox: "" + window.MAIN_PATH + "CanvasBox",
CanvasBoxBehavior: "" + window.MAIN_PATH + "CanvasBoxBehavior",
CanvasBoxButton: "" + window.MAIN_PATH + "CanvasBoxButton",
// (...)
};
But make this more automatic, using gulp or grunt is something what I am thinking to do, and it is not that hard.
This solution was created to be used into the project. So, the code may need some changes to be able to be used into any project. But may be a start.
Hope this helps.
As I said before, this still is a working progress. But I have created a more independent module what use gulp to keep it updated.
All the magic que be found in this links:
https://github.com/thiagomata/CanvasBox/blob/master/src/coffee/main/Instance.coffee
https://github.com/thiagomata/CanvasBox/blob/master/src/node/scripts.js
https://github.com/thiagomata/CanvasBox/blob/master/gulpfile.js
A special look should be in this lines of the Instance.coffee
###
# Create an instance of the object passing the argument
###
instaceObject = (->
ClassElement = (args) ->
window[args["0"]].apply this, args["1"]
->
ClassElement:: = (window[arguments["0"]])::
objElement = new ClassElement(arguments)
return objElement
)()
This lines allows me to initialize a instance of some object after load its file. As is used in the create method:
create:()->
#load()
return instaceObject(#packageName, arguments)

Categories

Resources