Embed text into PNG - javascript

I'm looking for a tool that'd let me embed json-formatted information inside a PNG -file.
This far it's been quite quiet. Do I have to write it myself?
I'd be especially interested of doing it with javascript. Into an image I extract from a canvas with toDataURL -method.

If you want to embed text you may want to look at this in particular, which comes from the PNG specification. It seems a little on the complicated side.
The "easy" steganographic method looks a little more simple.
What may actually be better suited for your purpose is to create a javascript object that contains the image data and the JSON data - then just pass that object around wherever you need it.

I am completely unfamiliar with Python, however if you can access any of the prominent image processing libraries it is possible.
Take a look here for ImageMagick<->Python solutions.
Edit
You may wish to take a look at this blog post for information regarding steganography (hiding information within an image) and an offshoot of the ImageMagick library. It uses C++ here but I'm sure you could figure out a way to incorporate the base processes in Python.

Hmm, here’s a partial libpng implementation in JS: http://www.xarg.org/download/pnglib.js.

Copying another answer of mine in another question:
Here's an old not too-fancy module I did for a friend once (Python 2.x code):
the code
from __future__ import division
import math, os, array, random
import itertools as it
import Image as I
import sys
def encode(txtfn, imgfn):
with open(txtfn, "rb") as ifp:
txtdata= ifp.read()
txtdata= txtdata.encode('zip')
img= I.open(imgfn).convert("RGB")
pixelcount= img.size[0]*img.size[1]
## sys.stderr.write("image %dx%d\n" % img.size)
factor= len(txtdata) / pixelcount
width= int(math.ceil(img.size[0]*factor**.5))
height= int(math.ceil(img.size[1]*factor**.5))
pixelcount= width * height
if pixelcount < len(txtdata): # just a sanity check
sys.stderr.write("phase 2, %d bytes in %d pixels?\n" % (len(txtdata), pixelcount))
sys.exit(1)
## sys.stderr.write("%d bytes in %d pixels (%dx%d)\n" % (len(txtdata), pixelcount, width, height))
img= img.resize( (width, height), I.ANTIALIAS)
txtarr= array.array('B')
txtarr.fromstring(txtdata)
txtarr.extend(random.randrange(256) for x in xrange(len(txtdata) - pixelcount))
newimg= img.copy()
newimg.putdata([
(
r & 0xf8 |(c & 0xe0)>>5,
g & 0xfc |(c & 0x18)>>3,
b & 0xf8 |(c & 0x07),
)
for (r, g, b), c in it.izip(img.getdata(), txtarr)])
newimg.save(os.path.splitext(imgfn)[0]+'.png', optimize=1, compression=9)
def decode(imgfn, txtfn):
img= I.open(imgfn)
with open(txtfn, 'wb') as ofp:
arrdata= array.array('B',
((r & 0x7) << 5 | (g & 0x3) << 3 | (b & 0x7)
for r, g, b in img.getdata())).tostring()
findata= arrdata.decode('zip')
ofp.write(findata)
if __name__ == "__main__":
if sys.argv[1] == 'e':
encode(sys.argv[2], sys.argv[3])
elif sys.argv[1] == 'd':
decode(sys.argv[2], sys.argv[3])
the algorithm
It stores a byte of data per image pixel using: the 3 least-significant bits of the blue band, the 2 LSB of the green one and the 3 LSB of the red one.
encode function: An input text file is compressed by zlib, and the input image is resized (keeping proportions) to ensure that there are at least as many pixels as compressed bytes. A PNG image with the same name as the input image (so don't use a ".png" filename as input if you leave the code as-is :) is saved containing the steganographic data.
decode function: The previously stored zlib-compressed data are extracted from the input image, and saved uncompressed under the provided filename.
I verified the old code still runs, so here's an example image containing steganographic data:
You'll notice that the noise added is barely visible.

You can use the recently released JavaScript library steganography.js. Have a look at the showcase and the documentation for the basic usage.
The library makes use of a HTML canvas and embeds the information in the alpha channel of the image. Basically you just need to use steg.encode(message, image) and pass the JSON-data (as string) as message and the image as dataURL. To read this information from the image use steg.decode(image) and pass the image as dataURL again.
After reading the information again you get the JSON-data as string so you will have to parse it to a JavaScript object again. So besides the parsing from/to string I hope it fits your requirements.

The answers here have seriously over-engineered the question. I am sorry you had to wait 11 years for the answer. The ability to do this is standard and part of the PNG spec. Tons of apps use this today.
You can drop JSON or any other data right into PNGs. You can do this in like 3 lines of JavaScript with the npm "png-chunk-text" (https://www.npmjs.com/package/png-chunk-text)
Now you can read and write JSON inside of PNGs at lightning speed and it took you 30 seconds to develop it, and the process is standard, i.e., other tools can read it too.

Someone has already attempted to embed a Mario-like platformer game in a PNG image. Using getImageData is the key to reading out the data; the object that that method returns gives a one-dimensional array holding the individual RGBA values of each pixel.
Note that this does not "hide" the data within the area that the user sees, but you could probably use a CSS sprite like technique (or "crop" the image using multiple canvases) to show only the part of a combined image you want to.

Why not just use this tool?
http://www.jsclasses.org/package/324-JavaScript-Embed-encoded-text-in-images-using-steganography.html
and here on github:
https://github.com/blauharley/LoremImageCryptonator
It's a normal object that can be used for embedding text into each color-channel(red, green...).
When embedding text into the alpha-channel there should not be as much noise as embedding text on other color-channels, so you should not see any difference before and after inserting text into an image.

Related

Confusions about different ways of displaying images from an external url using JavaScript

I recently learned there seem to be multiple ways to display an image on a web page.
The first way is to directly assign the URL to an image element's URL
const img = new Image();
img.onload = () => {
document.querySelector("#myImage").src = url;
};
img.onerror = () => {};
img.src = imageUrl;
Another way I recently learned is using fetch
fetch(imageUrl)
.then((response)=>response.blob())
.then((blob)=>{
const objectUrl = URL.createObjectURL(blob)
document.querySelector("#myImage").src = objectUrl;
})
I have a few questions about both approaches:
I am familiar with fetch but I normally use it to fetch JSON. The second way of using fetch to get the image seems to me like we are fetching raw binary data of that image file over HTTP, while the first one we are delegating the browser to kick off a Get request to fetch that image. But from the server's perspective, there are no differences in terms of how it sends the image down? Am I understanding this right?
In what situations we should favor one approach over the other? I feel like the second approach is going to have a lot of CORS problems than the first one, but not sure exactly why.
Are there any other ways to display an image on a web page? I heard of base64 encoding/decoding a lot when people talk about images. Is base64 related to response.blob() i.e. the second approach? Or it is different? If so, can someone please give me an example of using base64 to show an image?
Lastly, I think displaying images has been a hole in my knowledge of frontend or web development. Please feel free to recommend any good resources on this subject.
To answer your questions
It's basically same from the server side, except for headers and information the browser could send along, whereas in fetch you have full control over headers
The fetch method could be used for more flexible or further parameters requiring requests or secure transfers you might implement in your server. For instance, requiring a post request that has header or body containing certain data to permit transfer of image... or for flexible transfers such as data that could be put in chunks that you could later assemble and manipulate before presenting.
Using base64 is almost the same as binary, though it is used to transfer images/data through mediums designed to transfer text. This is because base64 uses ascii number and letters to represent any data.
Here, you can see an image generated from base64 string characters, without any link
<img style="width:64px; height:64px;" src="data:image/png;base64,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"></img>
This is also helpful to embed image directly in code as text, pass it in as a url parameter or you can even put image in JSON file. In some cases, it also performs better than binary image with compression like gzip too.
Direct Answer
Yes the server can send any kind of data (anything is just a binary consecution of numbers, even text and characters). It is the client which tries to make sense of the content, usually following certain standards.
If you don't need to manipulate the image I suggest you to avoid any request since the browser can handle it for you. Note that only fetch is hardly subjected to CORS because it does not send cross-origin cookies (see MDN - using fetch). If you need more control you can use XMLHTTPRequest. The usage of blob is inherited from old web approaches, those times where ArrayBuffer were not invented, and it was the only wrapper for binary data in the web environments. It is still supported for many reason.
Actually less than you think (just 1)! Check explanation...
Image resources are widely dependent on what kind of processing you need... Just displaying an image? MDN and CSS-tricks are full of tips, you just need to search for them. If you want to process an image instead, you need to take a further look to canvas elements and the usual resources are scarce or almost about game making (for obviously reasons), MDN and something in CSS-tricks for resources.
Explanation
What is an image?
I think you have a bias toward the concept of a browser displaying an image.
So what a image really is? binary data.
Pratically there is only one way for your browser (or your computer in general) to display an image, that is to have a byte array that is a flatten view of the pixels of your image. So at the end of the day you will ALWAYS need to feed your broswer with binary data that is interpretable as a raw image (usually a set of rgb(a) pixels).
Yes, there is only "ONE" way to display an image on a computer. But there are different ways we can represent that image.
Encoding
At low level, different computers represent numbers in different ways, so the web standards decided to represent images in so called RGB8 or RGBA8 encoding (Red-Green-Blue(-Alpha)-NumBits, 8 bits = 1 Byte). This means that each pixel is represented by an array of 4 bytes (numbers), each varying from 0 to 255. This array is the only thing your browser see as image.
At the end your image is something like this:
// using color(x, y) to describe the image pixels
[ red(0, 0), green(0, 0), blue(0, 0), alpha(0, 0), red(1, 0), ... ] =
[ 124, 12, 123, 255, 122, ... ]
Now that you can see an image as a linear array of pixels, we can decide how to write it down on a piece of paper (our "code"). The browser (usually and historically) parse every packet sent on the web in an HTML file as plain text, so we must use characters to describe our image, the standard is to ONLY use UTF-8 (character encoding, superset of ASCII). We can write it in JS as an array of numbers for example.
But take a look to the number 255. Each time you send that number on the newtork you are sending 3 characters: '2', '5', '5'. Web comunicates only with characters so... Is there a way to make a compact representation of that number in order to send less bytes as possible (saving those guys who have slow connection!)?
Base64 is the most famous encoding used to describe that linear array in the most compact way, because it compress the 255 characters into just 1 or 2 characters (depending on the sequence). Instead of representing number in base of 10, we can take rid of some characters we usually use as letters to represent more digits. So '11' become 'a', '12' => 'b', '13' => 'c', ..., '32' => 'a', ..., '63' => 'Z', '64' => '10', '65' => '11', ..., '128' => '20', and so on. Furthermore this algorithm exploit more low level representation to encode more digits in one single character (that's why u will see some '=' at the end sometimes).
Take a look on different representation of the same image:
// JavaScript Array
[ /* pixel 1 */ 124, 12, 123, 255, /* pixel 2 */ 122, 12, 56, 255 ] // 67 characters
// (30 without spaces and comments)
// Base64
fAx7w796DDjDvw== // 16 characters
// Base32
3sc3r7v3qc1o7vAb== // 18 characters (always >= Base64)
It's easy to see why they choose base64 as common algorithm (and this example counts just for 2 pixels).
(Base32 image example)
Formats
Now imagine to send a 4K image on the web, which has a dimension of 3'656 × 2'664. This means that you are sending on the internet 9'739'584 of pixels, with 4 bytes each, for a grand total of 38'958'336 bytes (~39MB). Furthermore imagine what a waste if the image is completely black (we can describe the whole image just with one pixel)... That's too much (especially for low connections), for this reason they invented some algorithms which can describe the image in a more compact way, we call them image format. PNG and JPEG/JPG are example of formats which compress the image (jpg 4k image ~8MB, png 4k image can vary from ~2MB to ~22MB depending on certain parameters and the image itself).
Someone keep the compression thing to a further level, enstabilishing the gzip compression standard format (a generic compression algorithm over an already compressed image, or any other kind of file).
Drawing on the Browser
At the end of this journey you have just two different ways browsers allow you to draw content: URI and ArrayBuffer.
URI: you can use it with <img> and css, by setting src property of the element or by setting any style property which can get an image URL as input.
ArrayBuffer: by manipulating the <canvas>.context buffer (that is just the linear array we discussed above)
Obviously browsers allow also to convert or switch between the two ways.
URI
URI are the way we define a certain content, that can be a stored resource (URL - all protocols but data, for example http(s)://, ws(s):// or file://) or a properly buffer described by a string (the data protocol).
When you ask for an image, by setting the src property, your browser parses the URL and, if it is a remote content, makes a request to retrieve it and parse the content in the proper way (format & encoding). In the same way, when you make a fetch call you are asking the browser to request the remote content; the fetch function has the possibility to get the response in different ways:
textual, just a bunch of characters (usually used to parse JSON/DOM/XML)
binary data, divided in:
ArrayBuffer, which is a representation of the linear array of the image, we discussed above
Blob, which is an abstract representation of a generic file-like object (which also encapsulate an internal ArrayBuffer). The Blob is something like a pointer to a file-like entity in the browser cache, so you don't need to download/request the same file multiple times.
// ArrayBuffer from fetch:
fetch(myRequest).then(function(response) {
return response.arrayBuffer();
})
// Blob from fetch
fetch(myRequest).then(function(response) {
return response.blob();
})
// ArrayBuffer from Blob
blob.arrayBuffer();
So now you have to tell to the browser how to make sense of the content you get back from the response. You need to convert the data to a parsable url:
var encodedURI = `data:${format};${encoding},` + encodeBuffer(new Uint8Array(arrayBuffer));
image.src = encodedURI
// for base64 encoding
var encodeBuffer = function(buffer) { return window.btoa(String.fromCharCode.apply(null, buffer)); }
// for blobs
image.src = (window.URL || window.webkitURL).createObjectURL(blob);
Note that browsers supports other encodings than just base64, also base32 is available but, as we saw above, is not so convinient to use. Also there is no builtin function like btoa to encode a buffer in base32.
Note also that the format value can be any kind of MIME type such as image/png|jpg|gif|svg or text/plain|html|xml, application/octet, etc.. Obviously only image types are then shown as images.
When the resource is not requested from a remote server (with a file:// or data protocol) the image is usually loaded syncronously, so as soon you set the URL, the browser will read, decode and put the buffer to display in your image. This has two consequences:
The data is managed locally (no internet connection requirements)
The data is treated synchronously, so if the image is big your computer will stuck into the processing until the end (see why it is a bad practice to use data protocol for videos or huge data in the special section, at the end)
URL vs URI
URI is a generic identifier for a resource, URL is an identifier for a location where to retrieve the resource. Usually in a browser context are almost an overlapped concept, I found this image explain better than thousand words:
The data is pratically an URI, every request with a protocol is actually an URL
Side Note
In your question you write this as an alternative method by "setting the image element url":
fetch(imageUrl)
.then((response)=>response.blob())
.then((blob)=>{
const objectUrl = URL.createObjectURL(blob)
document.querySelector("#myImage").src = objectUrl; // <-- setting url!
})
But watch out: you actually setting an image element source URL!
Canvas
The <canvas> element gives you the full control over an image buffer, also to further process it. You can literally draw your array in it:
var canvas = document.getElementById('mycanvas');
// or offline canvas:
var canvas = document.createElement('canvas');
canvas.width = myWidth;
canvas.height = myHeight;
var context = canvas.getContext('2d');
// exemple from array buffer
var arrayBuffer = /* from fetch or Blob.arrayBuffer() or also with new ArrayBuffer(size) */
var buffer = new Uint8ClampedArray(arrayBuffer);
var imageData = new ImageData(buffer, width, height);
context.putImageData(iData, 0, 0);
// example image from array (2 pixels)
var data = [
// R G B A
255, 255, 255, 255, // white pixel
255, 0, 0, 255 // red pixel
];
var buffer = new Uint8ClampedArray(data);
var imageData = new ImageData(buffer, 2, 1);
context.putImageData(iData, 0, 0);
(Note ImageData wants a RGBA array)
To get back the ArrayBuffer (which you can also plug back in the image.src after) you can do:
var imageData = context.getImageData(0, 0, canvas.width, canvas.heigth);
var buffer = imageData.data; // Uint8ClampedArray
var arrayBuffer = buffer.buffer; // ArrayBuffer
This is an example on how to process an image:
// reading image
var image = document.getElementById('myimage');
image.onload = function() {
// load image in canvas
context.drawImage(image, 0, 0);
// process your image
context.fillRect(20, 20, 150, 100);
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
imageData.data[0] = 255;
// converting back to base64 url
var resultUrl = window.btoa(String.fromCharCode.apply(null, imageData.data.buffer));
// setting image url and disabling onload
image.onload = null;
image.src = resultUrl;
};
// note src setted after onload
image.src = 'ANY-URL';
For this part I suggest you to take a look to Canvas Tutorial - MDN
SPECIAL
Audio and Video are treated in the same way, but you must encode and format also the time and sound dimension in some way. You can load a audio/video from base64 string (not so good idea for videos) or display a video on a canvas
strait to the point. basically you need to fetch the image instead of adding the url source at the html when you dont want people to see where yow pic is hosted at. if I right click on the image and you have the url directly in the img tag literally err body will be able to download the image, and not only that if I play around with the posible parameters that it might have I could not only get the image, but also get other images. so if you dont want to expose where yow pic is at you can "hide" it.
there are 4 ways you can get an image
html
css
blob
base64
but those 4 ways can be separated in two groups.
handled by the browser
html
css
base64 ...kindof
handled by you
blob
base64 ...kindof
the first group is not relevant as the browser will remove the image when it doesnt need it any mow show it when need it and more stuff.
why base64 could be included in that group ? bcuz if the image is not need it any more the browser cleans the memory used by it. so you just have to parse the binary into base64 and the browser will free any resource used by the pic, but on the other hand blobs are entirely managed and handled by you. so if you dont free the resources used by the pic the memory used in other words wont be able to be used by some other process/program/app, probably you're thinking why blob is prefered over base64?
BLOB stands for Binary Large Object which could be anything - text, images, video, executable code, random bytes, etc. Most databases have a special BLOB type that allows storing this type of data.
Base64 is an encoding that lets you represent any data as plain text. It can be easily be shown on the screen and useful in cases where binary data could be difficult to work with. For example, I could copy/paste the Base64 in the text field here but I won’t be able to do that with binary data. Also, Base64URL encoding is often used in HTTP URLs.
not only the source of yow pic is exposed, it also becomes over 30% bigger/heavier
const fileSelect = document.getElementById("fileSelect"),
fileElem = document.getElementById("fileElem"),
fileList = document.getElementById("fileList");
fileSelect.addEventListener("click", function (e) {
if (fileElem) {
fileElem.click();
}
e.preventDefault(); // prevent navigation to "#"
}, false);
fileElem.addEventListener("change", handleFiles, false);
function handleFiles() {
if (!this.files.length) {
fileList.innerHTML = "<p>No files selected!</p>";
} else {
fileList.innerHTML = "";
const list = document.createElement("ul");
fileList.appendChild(list);
for (let i = 0; i < this.files.length; i++) {
const li = document.createElement("li");
list.appendChild(li);
const image = document.createElement("img");
image.src = URL.createObjectURL(this.files[i]);
image.onload = function() {
image.setAttribute("src", this.result)
URL.revokeObjectURL(this.result);
}
li.appendChild(img);
const info = document.createElement("span");
li.appendChild(info);
}
}
}
<input type="file" id="fileElem" multiple accept="image/*" style="display:none">
<button type="button" id="fileSelect">Select some files</button>
<div id="fileList">
<p>No files selected!</p>
</div>
if you run the example and you right click on them images you'll see an url created, try to open it in another tab or so and you'll see the pic is unreachable
The second way is called Data URL, which allow embed small files inline in HTML/CSS, for example:
<img src="data:image/png;base64,iVBORw0KGgoAAA
ANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4
//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU
5ErkJggg==" alt="Red dot" />
this method can effectively reduce network requests during web page loading progress.
In the follwing situations, Data URL is applicable:
embed small files in html to reduct reqeusts
embed all assets in a single html for archive purpose
load resource dynamically generated by server
The base64 way is just Data URL, in addition to these two methods:
SVG image can directly embed in HTML by <svg> tag
Image can also be dynamically rendered using <canvas> API
Recommand a book to you The definitive guide to HTML5.
First of all, if you want to show image on webpage than we have 2 ways for that
Set image web URL to img property
Set binary code to img property
Option 1 is used when you have stored image to your server folder as a file
Option 2 is used when you have stored image as binary base64 to your database so you retrieve as binary code only or else you have create image from your binary code and than set as per option 1

Can you/how to use textures that are larger than MAX_TEXTURE_SIZE?

I have a PNG file generated on the server that is 1536 by 47616 Pixels. The PNG is paletted, and has 20 entries. The size of the PNG is 2.5MB - i dont see why memory can be an issue for the GPU.
This big image file basically contains a combination of a lot of small textures. Each of the texture inside this big file, is 128px width by 512px height each. Meaning that basically this big image contains 3*372 = 1116 images (1536/512 = 3 image every row, 47616/128 = 372 every column)
I put them in the same image because otherwise the end user will need to request 1116 images seperately, and with requesting and processing headers - this is a lot of images to process. Much faster to request a single massive image.
gl.getParameter(gl.MAX_TEXTURE_SIZE); // this gives me 16384 for my browser. My texture's height of 47616 is a lot higher than this.
Currently i try to bind the texture to the buffer as:
this.texture_image = new Image();
var parent = this;
this.texture_image.addEventListener("load", function(e) {
parent.tinfo.width = parent.texture_image.width;
parent.tinfo.height = parent.texture_image.height;
parent.gl.bindTexture(parent.gl.TEXTURE_2D, parent.tinfo.texture);
parent.gl.texImage2D(parent.gl.TEXTURE_2D, 0, parent.gl.RGB, parent.gl.RGB, parent.gl.UNSIGNED_BYTE, parent.texture_image);
});
this.texture_image.src= texture_image_url;
However this results in Google Chrome complaining:
main.js:5118 WebGL: INVALID_VALUE: texImage2D: width or height out of range
Which is obviously the case, since my height is way outside the MAX_TEXTURE_SIZE range.
So now, is there someway on the client that i can refer to smaller parts of the image? Maybe make my own smaller Image objects out of the large this.texture_image Image object?
I guess i can use Canvas and drawImage, but would prefer a WebGL based solution. Since i will be doing some other effects on it with WebGL later on.
Thanks.
The size of the PNG is 2.5MB - i dont see why memory can be an issue
for the GPU
PNG is a compressed file format not optimized for random realtime access, GPUs don't support encodings like that, without the use of any extensions GPUs only support raw pixel data thus your image will be expanded to WIDTHxHEIGHTx4 => 1536x47616x4 = 292552704 Bytes which are a hefty 292.553 Megabyte, even with the use of extensions you would be bound to fixed width block encoding schemes. That being said MAX_TEXTURE_SIZE is not so much about memory but the addressability of it, there is no way around that(in WebGL), your only option is to use a 2D canvas to split your texture atlas into suitable chunks.

Processing: how can shapes form a text?

i wonder how this is made: http://workshop.chromeexperiments.com/examples/gui/#1--Basic-Usage
Those spots form a text and when clicked , they explode.
How can shapes form a text? Can i achieve this with processing.js?
Thanks.
I guess one way is to have a model for each character. For example, manually create the letter 'A' via shapes. Then for each letter in the message, you would display the character model for the letters
I'm not sure if you want to write this in processing or in javascript. This is a javascript library, so you can use it in a javascript context. To make it on processing and use processingjs to display it on web you got to use processing resources to achieve the same result or manage to pass data from/to javascript and processing. There is this example from processingjs site on connecting your page with a processing sketch. Note that processingjs does not support use of processing libraries, so you can't use any of Precessing typography libraries in this workflow. I think that they could be very handy though...
As how doing this on processing, i think i would go for drawing letters in a not displayed PGraphics, and using a pixel color test on this surface to drive the drawings of the circles. This would allow to alter the text in run time. Like, in pseudo code
PGraphics matteImage = new PGraphics(size, size, render);//
matteImage.background(black);
matteImage.fill(255);
matteImage.text("A", x,y);
matteImage.loadPixels();
for(i; i < matteImage.length;i++)
{
color c = matteImage.pixels[i];
if ( c == white)
{
doDrawEllipses();
}
}
In drawing circles method/class i would add some noise/randomness, exploding handle...

Javascript + ActionScript: How to convert bytearray returned from Actionscript to image in Javascript

I have bytearray returned from ActionScript to Javascript through ExternalInterface call. Now, i have to convert this byteaarray to image in Javascript code. pls help...with any sample code...
Thanks in advance...
I see two possible solutions to this problem, neither of which I have tested so try them out:
HTML5 Canvas
First, using ActionScript convert your byte array to integer array. You will need four values for:
Red
Green
Blue
Alpha
Transfer this to Javascript, either using string representation or plain numbers and then load these numbers into the canvas:
var canvasData = ; // data from actionscript
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var imgData=ctx.createImageData(100,100);
for (var i=0;i<imgData.width*imgData.height*4;i+=4)
{
imgData.data[i+0]=canvasData[i][0]; // red
imgData.data[i+1]=canvasData[i][1]; // green
imgData.data[i+2]=canvasData[i][2]; // blue
imgData.data[i+3]=canvasData[i][3]; // alpha
}
ctx.putImageData(imgData,10,10);
Base64-encoded through CSS
If you don't want to rely on HTML5, use ActionScript to convert the byte array to a base64 string and then insert the image using the following css rule:
background-image: url(data:image/png;base64,__base64_data__);
and replace __base64_data__ with the generated string. This could be done dynamically using JQuery:
$('#img').css("background-image", "url(data:image/png;base64,__base64_data__)");
This also seems to be a much more efficient method than HTML5 Canvas, although actual performance will depend on the image size.
You can store your data in an ArrayBuffer and inject this into a canvas to display as explained here :
https://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays/

Hardcore mathematic and 3d shapes using canvas and javascript

I have an old friend who is a mathematician. He has his own math to compress his formulas, which are incredibly beautiful.
He works in a program called Mathematica, which transforms the formulas for 3D-shapes.
I wonder if it is possible to obtain these figures using Canvas and JavaScript? See attached formula and figure.
I know little of this myself. But I would be delighted if some one could show me an example.
Since you mention Mathematica I'll use it to provide a few more examples for various values of t. I can't help you with canvas though.
This is the Mathematica code:
With[{a = 3, t = 0.7},
RegionPlot3D[
10^-(t x + y)^10 + 10^-(-t x + y)^10 + 10^-(t y + z)^10 +
10^-(-t y + z)^10 + 10^-(t z + x)^10 + 10^-(-t z + x)^10 >
5/2, {x, -a, a}, {y, -a, a}, {z, -a, a}, PlotPoints -> 50,
Boxed -> False, Axes -> None
]]
t=0.2
t=0.4
t=0.7
t=1
It's definitely possible. You can take a look at the javascript-surface-plot library and the working example at http://www.grvisualisation.50webs.com/javascript_surface_plot.html. It produces a 3D model from a mathematical formula that can be panned and rotated as desired.
If you look at the code for the example, there is a setup function that you would need to update to whatever formula you wanted. Just need to convert your math formula into javascript.
I'm not sure what you want to do with these models once you have one, but this library seems to fit your requirements. Doing a search for html canvas 3d plot brought up additional libraries as well.
I would think WebGL would be ideal for this. It's graphics-accelerated in the newest browsers and can render in full 3D.
Perhaps there are libraries out there that can render from functions out of the box, but it's a new technology so you may have to write much of it yourself.
You are really asking two questions:
Are there any 3D canvas libraries: YES K3D
Are there math libraries for javascript: YES discussed here
If you are doing hard core math equations (which it looks like you are), you're better off doing it in something like MatLab/Maple and dumping it in a file then using a canvas 2D library to render the image. I have a lot of 3D data and I do just that. I run a Python script which calculates the points then appends it to an html file (rememer, web pages can't read data from file, so you have to include the data as a part of your html file). Then I load the html file and display generate the image using EaselJS

Categories

Resources