Canvas image zooming using Nearest Neighbor Algorithm - javascript

I'm using nearest neighbor algorithm to zoom the image on canvas. But, when I move the scaling bar higher, the image have white line that create a square array
Original Image
After I move the scale bar
The zoom is work but the problem is only the white lines.
For the source code I will provide in bottom
1.html
<!DOCTYPE HTML>
<html>
<head>
<title>Prototype PC</title>
</head>
<body>
<canvas id='canvas1'></canvas>
<hr>
<button id='read'>READ IMAGE</button>
<hr>
Scale <input type='range' value='1' min='1' max='5' step='0.25' id='scale'>
<br><button id='default2'>Default Scalling</button>
<hr/>
</body>
<style>
body{
background : rgba(255,255,255,1);
}
</style>
<script src='imagine.js'></script>
<script>
var canvas = document.getElementById('canvas1')
var obj = new pc(canvas)
obj.image2canvas("565043_553561101348179_1714194038_a.jpg")
var tes = new Array()
document.getElementById('read').addEventListener('click',function(){
tes = obj.image2read()
})
document.getElementById('scale').addEventListener('change',function(){
var scaleval = this.value
var xpos = 0
var ypos = 0
var xnow = 0
var ynow = 0
var objW = obj.width
var objH = obj.height
tesbackup = new Array()
for(var c=0; c<tes.length; c++){
temp = new Array()
for(var d=0; d<4; d++){
temp.push(255)
}
tesbackup.push(temp)
}
//end of copy
for(var i=0; i<tes.length; i++){
xpos = obj.i2x(i)
ypos = obj.i2y(i)
xnow = Math.round(xpos) * scaleval)
ynow = Math.round(ypos) * scaleval)
if (xnow < objW && ynow < objH) {
for (var j=0; j<scaleval; j++) {
for (var k=0; k<scaleval; k++) {
var idxnow = obj.xy2i(xnow,ynow)
tesbackup[idxnow][0] = tes[i][0]
tesbackup[idxnow][1] = tes[i][1]
tesbackup[idxnow][2] = tes[i][2]
}
}
}
}
obj.array2canvas(tesbackup)
})
</script>
and, for imagine.js
function info(text){
console.info(text)
}
function pc(canvas){
this.canvas = canvas
this.context = this.canvas.getContext('2d')
this.width = 0
this.height = 0
this.imgsrc = ""
this.image2read = function(){
this.originalLakeImageData = this.context.getImageData(0,0, this.width, this.height)
this.resultArr = new Array()
this.tempArr = new Array()
this.tempCount = 0
for(var i=0; i<this.originalLakeImageData.data.length; i++){
this.tempCount++
this.tempArr.push(this.originalLakeImageData.data[i])
if(this.tempCount == 4){
this.resultArr.push(this.tempArr)
this.tempArr = []
this.tempCount = 0
}
}
info('image2read Success ('+this.imgsrc+') : '+this.width+'x'+this.height)
return this.resultArr
}
this.image2canvas = function(imgsrc){
var imageObj = new Image()
var parent = this
imageObj.onload = function() {
parent.canvas.width = imageObj.width
parent.canvas.height = imageObj.height
parent.context.drawImage(imageObj, 0, 0)
parent.width = imageObj.width
parent.height = imageObj.height
info('image2canvas Success ('+imgsrc+')')
}
imageObj.src = imgsrc
this.imgsrc = imgsrc
}
this.array2canvas = function(arr){
this.imageData = this.context.getImageData(0,0, this.width, this.height)
if(this.imageData.data.length != arr.length*4) {
return false
}
for(var i = 0; i < arr.length; i++){
this.imageData.data[(i*4)] = arr[i][0]
this.imageData.data[(i*4)+1] = arr[i][1]
this.imageData.data[(i*4)+2] = arr[i][2]
this.imageData.data[(i*4)+3] = arr[i][3]
}
this.context.clearRect(0, 0, this.width, this.height)
this.context.putImageData(this.imageData, 0, 0)
info('Array2Canvas Success ('+this.imgsrc+')')
}
this.i2x = function(i){
return (i % this.width)
}
this.i2y = function(i){
return ((i - (i % this.width))/ this.width)
}
this.xy2i = function(x,y){
return (y * this.width) + (x)
}
}
Thanks in advance for a solution of this problem

