Finding the variable of a Canvas Element in unobfuscated Javascript - 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);

Related

Save Empscripten webGL canvas as image in JS

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));

How to access PGraphics pixels in p5.js

I'm trying to access pixels from a PGraphics instance in p5.js but even after calling loadPixels() the array is empty.
Here's what I've tried:
var buffer;
function setup() {
createCanvas(100,100);
pixelDensity(1);
buffer = createGraphics(100,100);
buffer.background(128);
}
function draw() {
image(buffer,0,0);
}
function mouseDragged(){
buffer.ellipse(mouseX,mouseY,3,3);
buffer.loadPixels();
console.log(buffer.pixels);//expecint pixels array, getting empty array
}
Is it possible to access pixels in PGraphics with p5.js ? If so, how ?
This appears to have been a bug: https://github.com/processing/p5.js/issues/1403
Other related bugs here and here.
It is fixed with the latest version (Version 0.5.3 from August 17, 2016).
If you're using the p5.js editor like me, then apparently that uses an old version of the library. You can download the latest version and copy it into your sketch folder. That seems to be persisting between runs of the editor, which is slightly scary, but maybe some magic is going on behind the scenes.
Anyway, your code works as expected after updating to the latest version.

Moving created files with JXA

I'm new to JXA scripting, but I'm attempting to troubleshoot some older scripts currently in place here at work. They loop through an InDesign document and create several PDFs based on it. Previously, they would be stored in a folder called "~/PDFExports". However, this doesn't work with 10.10.
If I change the code to just place the PDFs in "~/", it works fine. From there, I'd like to move the files to "~/PDFExports", but I can't seem to find an answer on how to do that. I've seen things about making calls to ObjC, or to call Application('Finder'), but neither work - they both return undefined.
Am I just missing something basic here, or is it really this hard to simply move a file with JXA?
EDIT: Some syntax for how I'm creating the folder in question and how I'm attempting to work with Finder.
//This is called in the Main function of the script, on first run.
var exportFolder = new Folder(exportPath);
if(!exportFolder.exists) {
exportFolder.create();
}
//This is called right after the PDF is created. file is a reference to the
actual PDF file, and destination is a file path string.
function MoveFile(file,destination){
var Finder = Application("Finder");
Application('Finder').move(sourceFile, { to: destinationFolder });
alert("File moved");
}
Adobe apps have long included their own embedded JS interpreter, JS API, and .jsx filename extension. It has nothing to do with JXA, and is not compatible with it.
InDesign's JSX documentation:
http://www.adobe.com/devnet/indesign/documentation.html#idscripting
(BTW, I'd also strongly advise against using JXA for Adobe app automation as it has a lot of missing/broken features and application compatibility problems, and really isn't fit for production work.)
Here's the link to Adobe's InDesign Scripting forum, which is the best place to get help with JSX:
https://forums.adobe.com/community/indesign/indesign_scripting
You could use Cocoa to create the folder
var exportFolder = $.NSHomeDirectory().stringByAppendingPathComponent("PDFExports")
var fileManager = $.NSFileManager.defaultManager
var folderExists = fileManager.fileExistsAtPath(exportFolder)
if (!folderExists) {
fileManager.createDirectoryAtPathWithIntermediateDirectoriesAttributesError(exportFolder, false, $(), $())
}
and to move a file
var success = fileManager.moveItemAtPathToPathError(sourceFile, destinationLocation, $());
if (success) alert("File moved");
Consider that destinationLocation must be the full path including the file name
and both sourceFile and destinationLocation must be NSString objects like exportFolder
Could it be that the folder is missing ? Could be your reference to the folder object not valid ? Any syntax to show ?
I will share some of what I learned about JXA move and duplicate methods. I am not a professional programmer just an attorney that is passionate about automation. My comments come from much trial and error, reading whatever I could find online, and A LOT of struggle. The move method does not work well with Finder. Use the System Events move method instead. The duplicate method in Finder works just fine. The duplicate method does not work well in system events. This is a modified snippet from a script I wrote showing move() using System Events.
(() => {
const strPathTargetFile = '/Users/bretfarve/Documents/MyFolderA/myFile.txt';
const strPathFolder = '/Users/bretfarve/Documents/MyFolderB/';
/* System Events Objects */
const SysEvents = Application('System Events');
const objPathFolder = SysEvents.aliases[strPathFolder];
SysEvents.move(SysEvents.aliases.byName(strPathTargetFile), {to: objPathFolder});
})();

