JavaScript/HTML5 Canvas - Load array of images - javascript

I have a folder of images, altogether 32x32 tiles. I am trying to load these images using JavaScript, onto an HTML5 canvas.
Here's what I have:
window.onload = function(){
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var imageObj = new Image();
var tiles = [];
canvas.width = 512;
canvas.height = 352;
for (x = 0; x <= 520; x++) {
imageObj.src = "line_tile/t"+x+".png";
tiles.push(imageObj);
}
var theX;
var theY;
for (x = 0; x <= 10; x++) {
for (y = 0; y <= 15; y++) {
theX = x*32;
theY = y*32;
context.drawImage(tiles[2], theY, theX,32,32);
console.log("Tile X: "+x+" | Tile Y: "+y+" - X Pos: "+theX+" | Y Pos: "+theY);
}
}
};
The problem is that this code only loads up the last tile (in this case tile[520]). In reality I want to load all the tiles. No matter what. How do I properely put a set of images into an array and load it?

Your modifying a single instance of imageObj; so basically you end up with an array all pointing to the same instance, which ends with 520.
try
for (x = 0; x <= 520; x++) {
var imageObj = new Image(); // new instance for each image
imageObj.src = "line_tile/t"+x+".png";
tiles.push(imageObj);
}

Not strictly related to your problem but you might encounter it (with current code)
I'm not sure if you do not need first to see if the Image was actually loaded before adding it to the tiles array.

Related

Error displaying an image from localhost on p5js Canvas