Rounding out pixels
Nearest pixel will result in some zoomed pixels being larger than otheres
It is a problem with the value of scaleval. It has a step of 0.25 and when you calculate each zoomed pixels address you use (and I am guessing as your code has syntax errors) Math.round(xpos * scaleval) but then you draw the pixel using only the fractional size eg 2.75 not the integer size eg 3.0
The size of each pixel is var xSize = Math.round((xpos + 1) * scaleval)-Math.round(xpos * scaleval) same for y. That way when the pixel zoom is not an integer value every so many zoomed pixels will be one pixel wider and higher.
The following is a fix of your code but as you had a number of syntax errors and bugs I have had to guess some of your intentions.
xpos = obj.i2x(i)
ypos = obj.i2y(i)
xnow = Math.round(xpos * scaleval)
ynow = Math.round(ypos * scaleval)
// pixel width and height
var pw = Math.round((xpos + 1) * scaleval) - xnow;
var ph = Math.round((ypos + 1) * scaleval) - ynow;
if (xnow < objW && ynow < objH) {
for (var y = 0; y < ph; y++) {
for (var x =0; x < pw; x++) {
var idxnow = obj.xy2i(xnow + x, ynow + y)
tesbackup[idxnow][0] = tes[i][0]
tesbackup[idxnow][1] = tes[i][1]
tesbackup[idxnow][2] = tes[i][2]
}
}
}
}
But you are not really doing a nearest neighbor algorithm. For that you iterate each of the destination pixels finding the nearest pixel and using its colour. That allows you to easily apply a transform to the zoom but still get every pixel and not skip pixels due to rounding errors.
Nearest neighbor
Example of using nearest neighbor lookup for a scale rotated and translated image
var scaleFac = 2.3; // scale 1> zoom in
var panX = 10; // scaled image pan
var panY = 10;
var ang = 1;
var w = ctx.canvas.width; // source image
var h = ctx.canvas.height;
var wd = ctx1.canvas.width; // destination image
var hd = ctx1.canvas.height;
// use 32bit ints as we are not interested in the channels
var src = ctx.getImageData(0, 0, w, h);
var data = new Uint32Array(src.data.buffer);// source
var dest = ctx1.createImageData(wd, hd);
var zoomData = new Uint32Array(dest.data.buffer);// destination
var xdx = Math.cos(ang) * scaleFac; // xAxis vector x
var xdy = Math.sin(ang) * scaleFac; // xAxis vector y
var ind = 0;
var xx,yy;
for(var y = 0; y < hd; y ++){
for(var x = 0; x < wd; x ++){
// transform point
xx = (x * xdx - y * xdy + panX);
yy = (x * xdy + y * xdx + panY);
// is the lookup pixel in bounds
if(xx >= 0 && xx < w && yy >= 0 && yy < h){
// use the nearest pixel to set the new pixel
zoomData[ind++] = data[(xx | 0) + (yy | 0) * w]; // set the pixel
}else{
zoomData[ind++] = 0; // pixels outside bound are transparent
}
}
}
ctx1.putImageData(dest, 0, 0); // put the pixels onto the destination canvas

Related

print image using ESC POS commands Javascript

I'm trying print image date using ESC POS commands in Javascript, but until now just crap is printing out.
Command ESC *
I'm using Javascript, trying print direct to bluethoo a image in bit64string.
I tried this example with ESC #, ESC * command in Javscript
It only prints me characters, but not the image
Thank in advance
// let image = context.getImageData(0, 0, width, height)
getImageData(image, Width, Height){
var dimensions = Width * Height
var dots = new Uint8Array(dimensions)
var index = 0
var threshold = 127;
for (var y = 0; y < Height; y++)
{
for (var x = 0; x < Width; x++)
{
var color = this.getPixelI(image, x, y)
//let luminance
let luminance = (0.2126* color[0] + 0.7152 * color[1] + 0.0722 * color[2]);
dots[index] = luminance < threshold
index++;
}
}
return dots
}
getPixelI(imgData, x, y) {
var i = y * (imgData.width * 4) + x * 4;
var d = imgData.data;
return [d[i],d[i+1],d[i+2], d[i+3]] // returns array [R,G,B,A]
}

Convolution matrix algorithm