Saving animated SVG from a webpage

There's an Internet Site with a Chinese dictionary that uses animations to show you the proper way to write Chinese characters. I'm not entirely sure how the animations work, but they appear to SVG-based. You can find an example of the animation here:
http://ce.linedict.com/dict.html#/cnen/entry/e1d0a1716f89470d88087dce285914a1
Be sure to click "Strokes" to view the animation.
So here"s the challenge I'm facing: I was wondering if there is any way to download the animations so that they can be viewed offline. The reason I want to do this is to add them to Anki, a flashcard app that I use on my iPad when I'm on the go (i.e. offline).
I tried examining the element in Firefox and saving the inner HTML of the element as a separate file, but that results in an static page. Doing the same in Safari is impossible as the inner HTML keeps changing as the animation is rendered, which makes me think that the inner HTML is being rendered on the fly by a separate (?) Java / (?) Jquery script nested somewhere on the site. Unfortunately, my knowledge of Java is rudimentary at best, and although I've started a tutorial online to get myself acquainted with the code, I think it'd be ages before I can figure out what's going on here. That's why I'm looking for help from you guys who have more experience with webpages than I do.
So is there any way to save the animation for offline use, either as a code or a svg / animated gif file? Anki, the flashcard app I'm using, uses HTML to render its content in a similar manner to web browsers (and as far as I know there are ways to integrate JavaScript).
I guess that I could use screen capturing programs to record the animation and save it as a .gif file, but given the fact that there are about 3500-5000 Chinese characters that a learner must master to be fluent in Chinese, that would take a hell of a time, so I'm looking for something that can be Scriptable (I have some working understanding of Apple Scripting, and could take it from myself there once I understand how to the animation works).
I'd appreciate any help or suggestion that could bring me any further.
---Edit on 2015-05-23:
Like I mentioned in a reply to Robert below, I've had a look at the DOM structure of a different entry, this time for a single character rather than a multi-character word. When the stroke order animation button on the page is activated, the following code can be found in the DOM structure:
<input type="hidden" id="hidden_strokeFileName" value="1200爸.swf">
as you can see there seems to be a reference to a file name with a flash-based animation. If the animation is indeed contained in an .swf file, it should be possible to download it for offline use, right? However, I'm baffled as to how the browser stitches up the URL under which the animation can be found so that I could download it. Can anyone help?
Here's the link to the page: http://ce.linedict.com/dict.html#/cnen/entry/cca145dd67574395a5a28af08a3afb30
I created a simple tool (script snippet, 16 lines of code): https://gist.github.com/VityaSchel/bc2a83e330661c2bae580ebb4aab05cf
It records svg changes using MutationObserver interface. I used it to record animations of "d" argument but you can modify this script to record all changes in svg structure and attributes.
One major issue with this tool is that it records unevenly, so some parts may be slightly slower than others.
let recordedChanges = []
let startRecordingSVG = () => { recordedChanges = [] }
let stopRecordingSVG = () => { console.log(JSON.stringify(recordedChanges)) }
let observer = new MutationObserver(mutationRecords => {
for(const record of mutationRecords) {
if(record.attributeName === 'd') {
recordedChanges.push(record.target.getAttribute('d'))
}
}
})
observer.observe($0, {
subtree: true,
attributes: true
})
/* USAGE */
// 1. Select target svg (or better it's parent, because svg may be overwritten) in Elements panel
// 2. Paste this into Console
// 3. Paste startRecordingSVG() into console
// 4. Do something so animation plays
// 5. Paste stopRecordingSVG() into console
// 6. An array with all animation keyframes will be printed in JSON format, use it to transform to lottie animation or CSS keyframes
In addition to that I created a function to convert this array to CSS keyframes:
function arrayFramesToCSSKeyframes(array) {
let frames = '', i = 1
for(let framePath of array) {
const percentage = i/array.length*100
const frameCode = []
frames += ` ${Math.round(percentage)}% {\n`
frames += ` d: path("${framePath}");\n`
frames += ` }\n\n`
i++
}
return `#keyframes frames {\n${frames}\n}`
}