Problem:
I am learning p5.js and I am following a tutorial from Coding Train YouTube channel. Everything was fine until I had to call a library function on an Image object. The problem is that I have instantiated the library in an object p and I'm using it's variables through p object. I don't know why it isn't recognizing the loadPixels() function. In the tutorial, the function works fine.
Error Message:
🌸 p5.js says: There's an error as "loadPixels" could not be called as a function
(on line 17 in help.html [file:///G:/Android/help.html:17:11]).
Verify whether "img" has "loadPixels" in it and check the spelling, letter-casing (Javacript is case-sensitive) and its type.
For more: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_a_function#What_went_wrong
Code:
<!DOCTYPE html>
<head>
<script src='p5/p5.js'></script>
</head>
<body>
<div id='container'></div>
<script>
let sketch = function(p) {
p.setup = function(){
p.createCanvas(56, 56);
img = new Image();
img.src = "scott.jpg";
}
p.draw = function() {
// p.drawingContext.drawImage(img, 0, 0);
p.loadPixels();
img.loadPixels();
for (var x=0; x<p.width; x++) {
for (var y=0; y<p.height; y++) {
// var d = p.dist(x, y, p.width/2, p.height/2);
var loc = x+y+p.width;
// p.pixels[loc] = p.color(d);
p.pixels[loc] = img.pixels[loc];
}
}
}
p.updatePixels();
};
new p5(sketch, 'container');
</script>
</body>
</html>
Edit:
As someone pointed out that the problem is that I'm using Image() which is the default Image class for javascript. I did some changes to my code but now it gives me this error.
Error :-
Uncaught DOMException: The operation is insecure. help.html:18
openWindow file:///G:/Android/help.html:18
onclick file:///G:/Android/help.html:1
Code :-
<!DOCTYPE html>
<head>
<script src='p5/p5.js'></script>
</head>
<body>
<button onclick="openWindow()">click me to open a new window.</button>
<div id='container'></div>
<script>
function openWindow() {
var newWindow = window.open("", "Import Image", "height=56,width=56,status=yes,toolbar=no,menubar=no,location=no");
newWindow.document.write("<canvas id='imagePlaceholder'>Canvas not supported!</canvas>");
var canvas = newWindow.document.getElementById("imagePlaceholder");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// console.log(ctx.getImageData(0, 0, 56, 56).data);
dest = ctx.getImageData(0, 0, 56, 56).data;
}
let sketch = function(p) {
p.setup = function(){
p.createCanvas(56, 56);
img = new Image();
img.src = "scott.jpg";
let dest = p.createImage(56, 56);
console.log(img);
}
p.draw = function() {
// p.drawingContext.drawImage(img, 0, 0);
// p.loadPixels();
img.loadPixels();
for (var x=0; x<p.width; x++) {
for (var y=0; y<p.height; y++) {
// var d = p.dist(x, y, p.width/2, p.height/2);
var loc = x+y+p.width;
// p.pixels[loc] = p.color(d);
p.pixels[loc] = img.pixels[loc];
}
}
}
p.updatePixels();
};
new p5(sketch, 'container');
</script>
</body>
</html>
Because you are specifically trying to load an image from a local computer as opposed to a publicly accessible URL, a file input with user interaction is going to be the only way to do this. This is a deliberate constraint put in place by web browsers to prevent a malicious webpage from illicitly reading data from your local files. However there is a much simpler way to get the image data from the file input onto your p5js canvas. In fact this exact use case can be seen in the documentation for the createFileInput function.
let input;
let img;
function setup() {
input = createFileInput(handleFile);
input.position(0, 0);
}
function draw() {
background(255);
if (img) {
image(img, 0, 0, width, height);
}
}
function handleFile(file) {
if (file.type === 'image') {
img = createImg(file.data, '');
img.hide();
} else {
img = null;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
</body>
</html>
One obvious problem here is that you are using the builtin Image() constructor, which creates an HTMLImageTag (see the MDN Article) instead of creating a p5js p5.Image object (see the p5js Reference). However there are several other issues. In p5js you need to load images in the preload function to ensure they are available when you start drawing (this is an asynchronous operation). You'd have a much easier time drawing images in p5js using the built in image function. If you are going to use pixel arrays, you need to understand the structure of these arrays. They don't store Color objects, they store 4 separate numbers for each color channel (red, green, blue, and alpha). So the indices in the array are not (x + y * width), but ((x + y * width) * 4 + channel) where channel is a number from 0 to 3. Also you need to account for the fact that the canvas may have a pixel density > 1, whereas the image will have a pixel density of 1. I strongly suggest you read all of the documentation for the Image related p5js functions.
let sketch = function(p) {
let img;
p.preload = function() {
img = p.loadImage("https://s3-ap-southeast-1.amazonaws.com/investingnote-production-webbucket/attachments/41645da792aef1c5054c33de240a52e2c32d205e.png");
};
p.setup = function() {
p.createCanvas(200, 200);
};
p.draw = function() {
// this would be a lot simpler way to draw the image:
// p.image(img, 0, 0);
p.loadPixels();
img.loadPixels();
// Handle high pixel density displays. This code effectively scale the image up so that each 1 pixel in the source image is density * density pixels in the display, thus preserving the size of the image but leading to visible pixelation.
let density = p.pixelDensity();
for (var x = 0; x < p.width && x < img.width; x++) {
for (var y = 0; y < p.height && y < img.height; y++) {
// There are 4 values per pixel in the pixels array:
var srcLoc = (x + y * img.width) * 4;
for (var xd = 0; xd < density; xd++) {
for (var yd = 0; yd < density; yd++) {
var destLoc =
(x * density + xd + (y * density + yd) * p.width * density) * 4;
for (var i = 0; i < 4; i++) {
p.pixels[destLoc + i] = img.pixels[srcLoc + i];
}
}
}
}
}
p.updatePixels();
};
};
new p5(sketch, 'container');
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
<div id='container'></div>
</body>
</html>
Here is an alternate version of the snippet that handles pixelDensity differently:
let sketch = function(p) {
let img;
p.preload = function() {
img = p.loadImage("https://s3-ap-southeast-1.amazonaws.com/investingnote-production-webbucket/attachments/41645da792aef1c5054c33de240a52e2c32d205e.png");
};
p.setup = function() {
p.createCanvas(200, 200);
};
p.draw = function() {
// this would be a lot simpler way to draw the image:
// p.image(img, 0, 0);
p.loadPixels();
img.loadPixels();
// Handle high pixel density displays. This code shrinks the image down by mapping one pixel in the source image to 1 / (density ^ 2) actual pixels in the canvas.
let density = p.pixelDensity();
for (var x = 0; x < p.width * density && x < img.width; x++) {
for (var y = 0; y < p.height * density && y < img.height; y++) {
// There are 4 values per pixel in the pixels array:
var srcLoc = (x + y * img.width) * 4;
var destLoc = (x + y * p.width * density) * 4;
for (var i = 0; i < 4; i++) {
p.pixels[destLoc + i] = img.pixels[srcLoc + i];
}
}
}
p.updatePixels();
};
};
new p5(sketch, 'container');
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
<div id='container'></div>
</body>
</html>
I tried a lot of things and almost giveup, but at the end I had to change the code a bit and this worked for me. Although what I got was base64 url as Alice in the comment suggested and I converted it into Uint8ClampedArray. Now if anyone wants a full image or all the pixels of an image then they can follow this link :- https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas . I hope it will be helpful for anyone who wants to make an offline webcanvas based application and don't want to dabble with CORS.
var fileSelector = document.createElement('input');
fileSelector.setAttribute('type', 'file');
fileSelector.setAttribute('accept', 'image/gif, image/jpeg, image/png');
fileSelector.click();
fileSelector.onchange = function(e) {
img = new Image();
var file = e.target.files[0];
var reader = new FileReader();
reader.onloadend = function() {
img.src = reader.result;
}
reader.readAsDataURL(file);
var newWindow = window.open("", "_blank", "height=56,width=56,status=yes,toolbar=no,menubar=no,location=no");
newWindow.document.write("<canvas id='imagePlaceholder'>Canvas not supported!</canvas>");
var canvas = newWindow.document.getElementById("imagePlaceholder");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// console.log(ctx.getImageData(0, 0, 56, 56).data);
var dest = ctx.getImageData(0, 0, img.width, img.height).data;
console.log(dest);
newWindow.close();
}

Animate clipping mask with canvas

Please see attached image. you can see first slide with canvas masking to animate like second slide.
Well I have no clue what language you wanted, so Javascript it is, but the principles are the same for whatever SDK or language you use.
Easy to do. Use requestAnimationFrame to make it smooth. Create a set of shapes that you can animate each from and just draw them to the canvas context as normal using moveTo, lineTo or which ever path function you want. Instead of calling fill or stroke call ctx.clip() then draw the image and it will only be displayed in the clipping areas.
Below is a rather kludged example but it will give you the basics, you can improve it to meet your needs.
var canvas = document.getElementById("canV"); // get the canvas
var ctx = canvas.getContext("2d"); // get the context
var blobs = []; // array to hold blobs
var width = canvas.width;
var height = canvas.height;
var blobPCount = 16; // how bumpy the blobs are
var wobbla = 0; // adds a bit of a wobble
var growSpeed = 2; // grow speed 0.1 very slow 10 mega fast
var cludgeFactor = 0.25; // used to work out when its all done
var cludgeFactorA = 1-cludgeFactor; // its a bit of a cludge hence the name
// a wiki commons image
var imageURL = "https://upload.wikimedia.org/wikipedia/commons/e/ee/Baby_Cuttlefish2_%285589806913%29.jpg";
var image = new Image(); // create the image
image.src = imageURL; // load it
var done = false; // flag is true when done
function addBlob(){ // adds a blob
var b = {};
b.x = Math.random()*width; // find a random pos for it
b.y = Math.random()*height;
b.points = []; // create a set of pointy in a circle that will grow outward
for(var i = 0; i < Math.PI*2;i+= (Math.PI*2)/blobPCount){
var p = {};
// mess up the perfection a little
var dir= (i+((Math.PI*2)/(blobPCount*3))*(Math.random()-0.5))*(1+2/blobPCount);
p.dx = Math.cos(dir); // the delta x and y
p.dy = Math.sin(dir);
p.x = p.dx * 5; // the starting size
p.y = p.dy * 5;
p.dx *= growSpeed; // set the speed
p.dy *= growSpeed;
b.points.push(p); // add the point
}
blobs.push(b); // and the blob
}
function growBlobs(){ // grows the blob
var i;
for(i = 0; i < blobs.length; i++){ // for each blob
var b = blobs[i];
var pc = b.points.length;
for(var j = 0; j < pc; j++){ // grow the points
var p = b.points[j];
p.x += p.dx+p.dx*Math.random()*0.2; // move the point with a liitle random
p.y += p.dy+p.dy*Math.random()*0.2;
}
}
}
// creates the clipping mask
function createClipMask(){
var i;
ctx.beginPath(); // begin a path
wobbla += 0.2; // add some wobbla
var inside = false; // the flag to test done
for(i = 0; i < blobs.length; i++){ // for each blob
var b = blobs[i];
var pc = b.points.length; // get len
for(var j = 0; j < pc-1; j++){ // do eacj point
var p = b.points[j];
var x = b.x+p.x + Math.sin(wobbla+i+j*0.2)*10; // get a point
var y = b.y+p.y + Math.cos(wobbla+i+j*0.2)*10;
if(j === 0){
ctx.moveTo(x,y); // move to the first point
}else{
j ++; // all other points as a second order bexier
p = b.points[j];
var x1 = b.x +p.x*0.75 + Math.sin(wobbla+i+j*0.2)*10; // add the wobble
var y1 = b.y +p.y*0.75 + Math.cos(wobbla+i+j*0.2)*10;
ctx.quadraticCurveTo(x,y,x1,y1); // create tke bezier path
// test if the points are inside the screen by cludge factor
if(!inside && x > width*cludgeFactor && x < width*cludgeFactorA &&
y > height*cludgeFactor && y < height*cludgeFactorA){
inside = true;
}
}
}
ctx.closePath(); // done with this blob close the path
}
// all blobs done so set the clip
ctx.clip();
if(!inside){ // if no points inside the cludge area the we are done
done = true;
}
}
// make a semi random number of blobs
var numberBlobs = Math.ceil(Math.random()^5) +3;
for(var i = 0; i < numberBlobs; i++){
addBlob();
}
// do the update
function update(){
if(done){ // if done draw the image
ctx.drawImage(image,0,0,width,height);
return; // return stops the rendering
}
ctx.fillStyle = "white"; // draw the white
ctx.fillRect(0,0,width,height);
ctx.save(); // save the current ctx state
if(image.complete){ // has the image loaded
growBlobs(); // yes so grow blobs
createClipMask(); // create the clip
ctx.drawImage(image,0,0,width,height); //draw the clipped image
}
ctx.restore(); // dont need the clip anymore so restore the ctx state
window.requestAnimationFrame (update); //request a new anim frame
}
update(); // start it all going
.canC {
width:500px;
height:400px;
}
<canvas class="canC" id="canV" width=500 height=400></canvas>
Also I had to guess what you wanted so hope I guessed right. Any question please do ask.

JavaScript array to PNG? - client side

Is there any way converting a 2d array of hex codes to a png image?
The arrays look like this (only much larger)
[
[
'#FF0000',
'#00FF00'
],
[
'#0000FF',
'#000000'
]
]
From this array, the image should look like this
If the method doesn't work with arrays like this, what type of array will it work with?
If you want to render a PNG client-side, without libraries, you can use the HTML5 Canvas.
Either way, I recommend to stick to a one-dimension array, and store the image’s dimensions. It makes things a lot easier to work with.
var pixels = [ ... ], // your massive array
width = 4, // width in pixels
height = Math.ceil(pixels.length / width),
// Create canvas
canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
imgData = context.createImageData(width, height);
canvas.height = height;
canvas.width = width;
// fill imgData with colors from array
for(var i = 0; i < pixels.length; i++) {
// Convert pixels[i] to RGB
// See http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
imgData[i] = r;
imgData[i + 1] = g;
imgData[i + 2] = b;
imgData[i + 3] = 255; // Alpha channel
}
// put data to context at (0, 0)
context.putImageData(imgData, 0, 0);
// output image
var img = new Image();
img.src = canvas.toDataURL('image/png');
// add image to body (or whatever you want to do)
document.body.appendChild(img);
Alternatively, if you can’t rely on a relatively new feature like this, or simply find this too much work, you can go for Tom’s answer :)
PNGlib looks helpful. You would have to create a loop similar to their example:
var p = new PNGlib(200, 200, 256);
for (var x = 0; x < 2; x++)
for (var y = 0; y < 2; y++)
p.buffer[p.index(x, y)] = p.color(/* your colour */);
document.write('<img src="data:image/png;base64,' + p.getBase64() + '">');
It's difficult to give a more specific example with the information that you've provided, but I think that this is what you're after. You would obviously have to change the x and y limits for different arrays.
You could draw the array of RGB values to a HTML5 canvas object and then get the contents of that canvas using the .toDataURL() canvas method:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example</title>
</head>
<body>
<script>
"use strict";
// Here's the image data we want to draw:
var data = [
["#FF0000", "#00FF00"],
["#FFFF00", "#0000FF"]
];
// First, we need to create a canvas with the same dimensions as the image data:
var canvas = document.createElement("canvas");
canvas.height = data.length;
canvas.width = data[0].length;
//canvas.style.visibility = "hidden";
document.body.appendChild(canvas);
// Now that we have canvas to work with, we need to draw the image data into it:
var ctx = canvas.getContext("2d");
for (var y = 0; y < data.length; ++y) {
for (var x = 0; x < data[y].length; ++x) {
ctx.fillStyle = data[y][x];
ctx.fillRect(x, y, 1, 1);
}
}
// Finally, we get the image data using the .toDataURL() canvas method:
console.log(canvas.toDataURL("image/png"));
</script>
</body>
</html>
Solution for the image stored in 2 dimensional array, with RGB colors
as an answer to another question
var img=[[[0,0,0],[0,0,0],[0,0,0],[255,0,0],[0,0,0]],
[[0,0,0],[0,0,255],[0,0,0],[0,0,0],[255,0,0]],
[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[255,0,0]],
[[0,0,0],[0,0,255],[0,0,0],[0,0,0],[255,0,0]],
[[0,0,0],[0,0,0],[0,0,0],[255,0,0],[0,0,0]]];
var pixelSize = 20;
var c = document.createElement("canvas");
c.height = img[0].length * pixelSize;
c.width = img.length * pixelSize;
document.body.appendChild(c);
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
for (var i = 0; i < img.length; i++) {
for (var j = 0; j < img[0].length; j++) {
ctx.fillStyle = "rgb("+img[i][j][0]+","+img[i][j][1]+","+img[i][j][2]+")";
ctx.fillRect(i*pixelSize, j*pixelSize, pixelSize, pixelSize);
}
}
console.log(c.toDataURL("image/png"));
var png = document.createElement("img");
png.src = c.toDataURL("image/png");
c.remove();
document.body.appendChild(png);
Or use an updated version of pnglib:
https://github.com/IjzerenHein/pnglib-es6

Render .pdf to single Canvas using pdf.js and ImageData

I am trying to read an entire .pdf Document using PDF.js and then render all the pages on a single canvas.
My idea: render each page onto a canvas and get the ImageData (context.getImageData()), clear the canvas do the next page. I store all the ImageDatas in an array and once all pages are in there I want to put all the ImageDatas from the array onto a single canvas.
var pdf = null;
PDFJS.disableWorker = true;
var pages = new Array();
//Prepare some things
var canvas = document.getElementById('cv');
var context = canvas.getContext('2d');
var scale = 1.5;
PDFJS.getDocument(url).then(function getPdfHelloWorld(_pdf) {
pdf = _pdf;
//Render all the pages on a single canvas
for(var i = 1; i <= pdf.numPages; i ++){
pdf.getPage(i).then(function getPage(page){
var viewport = page.getViewport(scale);
canvas.width = viewport.width;
canvas.height = viewport.height;
page.render({canvasContext: context, viewport: viewport});
pages[i-1] = context.getImageData(0, 0, canvas.width, canvas.height);
context.clearRect(0, 0, canvas.width, canvas.height);
p.Out("pre-rendered page " + i);
});
}
//Now we have all 'dem Pages in "pages" and need to render 'em out
canvas.height = 0;
var start = 0;
for(var i = 0; i < pages.length; i++){
if(canvas.width < pages[i].width) canvas.width = pages[i].width;
canvas.height = canvas.height + pages[i].height;
context.putImageData(pages[i], 0, start);
start += pages[i].height;
}
});
So from the way I understnad thing this should work, right?
When I run this I end up with the canvas that is big enought to contain all the pages of the pdf but doesn't show the pdf...
Thank you for helping.
The PDF operations are asynchronous at all stages. This means you also need to catch the promise at the last render as well. If you not catch it you will only get a blank canvas as the rendering isn't finished before the loop continues to the next page.
Tip: I would also recommend that you use something else than getImageData as this will store uncompressed bitmap, for example the data-uri instead which is compressed data.
Here is a slightly different approach eliminating the for-loop and uses the promises better for this purpose:
LIVE FIDDLE
var canvas = document.createElement('canvas'), // single off-screen canvas
ctx = canvas.getContext('2d'), // to render to
pages = [],
currentPage = 1,
url = 'path/to/document.pdf'; // specify a valid url
PDFJS.getDocument(url).then(iterate); // load PDF document
/* To avoid too many levels, which easily happen when using chained promises,
the function is separated and just referenced in the first promise callback
*/
function iterate(pdf) {
// init parsing of first page
if (currentPage <= pdf.numPages) getPage();
// main entry point/function for loop
function getPage() {
// when promise is returned do as usual
pdf.getPage(currentPage).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
// now, tap into the returned promise from render:
page.render(renderContext).then(function() {
// store compressed image data in array
pages.push(canvas.toDataURL());
if (currentPage < pdf.numPages) {
currentPage++;
getPage(); // get next page
}
else {
done(); // call done() when all pages are parsed
}
});
});
}
}
When you then need to retrieve a page you simply create an image element and set the data-uri as source:
function drawPage(index, callback) {
var img = new Image;
img.onload = function() {
/* this will draw the image loaded onto canvas at position 0,0
at the optional width and height of the canvas.
'this' is current image loaded
*/
ctx.drawImage(this, 0, 0, ctx.canvas.width, ctx.canvas.height);
callback(); // invoke callback when we're done
}
img.src = pages[index]; // start loading the data-uri as source
}
Due to the image loading it will be asynchronous in nature as well which is why we need the callback. If you don't want the asynchronous nature then you could also do this step (creating and setting the image element) in the render promise above storing image elements instead of data-uris.
Hope this helps!
I can’t speak to the part of your code that renders the pdf into a canvas, but I do see some problems.
Every resetting canvas.width or canvas.height automatically clears the canvas contents. So in the top section, your clearRect is not needed because the canvas is cleared by canvas.width prior to your every page.render.
More importantly, in the bottom section, all your previous pdf drawings are cleared by every canvas resizing (oops!).
getImageData() gets an array where each pixel is represented by 4 consecutive elements of that array (red then green then blue then alpha). Since getImageData() is an array, so it doesn’t have a pages[i].width or pages[i].height—it only has a pages[i].length. That array length cannot be used to determine widths or heights.
So to get you started, I would start by changing your code to this (very, very untested!):
var pdf = null;
PDFJS.disableWorker = true;
var pages = new Array();
//Prepare some things
var canvas = document.getElementById('cv');
var context = canvas.getContext('2d');
var scale = 1.5;
var canvasWidth=0;
var canvasHeight=0;
var pageStarts=new Array();
pageStarts[0]=0;
PDFJS.getDocument(url).then(function getPdfHelloWorld(_pdf) {
pdf = _pdf;
//Render all the pages on a single canvas
for(var i = 1; i <= pdf.numPages; i ++){
pdf.getPage(i).then(function getPage(page){
var viewport = page.getViewport(scale);
// changing canvas.width and/or canvas.height auto-clears the canvas
canvas.width = viewport.width;
canvas.height = viewport.height;
page.render({canvasContext: context, viewport: viewport});
pages[i-1] = context.getImageData(0, 0, canvas.width, canvas.height);
// calculate the width of the final display canvas
if(canvas.width>maxCanvasWidth){
maxCanvasWidth=canvas.width;
}
// calculate the accumulated with of the final display canvas
canvasHeight+=canvas.height;
// save the "Y" starting position of this pages[i]
pageStarts[i]=pageStarts[i-1]+canvas.height;
p.Out("pre-rendered page " + i);
});
}
canvas.width=canvasWidth;
canvas.height = canvasHeight; // this auto-clears all canvas contents
for(var i = 0; i < pages.length; i++){
context.putImageData(pages[i], 0, pageStarts[i]);
}
});
Alternatively, here’s a more traditional way of accomplishing your task:
Use a single “display” canvas and allow the user to “page through” each desired page.
Since you already start by drawing each page into a canvas, why not keep a separate, hidden canvas for each page. Then when the user wants to see page#6, you just copy the hidden canvas#6 onto your display canvas.
The Mozilla devs use this approach in their pdfJS demo here: http://mozilla.github.com/pdf.js/web/viewer.html
You can check out the code for the viewer here: http://mozilla.github.com/pdf.js/web/viewer.js
You can pass the number page to the promises , get that page canvas data and render in the right order on canvas
var renderPageFactory = function (pdfDoc, num) {
return function () {
var localCanvas = document.createElement('canvas');
///return pdfDoc.getPage(num).then(renderPage);
return pdfDoc.getPage(num).then((page) => {
renderPage(page, localCanvas, num);
});
};
};
var renderPages = function (pdfDoc) {
var renderedPage = $q.resolve();
for (var num = 1; num <= pdfDoc.numPages; num++) {
// Wait for the last page t render, then render the next
renderedPage = renderedPage.then(renderPageFactory(pdfDoc, num));
}
};
renderPages(pdf);
Complete example
function renderPDF(url, canvas) {
var pdf = null;
PDFJS.disableWorker = true;
var pages = new Array();
var context = canvas.getContext('2d');
var scale = 1;
var canvasWidth = 256;
var canvasHeight = 0;
var pageStarts = new Array();
pageStarts[0] = 0;
var k = 0;
function finishPage(localCanvas, num) {
var ctx = localCanvas.getContext('2d');
pages[num] = ctx.getImageData(0, 0, localCanvas.width, localCanvas.height);
// calculate the accumulated with of the final display canvas
canvasHeight += localCanvas.height;
// save the "Y" starting position of this pages[i]
pageStarts[num] = pageStarts[num -1] + localCanvas.height;
if (k + 1 >= pdf.numPages) {
canvas.width = canvasWidth;
canvas.height = canvasHeight; // this auto-clears all canvas contents
for (var i = 0; i < pages.length; i++) {
context.putImageData(pages[i+1], 0, pageStarts[i]);
}
var img = canvas.toDataURL("image/png");
$scope.printPOS(img);
}
k++;
}
function renderPage(page, localCanvas, num) {
var ctx = localCanvas.getContext('2d');
var viewport = page.getViewport(scale);
// var viewport = page.getViewport(canvas.width / page.getViewport(1.0).width);
// changing canvas.width and/or canvas.height auto-clears the canvas
localCanvas.width = viewport.width;
/// viewport.width = canvas.width;
localCanvas.height = viewport.height;
var renderTask = page.render({canvasContext: ctx, viewport: viewport});
renderTask.then(() => {
finishPage(localCanvas, num);
});
}
PDFJS.getDocument(url).then(function getPdfHelloWorld(_pdf) {
pdf = _pdf;
var renderPageFactory = function (pdfDoc, num) {
return function () {
var localCanvas = document.createElement('canvas');
///return pdfDoc.getPage(num).then(renderPage);
return pdfDoc.getPage(num).then((page) => {
renderPage(page, localCanvas, num);
});
};
};
var renderPages = function (pdfDoc) {
var renderedPage = $q.resolve();
for (var num = 1; num <= pdfDoc.numPages; num++) {
// Wait for the last page t render, then render the next
renderedPage = renderedPage.then(renderPageFactory(pdfDoc, num));
}
};
renderPages(pdf);
});
}

JavaScript - Splitting a tileset image to be stored in 2D Image() array

Let's say I have this image:
and I have this 2D array tiles[] .. and using Image() function... how can do I use the (what I assume be best way?) for loop to add each tile into the array so tile[0] through however many there are is read and used as Image() objects later to be painted on HTML5 canvas?
I would..
Figure out how many tiles wide and high this image is
Draw the image to a canvas in memory, and use the context to get image data.
Loop through and subimage each tile, storing in an array.
Assuming:
imageWidth, imageHeight, tileWidth, tileHeight
All describe what their names suggest, and:
EDIT: Added image load as per comment, fixed wrongly name ImageWidth and ImageHeight to imageWidth and imageHeight
EDIT: Performing code inside imageObj.onload as the image is drawn here, drawImage() from origin (0,0)
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var imageObj = new Image();
imageObj.src = "tilesetImageLocationHere";
imageObj.onload = function() {
ctx.drawImage(imageObj, 0, 0);
Then, split up your image into a list of tile data..
var tilesX = imageWidth / tileWidth;
var tilesY = imageHeight / tileHeight;
var totalTiles = tilesX * tilesY;
var tileData = new Array();
for(var i=0; i<tilesY; i++)
{
for(var j=0; j<tilesX; j++)
{
// Store the image data of each tile in the array.
tileData.push(ctx.getImageData(j*tileWidth, i*tileHeight, tileWidth, tileHeight));
}
}
//From here you should be able to draw your images back into a canvas like so:
ctx.putImageData(tileData[0], x, y);
}
ok I did this on my localhost: it should give you a base at least. I think you should be able to use this straight outta the box but you might have to make a few calculation adjustments for it depending on what you want. I just don't think it's neccessary to spend thee time to perfect it to your example:
You'll obviously have to point the image to your own image.
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
var canvas = document.getElementById('fakeCanvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.src = '/theImage.png';
var tiles = [];
var imageTileNumWidth = 23;
var imageTileNumHeight = 21;
img.onload = function(){
var imageWidth = img.width;
var imageHeight = img.height;
sndCanvasWidth = imageWidth/imageTileNumWidth;
sndCanvasHeight = imageHeight/imageTileNumHeight;
canvas.width = imageWidth;
canvas.height = imageHeight;
ctx.drawImage(img,0,0,imageWidth,imageHeight);
var sndCanvas = document.getElementById('drawCanvas');
sndCanvas.width=sndCanvasWidth;
sndCanvas.height=sndCanvasHeight;
var i=0;
var j=0;
var t=0;
for(i=0;i<imageWidth;i+=sndCanvasWidth){
for(j=0;j<imageHeight;j+=sndCanvasHeight){
var myImageData = ctx.getImageData(j,i,sndCanvasWidth,sndCanvasHeight);
var ctx2 = sndCanvas.getContext("2d");
ctx2.putImageData(myImageData,0,0);
tiles.push(myImageData);
}
}
};
});
</script>
</head>
<body>
<canvas id="fakeCanvas"></canvas>
<canvas id="drawCanvas"></canvas>
</body>
</html>

Categories

Resources