Could someone help me understand how the convolution matrix works to get effects on images?I know that the value of a pixel(with each channel, for red, green and blue) is obtained as a sum of the original data matrix multiplied with the convolution matrix, but I do not get the algorithm.I hava a example taken from a website that makes a blur efect on an image.Could you help me with some explaining on the steps made?
Filters.tmpCanvas = document.createElement('canvas');
Filters.tmpCtx = Filters.tmpCanvas.getContext('2d');
Filters.createImageData = function(w,h) {
return this.tmpCtx.createImageData(w,h);
};
Filters.convolute = function(pixels, weights, opaque) {
var side = Math.round(Math.sqrt(weights.length));
var halfSide = Math.floor(side/2);
var src = pixels.data;
var sw = pixels.width;
var sh = pixels.height;
// pad output by the convolution matrix
var w = sw;
var h = sh;
var output = Filters.createImageData(w, h);
var dst = output.data;
// go through the destination image pixels
var alphaFac = opaque ? 1 : 0;
for (var y=0; y<h; y++) {
for (var x=0; x<w; x++) {
var sy = y;
var sx = x;
var dstOff = (y*w+x)*4;
// calculate the weighed sum of the source image pixels that
// fall under the convolution matrix
var r=0, g=0, b=0, a=0;
for (var cy=0; cy<side; cy++) {
for (var cx=0; cx<side; cx++) {
var scy = sy + cy - halfSide;
var scx = sx + cx - halfSide;
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
var srcOff = (scy*sw+scx)*4;
var wt = weights[cy*side+cx];
r += src[srcOff] * wt;
g += src[srcOff+1] * wt;
b += src[srcOff+2] * wt;
a += src[srcOff+3] * wt;
}
}
}
dst[dstOff] = r;
dst[dstOff+1] = g;
dst[dstOff+2] = b;
dst[dstOff+3] = a + alphaFac*(255-a);
}
}
return output;
};

How to divide image in tiles?