Paperscope and paperjs

So I am trying to create a project with two canvas elements, each with its own paperscript, and with buttons on the outside of each that control certain functions within both.
Under the documentation under Paperscript, it says:
Please note:
When including more than one PaperScript in a page, each script will run >in its own scope and will not see the objects and functions declared in >the others. For PaperScript to communicate with other PaperScript or >JavaScript code, see the tutorial about PaperScript Interoperability.
... which is unfortunate because that tutorial reads as follows:
Coming very soon!
I've gotten stuck very quickly in this process. I've tried putting functions in global scope, calling them from outside their canvas, and seeing them print on the wrong canvas. I've tried exporting functions via a module and it seems to run the function (?!?!). And worst, the 'paper.projects' object is an array with one(!) project in it, the first canvas.
So I'm stumped.
Anyone know how to do this?
EDIT: So apparently there is this answer but I don't see how that gets me being able to call functions in the PaperScript scope from global scope scripts.
That just seems like a script to call global functions in the PaperScope, which doesn't work for me if I'm trying to make outside buttons do things.
Clearly I'm missing something.
SECOND EDIT: I have played around with various global functions, either in window.global or just sitting by themselves with no var declaration... but what seems to happen is that when I try to call a function which I have defined, say as:
globals.makecircle = function () {
var o = new Path.Circle({
radius: 50,
center: new Point (200,200)
})
}
in the main scope, it will just as soon run in the wrong window as the correct window. Also there is an incredible delay before it runs which I can't figure out.
THIRD EDIT: For clarity.
I have firstcanvas.js attached to canvas1 in my HTML, I have secondcanvas.js attached to canvas2. Both are referenced as paperscript type, as:
<script type="text/paperscript" src="scripts/firstcanvas.js" canvas="canvas1"></script>
<script type="text/paperscript" src="scripts/secondcanvas.js" canvas="canvas2"></script>
I create window.globals object as Jurg suggests. I call it from main.js with a button, such as:
window.globals = {}
`$('document').ready($('#dfs').on('click', window.globals.makecircle))`
I add this function to globals in firstcanvas.js as above.
If I have most recently clicked on canvas2, clicking on the button with id='DFS' will cause the function to run, extremely delayed, on canvas2.
And paper.projects does not list both projects, so I can't use the activate() functions.
Okay! SOLVED!!!
Here's how to reference/activate PaperScript-created scopes from the global scope. Although there is no user-accessible array of scopes (that I know of), PaperScope.get(id) will retrieve them. For some reason I find PaperScope.get(0) already populated, and my two canvas/PaperScript elements actually referring to scopes with ids 1 and 2.
Therefore:
pscope1 = PaperScope.get(1)
pscope2 = PaperScope.get(2)
Then, in any function where I want to do something on my first canvas:
pscope1.activate()
// cool paper.js graphics stuff
pscope1.view.update()
The last line is because paper.js won't automatically update a view that the user is not interacting with.
Thanks to Jurg Lehni for the hint to use .activate().
PS Make sure that your paperscript objects are created before using PaperScope.get. I used good 'ol JQuery $('document').ready() for this...
PPS Another little hit from Jurg Lehni himself: Inside a PaperScript, this will point to the current scope. You could use that and store it in the global object.

Categories

Resources