I have to achieve the following task:
divides the image into tiles, computes the average color of each tile,
fetches a tile from the server for that color, and composites the
results into a photomosaic of the original image.
What would be the best strategy? the first solution coming to my mind is using canvas.
A simple way to get pixel data and finding the means of tiles. The code will need more checks for images that do not have dimensions that can be divided by the number of tiles.
var image = new Image();
image.src = ??? // the URL if the image is not from your domain you will have to move it to your server first
// wait for image to load
image.onload = function(){
// create a canvas
var canvas = document.createElement("canvas");
//set its size to match the image
canvas.width = this.width;
canvas.height = this.height;
var ctx = canvas.getContext("2d"); // get the 2d interface
// draw the image on the canvas
ctx.drawImage(this,0,0);
// get the tile size
var tileSizeX = Math.floor(this.width / 10);
var tileSizeY = Math.floor(this.height / 10);
var x,y;
// array to hold tile colours
var tileColours = [];
// for each tile
for(y = 0; y < this.height; y += tileSizeY){
for(x = 0; x < this.width; x += tileSizeX){
// get the pixel data
var imgData = ctx.getImageData(x,y,tileSizeX,tileSizeY);
var r,g,b,ind;
var i = tileSizeY * tileSizeX; // get pixel count
ind = r = g = b = 0;
// for each pixel (rgba 8 bits each)
while(i > 0){
// sum the channels
r += imgData.data[ind++];
g += imgData.data[ind++];
b += imgData.data[ind++];
ind ++;
i --;
}
i = ind /4; // get the count again
// calculate channel means
r /= i;
g /= i;
b /= i;
//store the tile coords and colour
tileColours[tileColours.length] = {
rgb : [r,g,b],
x : x,
y : y,
}
}
// all done now fetch the images for the found tiles.
}
I created a solution for this (I am not getting the tile images from back end)
// first function call to create photomosaic
function photomosaic(image) {
// Dimensions of each tile
var tileWidth = TILE_WIDTH;
var tileHeight = TILE_HEIGHT;
//creating the canvas for photomosaic
var canvas = document.createElement('canvas');
var context = canvas.getContext("2d");
canvas.height = image.height;
canvas.width = image.width;
var imageData = context.getImageData(0, 0, image.width, image.height);
var pixels = imageData.data;
// Number of mosaic tiles
var numTileRows = image.width / tileWidth;
var numTileCols = image.height / tileHeight;
//canvas copy of image
var imageCanvas = document.createElement('canvas');
var imageCanvasContext = canvas.getContext('2d');
imageCanvas.height = image.height;
imageCanvas.width = image.width;
imageCanvasContext.drawImage(image, 0, 0);
//function for finding the average color
function averageColor(row, column) {
var blockSize = 1, // we can set how many pixels to skip
data, width, height,
i = -4,
length,
rgb = {
r: 0,
g: 0,
b: 0
},
count = 0;
try {
data = imageCanvasContext.getImageData(column * TILE_WIDTH, row * TILE_HEIGHT, TILE_HEIGHT, TILE_WIDTH);
} catch (e) {
alert('Not happening this time!');
return rgb;
}
length = data.data.length;
while ((i += blockSize * 4) < length) {
++count;
rgb.r += data.data[i];
rgb.g += data.data[i + 1];
rgb.b += data.data[i + 2];
}
// ~~ used to floor values
rgb.r = ~~(rgb.r / count);
rgb.g = ~~(rgb.g / count);
rgb.b = ~~(rgb.b / count);
return rgb;
}
// Loop through each tile
for (var r = 0; r < numTileRows; r++) {
for (var c = 0; c < numTileCols; c++) {
// Set the pixel values for each tile
var rgb = averageColor(r, c)
var red = rgb.r;
var green = rgb.g;
var blue = rgb.b;
// Loop through each tile pixel
for (var tr = 0; tr < tileHeight; tr++) {
for (var tc = 0; tc < tileWidth; tc++) {
// Calculate the true position of the tile pixel
var trueRow = (r * tileHeight) + tr;
var trueCol = (c * tileWidth) + tc;
// Calculate the position of the current pixel in the array
var pos = (trueRow * (imageData.width * 4)) + (trueCol * 4);
// Assign the colour to each pixel
pixels[pos + 0] = red;
pixels[pos + 1] = green;
pixels[pos + 2] = blue;
pixels[pos + 3] = 255;
};
};
};
};
// Draw image data to the canvas
context.putImageData(imageData, 0, 0);
return canvas;
}
function create() {
var image = document.getElementById('image');
var canvas = photomosaic(image);
document.getElementById("output").appendChild(canvas);
};
DEMO:https://jsfiddle.net/gurinderiitr/sx735L5n/
Try using the JIMP javascript library to read the pixel color and use invert, normalize or similar property for modifying the image.
Have a look on the jimp library
https://github.com/oliver-moran/jimp

How to make blur effect particles in javascript

Hi I want to make a blur effect particle like this:
Can I use shadowBlur and shadowOffsetX/shadowOffsetY to do this? The actual shine will glow and fade a little bit repeatedly, so if I have to write some kind of animation how can I achieve this?
I have tried this code (jsfiddle example) but it doesn't look like the effect. So I wonder how to blur and glow the particle at the same time?
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const ra = window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.oRequestAnimationFrame
|| window.msRequestAnimationFrame
|| function(callback) {
window.setTimeout(callback, 1000 / 60);
};
class Particle {
constructor(options) {
this.ctx = options.context;
this.x = options.x;
this.y = options.y;
this.radius = options.radius;
this.lightSize = this.radius;
this.color = options.color;
this.lightDirection = true;
}
glow() {
const lightSpeed = 0.5;
this.lightSize += this.lightDirection ? lightSpeed : -lightSpeed;
if (this.lightSize > this.radius || this.lightSize < this.radius) {
this.lightDirection = !this.lightDirection;
}
}
render() {
this.ctx.clearRect(0, 0, canvas.width, canvas.height);
this.glow();
this.ctx.globalAlpha = 0.5;
this.ctx.fillStyle = this.color;
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.lightSize,
0, Math.PI * 2
);
this.ctx.fill();
this.ctx.globalAlpha = 0.62;
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.radius * 0.7, 0, Math.PI * 2);
this.ctx.shadowColor = this.color;
this.ctx.shadowBlur = 6;
this.ctx.shadowOffsetX = 0;
this.ctx.shadowOffsetY = 0;
this.ctx.fill();
}
}
var particle = new Particle({
context: ctx,
x: 60,
y: 80,
radius: 12,
color: '#4d88ff'
});
function run() {
particle.render();
ra(run);
}
run();
<canvas id='canvas'></canvas>
There are several ways to do this. For a particle system my option is to pre render the blur using a blur filter. A common filter is the convolution filter. It uses a small array to determine the amount neighboring pixels contribute to each pixel of the image. You are best to look up convolution functions to understand it.
Wiki Convolution and Wiki Gaussian blur for more info.
I am not much of a fan of the standard Gaussian blur or the convolution filter used so in the demo snippet below you can find my version that I think creates a much better blur. The convolution blur filter is procedurally created and is in the imageTools object.
To use create a filter pass an object with properties size the blur amount in pixels and power is the strength. Lower powers is less spread on the blur.
// image must be loaded or created
var blurFilter = imageTools.createBlurConvolutionArray({size:17,power:1}); // size must be greater than 2 and must be odd eg 3,5,7,9...
// apply the convolution filter on the image. The returned image may be a new
//image if the input image does not have a ctx property pointing to a 2d canvas context
image = imageTools.applyConvolutionFilter(image,blurFilter);
In the demo I create a image, draw a circle on it, copy it and pad it so that there is room for the blur. Then create a blur filter and apply it to the image.
When I render the particles I first draw all the unblurred images, then draw the blurred copies with the ctx.globalCompositeOperation = "screen"; so that they have a shine. To vary the amount of shine I use the ctx.globalAlpha to vary the intensity of the rendered blurred image. To improve the FX I have drawn the blur image twice, once with oscillating scale and next at fixed scale and alpha.
The demo is simple, image tools can be found at the top. Then there is some stuff to setup the canvas and handle resize event. Then there is the code that creates the images, and apply the filters. Then starts the render adds some particles and renders everything.
Look in the function drawParticles for how I draw everything.
imageTools has all the image functions you will need. The imageTools.applyConvolutionFilter will apply any filter (sharpen, outline, and many more) you just need to create the appropriate filter. The apply uses the photon count colour model so gives a very high quality result especially for blurs type effects. (though for sharpen you may want to get in and change the squaring of the RGB values, I personally like it other do not)
The blur filter is not fast so if you apply it to larger images It would be best that you break it up in so you do not block the page execution.
A cheap way to get a blur is to copy the image to blur to a smaller version of itself, eg 1/4 then render it scaled back to normal size, the canvas will apply bilinear filtering on the image give a blur effect. Not the best quality but for most situations it is indistinguishable from the more sophisticated blur that I have presented.
UPDATE
Change the code so that the particles have a bit of a 3dFX to show that the blur can work up to larger scales. The blue particles are 32 by 32 image and the blur is 9 pixels with the blur image being 50by 50 pixels.
var imageTools = (function () {
var tools = {
canvas : function (width, height) { // create a blank image (canvas)
var c = document.createElement("canvas");
c.width = width;
c.height = height;
return c;
},
createImage : function (width, height) {
var image = this.canvas(width, height);
image.ctx = image.getContext("2d");
return image;
},
image2Canvas : function (img) {
var image = this.canvas(img.width, img.height);
image.ctx = image.getContext("2d");
image.drawImage(img, 0, 0);
return image;
},
padImage : function(img,amount){
var image = this.canvas(img.width + amount * 2, img.height + amount * 2);
image.ctx = image.getContext("2d");
image.ctx.drawImage(img, amount, amount);
return image;
},
getImageData : function (image) {
return (image.ctx || (this.image2Canvas(image).ctx)).getImageData(0, 0, image.width, image.height);
},
putImageData : function (image, imgData){
(image.ctx || (this.image2Canvas(image).ctx)).putImageData(imgData,0, 0);
return image;
},
createBlurConvolutionArray : function(options){
var i, j, d; // misc vars
var filterArray = []; // the array to create
var size = options.size === undefined ? 3: options.size; // array size
var center = Math.floor(size / 2); // center of array
// the power ? needs descriptive UI options
var power = options.power === undefined ? 1: options.power;
// dist to corner
var maxDist = Math.sqrt(center * center + center * center);
var dist = 0; // distance sum
var sum = 0; // weight sum
var centerWeight; // center calculated weight
var totalDistance; // calculated total distance from center
// first pass get the total distance
for(i = 0; i < size; i++){
for(j = 0; j < size; j++){
d = (maxDist-Math.sqrt((center-i)*(center-i)+(center-j)*(center-j)));
d = Math.pow(d,power)
dist += d;
}
}
totalDistance = dist; // total distance to all points;
// second pass get the total weight of all but center
for(i = 0; i < size; i++){
for(j = 0; j < size; j++){
d = (maxDist-Math.sqrt((center-i)*(center-i)+(center-j)*(center-j)));
d = Math.pow(d,power)
d = d/totalDistance;
sum += d;
}
}
var scale = 1/sum;
sum = 0; // used to check
for(i = 0; i < size; i++){
for(j = 0; j < size; j++){
d = (maxDist-Math.sqrt((center-i)*(center-i)+(center-j)*(center-j)));
d = Math.pow(d,power)
d = d/totalDistance;
filterArray.push(d*scale);
}
}
return filterArray;
},
applyConvolutionFilter : function(image,filter){
imageData = this.getImageData(image);
imageDataResult = this.getImageData(image);
var w = imageData.width;
var h = imageData.height;
var data = imageData.data;
var data1 = imageDataResult.data;
var side = Math.round(Math.sqrt(filter.length));
var halfSide = Math.floor(side/2);
var r,g,b,a,c;
for(var y = 0; y < h; y++){
for(var x = 0; x < w; x++){
var ind = y*4*w+x*4;
r = 0;
g = 0;
b = 0;
a = 0;
for (var cy=0; cy<side; cy++) {
for (var cx=0; cx<side; cx++) {
var scy = y + cy - halfSide;
var scx = x + cx - halfSide;
if (scy >= 0 && scy < h && scx >= 0 && scx < w) {
var srcOff = (scy*w+scx)*4;
var wt = filter[cy*side+cx];
r += data[srcOff+0] * data[srcOff+0] * wt;
g += data[srcOff+1] * data[srcOff+1] * wt;
b += data[srcOff+2] * data[srcOff+2] * wt;
a += data[srcOff+3] * data[srcOff+3] * wt;
}
}
}
data1[ind+0] = Math.sqrt(Math.max(0,r));
data1[ind+1] = Math.sqrt(Math.max(0,g));
data1[ind+2] = Math.sqrt(Math.max(0,b));
data1[ind+3] = Math.sqrt(Math.max(0,a));
}
}
return this.putImageData(image,imageDataResult);
}
};
return tools;
})();
/** SimpleFullCanvasMouse.js begin **/
const CANVAS_ELEMENT_ID = "canv";
const U = undefined;
var w, h, cw, ch; // short cut vars
var canvas, ctx;
var globalTime = 0;
var createCanvas, resizeCanvas, setGlobals;
var L = typeof log === "function" ? log : function(d){ console.log(d); }
createCanvas = function () {
var c,cs;
cs = (c = document.createElement("canvas")).style;
c.id = CANVAS_ELEMENT_ID;
cs.position = "absolute";
cs.top = cs.left = "0px";
cs.zIndex = 1000;
document.body.appendChild(c);
return c;
}
resizeCanvas = function () {
if (canvas === U) { canvas = createCanvas(); }
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") { setGlobals(); }
}
setGlobals = function(){
cw = (w = canvas.width) / 2; ch = (h = canvas.height) / 2;
if(particles && particles.length > 0){
particles.length = 0;
}
}
resizeCanvas(); // create and size canvas
window.addEventListener("resize",resizeCanvas); // add resize event
const IMAGE_SIZE = 32;
const IMAGE_SIZE_HALF = 16;
const GRAV = 2001;
const NUM_PARTICLES = 90;
var background = imageTools.createImage(8,8);
var grad = ctx.createLinearGradient(0,0,0,8);
grad.addColorStop(0,"#000");
grad.addColorStop(1,"#048");
background.ctx.fillStyle = grad;
background.ctx.fillRect(0,0,8,8);
var circle = imageTools.createImage(IMAGE_SIZE,IMAGE_SIZE);
circle.ctx.fillStyle = "#5BF";
circle.ctx.arc(IMAGE_SIZE_HALF, IMAGE_SIZE_HALF, IMAGE_SIZE_HALF -2,0, Math.PI * 2);
circle.ctx.fill();
var blurFilter = imageTools.createBlurConvolutionArray({size:9,power:1}); // size must be greater than 2 and must be odd eg 3,5,7,9...
var blurCircle = imageTools.padImage(circle,9);
blurCircle = imageTools.applyConvolutionFilter(blurCircle,blurFilter)
var sun = imageTools.createImage(64,64);
grad = ctx.createRadialGradient(32,32,0,32,32,32);
grad.addColorStop(0,"#FF0");
grad.addColorStop(1,"#A40");
sun.ctx.fillStyle = grad;
sun.ctx.arc(32,32,32 -2,0, Math.PI * 2);
sun.ctx.fill();
var sunBlur = imageTools.padImage(sun,17);
blurFilter = imageTools.createBlurConvolutionArray({size:17,power:1}); // size must be greater than 2 and must be odd eg 3,5,7,9...
sunBlur = imageTools.applyConvolutionFilter(sunBlur,blurFilter);
var particles = [];
var createParticle = function(x,y,dx,dy){
var dir = Math.atan2(y-ch,x-cw);
var dist = Math.sqrt(Math.pow(y-ch,2)+Math.pow(x-cw,2));
var v = Math.sqrt(GRAV / dist); // get apporox orbital speed
return {
x : x,
y : y,
dx : dx + Math.cos(dir + Math.PI/2) * v, // set orbit speed at tangent
dy : dy + Math.sin(dir + Math.PI/2) * v,
s : (Math.random() + Math.random() + Math.random())/4 + 0.5, // scale
v : (Math.random() + Math.random() + Math.random()) / 3 + 2, // glow vary rate
};
}
var depthSort = function(a,b){
return b.y - a.y;
}
var updateParticles = function(){
var i,p,f,dist,dir;
for(i = 0; i < particles.length; i ++){
p = particles[i];
dist = Math.sqrt(Math.pow(cw-p.x,2)+Math.pow(ch-p.y,2));
dir = Math.atan2(ch-p.y,cw-p.x);
f = GRAV * 1 / (dist * dist);
p.dx += Math.cos(dir) * f;
p.dy += Math.sin(dir) * f;
p.x += p.dx;
p.y += p.dy;
p.rx = ((p.x - cw ) / (p.y + h)) * h + cw;
p.ry = ((p.y - ch ) / (p.y + h)) * h * -0.051+ ch;
//p.ry = ((h-p.y) - ch) * 0.1 + ch;
p.rs = (p.s / (p.y + h)) * h
}
particles.sort(depthSort)
}
var drawParticles = function(){
var i,j,p,f,dist,dir;
// draw behind the sun
for(i = 0; i < particles.length; i ++){
p = particles[i];
if(p.y - ch < 0){
break;
}
ctx.setTransform(p.rs,0,0,p.rs,p.rx,p.ry);
ctx.drawImage(circle,-IMAGE_SIZE_HALF,-IMAGE_SIZE_HALF);
}
// draw glow for behind the sun
ctx.globalCompositeOperation = "screen";
var iw = -blurCircle.width/2;
for(j = 0; j < i; j ++){
p = particles[j];
ctx.globalAlpha = ((Math.sin(globalTime / (50 * p.v)) + 1) / 2) * 0.6 + 0.4;
var scale = (1-(Math.sin(globalTime / (50 * p.v)) + 1) / 2) * 0.6 + 0.6;
ctx.setTransform(p.rs * 1.5 * scale,0,0,p.rs * 1.5* scale,p.rx,p.ry);
ctx.drawImage(blurCircle,iw,iw);
// second pass to intensify the glow
ctx.globalAlpha = 0.7;
ctx.setTransform(p.rs * 1.1,0,0,p.rs * 1.1,p.rx,p.ry);
ctx.drawImage(blurCircle,iw,iw);
}
// draw the sun
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = 1;
ctx.setTransform(1,0,0,1,cw,ch);
ctx.drawImage(sun,-sun.width/2,-sun.height/2);
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "screen";
ctx.setTransform(1,0,0,1,cw,ch);
ctx.drawImage(sunBlur,-sunBlur.width/2,-sunBlur.height/2);
var scale = Math.sin(globalTime / 100) *0.5 + 1;
ctx.globalAlpha = (Math.cos(globalTime / 100) + 1) * 0.2 + 0.4;;
ctx.setTransform(1 + scale,0,0,1 + scale,cw,ch);
ctx.drawImage(sunBlur,-sunBlur.width/2,-sunBlur.height/2);
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
// draw in front the sun
for(j = i; j < particles.length; j ++){
p = particles[j];
if(p.y > -h){ // don't draw past the near view plane
ctx.setTransform(p.rs,0,0,p.rs,p.rx,p.ry);
ctx.drawImage(circle,-IMAGE_SIZE_HALF,-IMAGE_SIZE_HALF);
}
}
ctx.globalCompositeOperation = "screen";
var iw = -blurCircle.width/2;
for(j = i; j < particles.length; j ++){
p = particles[j];
if(p.y > -h){ // don't draw past the near view plane
ctx.globalAlpha = ((Math.sin(globalTime / (50 * p.v)) + 1) / 2) * 0.6 + 0.4;
var scale = (1-(Math.sin(globalTime / (50 * p.v)) + 1) / 2) * 0.6 + 0.6;
ctx.setTransform(p.rs * 1.5 * scale,0,0,p.rs * 1.5* scale,p.rx,p.ry);
ctx.drawImage(blurCircle,iw,iw);
// second pass to intensify the glow
ctx.globalAlpha = 0.7;
ctx.setTransform(p.rs * 1.1,0,0,p.rs * 1.1,p.rx,p.ry);
ctx.drawImage(blurCircle,iw,iw);
}
}
ctx.globalCompositeOperation = "source-over";
}
var addParticles = function(count){
var ww = (h-10)* 2;
var cx = cw - ww/2;
var cy = ch - ww/2;
for(var i = 0; i < count; i ++){
particles.push(createParticle(cx + Math.random() * ww,cy + Math.random() * ww, Math.random() - 0.5, Math.random() - 0.5));
}
}
function display(){ // put code in here
if(particles.length === 0){
addParticles(NUM_PARTICLES);
}
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.drawImage(background,0,0,w,h)
updateParticles();
drawParticles();
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
}
function update(timer){ // Main update loop
globalTime = timer;
display(); // call demo code
requestAnimationFrame(update);
}
requestAnimationFrame(update);
/** SimpleFullCanvasMouse.js end **/

Javascript Image Transform? [scale and rotate]

I'm looking to build an image transform tool with Javascript. Something that utilizes handles around the image similar to Photoshop and allows the user to scale and rotate. I'm looking to make this work in IE 6 and up and Firefox 3+ and Safari 3+.
Does anyone know of a library or tool that could help with this? I've seen a lot of tools that utilize the Canvas element but that leaves out IT. I've also seen the Raphael library which might work. Any other options out there?
Have a look at this rotorzoom.
It rotates it zooms it's fast and i can do with a few more hits.
http://codepen.io/hex2bin/pen/tHwhF
var requestId = 0;
var animationStartTime = 0;
var img = new Image();
initimg(img);
dst = document.getElementById("dst").getContext("2d");
dst.drawImage(img, 0, 0, 256, 256);
// read the width and height of the canvas
i = 0;
var imageDataDst = dst.getImageData(0, 0, 1024, 512);
var bufDst = new ArrayBuffer(imageDataDst.data.length);
var buf8Dst = new Uint8ClampedArray(bufDst);
var data32Dst = new Uint32Array(bufDst);
var data32Src = new Uint32Array(256*256);
var scan1=0;
var scan4=0
// fill the source array with the image
for (var y = 0; y < 256; ++y) {
scan4=y*1024*4;
for (var x = 0; x < 256; ++x) {
data32Src[scan1++] =
(255 << 24) + // alpha
(imageDataDst.data[scan4+2] << 16) + // blue
(imageDataDst.data[scan4+1] << 8) + // green
imageDataDst.data[scan4]; // red
scan4=scan4+4;
}
}
animationStartTime = window.performance.now();
requestId = window.requestAnimationFrame(animate);
var j=0;
function animate(time) {
var height=512;
var width=1024;
j=j+1;
var timestamp = j / 100;
var pos;
var startY = 128;
var startX = 128;
var i=0;
var scaledHeight = 512 + Math.sin(timestamp*1.4) * 500;
var scaledWidth = 512 + Math.sin(timestamp*1.4) * 500
var angleRadians = timestamp;
var deltaX1 = Math.cos(angleRadians) * scaledHeight / 256;
var deltaY1 = Math.sin(angleRadians) * scaledHeight / 256;
var deltaY1x256=deltaY1*256;
var deltaX2 =Math.sin(angleRadians ) * scaledWidth / 256;
var deltaY2 =- Math.cos(angleRadians ) * scaledWidth / 256;
var h = height;
while (h--) {
var x =262144+ startX+deltaX1*-512+deltaX2*-256;
var y =262144+ startY+deltaY1*-512+deltaY2*-256;
var y256=y*256;
var w = width;
while (w--) {
//Optimised inner loop. Can it be done better?
pos =(y256>>0&0xff00)+(x>>0&0xff);
data32Dst[i++] =data32Src[pos];
x += deltaX1;
y256 += deltaY1x256;
//end of inner loop
}
startX += deltaX2
startY += deltaY2;
}
imageDataDst.data.set(buf8Dst);
dst.putImageData(imageDataDst, 0, 0);
requestId = window.requestAnimationFrame(animate);
}
function initimg(image1)
{
image1.src = 'data:image/jpeg;base64,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';
}
<body>
<canvas id="dst" width="1024", height="512">
Random Canvas
</canvas>
</body>
Check out Raphael library on Github: https://github.com/DmitryBaranovskiy/raphael

Categories

Resources