Parse
File Parse image.js
This tree is parsed live from the source file.
Classes
-
{{ item.name }}
- {{ key }}
Not Classes
{{ getTree() }}
Comments
{{ getTreeComments() }}
Source
const image = function(ctx, src) {
const img = new Image();
img.crossOrigin = 'anonymous';
img.addEventListener("load", () => {
ctx.drawImage(img, 0, 0);
});
img.src = src;
}
class ImageLoader {
/* Load an _image_ (such as png) into the point position with
a given path:
point = new Point(200,200)
image = new ImageLoader("./images/mail.png", point)
image.position.rotation = 30
image.draw(ctx)
The draw factors in the point radius, position, and rotation
*/
constructor(path, point) {
this.path = path
this.width = undefined
this.height = undefined
this.position = point == undefined? new Point(100, 100, 100): point;
this.scale = 1
if(path != undefined) {
this.load(path)
}
}
load(path=this.path) {
const img = new Image();
// img.crossOrigin = 'anonymous';
// img.src = path + '?' + new Date().getTime();
// img.setAttribute('crossOrigin', '');
img.addEventListener("load", (d) => {
this.imageData = img
// this.loaded(d)
});
img.src = path;
this.image = img
}
setImageData(data) {
// stage.offScreenCanvas.transferToImageBitmap()
this.imageData = data
}
getOffscreenCanvas(width = 200, height = 300){
let osc = new OffscreenCanvas(width, height)
return osc
}
getImageData(osc, width = 200, height = 300) {
let osctx = osc.getContext('2d')
osctx.drawImage(this.image, 0, 0, width, height)
let imageData = osctx.getImageData(0, 0, width, height)
return imageData
}
draw(ctx) {
if(!this.imageData) { return };
let p = this.position
, radius = p.radius
, r2 = radius * 2
, scale = this.scale
;
ctx.save()
/* First we move to the location of which to spin.*/
ctx.translate(p.x, p.y)
/* Then spin the paper */
ctx.scale(scale, scale)
ctx.rotate(p.radians)
/* then move to the position relative to the center of the image. */
ctx.translate(-radius, -radius)
/* Draw the image at position 0, 0
of which is the rotated and translated position.*/
ctx.drawImage(this.imageData, 0, 0, r2, r2)
// oSepia(ctx, this.imageData, stage.canvas)
ctx.restore()
}
recolor() {
var data = this.imageData
// we manipulate the pixel data here in order to make the image lighter
var changeColor = function() {
for (var i = 0; i < data.length; i += 4) { // we are jumping every 4 values of RGBA for every pixel
data[i] = data[i] - 199; // the red color channel - we have decreased its value
data[i + 1] = data[i + 1] - 199; // the green color channel - we have decreased its value
data[i + 2] = data[i + 2] + 100; // the blue color channel - we have increased its value
}
putImageData(ctx, imageData, 0, 0);
};
}
}
var oSepia = function(ctx, img, canvas) {
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (var i = 0; i < data.length; i += 4) {
let red = data[i], green = data[i + 1], blue = data[i + 2];
data[i] = Math.min(Math.round(0.393 * red + 0.769 * green + 0.189 * blue), 255);
data[i + 1] = Math.min(Math.round(0.349 * red + 0.686 * green + 0.168 * blue), 255);
data[i + 2] = Math.min(Math.round(0.272 * red + 0.534 * green + 0.131 * blue), 255);
}
ctx.putImageData(imageData, 0, 0);
}
const asImageData = function(img, ctx) {
/*
createImageBitmap(asImageData(stage.image.image, stage.ctx)).then(b=>{
stage.image.imageData = b;
})
*/
/* A ctx.drawImage */
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, img.width, img.height);
return imageData
}
const putImageData = function(ctx, imageData) {
ctx.putImageData(imageData, 0, 0);
}
const imageDataEditor = function(ctx, img, func) {
let imageData = asImageData(img, ctx)
let data = imageData.data
func(data)
putImageData(ctx, imageData)
}
var sepia = function(data) {
const round = Math.round;
const min = Math.min;
for (var i = 0; i < data.length; i += 4) {
let red = data[i]
, green = data[i + 1]
, blue = data[i + 2]
;
data[i] = min(round(0.393 * red + 0.769 * green + 0.189 * blue), 255);
data[i + 1] = min(round(0.349 * red + 0.686 * green + 0.168 * blue), 255);
data[i + 2] = min(round(0.272 * red + 0.534 * green + 0.131 * blue), 255);
}
}
var invert = function(data) {
for (var i = 0; i < data.length; i += 4) {
data[i] = 255 - data[i]; // red
data[i + 1] = 255 - data[i + 1]; // green
data[i + 2] = 255 - data[i + 2]; // blue
}
};
var blur = function(data, width, height) {
// Copy of original data so we don’t overwrite as we go
var copy = new Uint8ClampedArray(data);
for (var y = 1; y < height - 1; y++) {
for (var x = 1; x < width - 1; x++) {
var r = 0, g = 0, b = 0;
for (var dy = -1; dy <= 1; dy++) {
for (var dx = -1; dx <= 1; dx++) {
var i = ((y + dy) * width + (x + dx)) * 4;
r += copy[i];
g += copy[i + 1];
b += copy[i + 2];
}
}
var i = (y * width + x) * 4;
data[i] = r * .11;
data[i + 1] = g * .11;
data[i + 2] = b * .11;
}
}
};
var blur2 = function(data, width, height, amount = 1) {
var size = Math.max(3, (amount * 2 + 1) | 1); // ensures odd size: 3, 5, 7...
var half = Math.floor(size / 2);
var copy = new Uint8ClampedArray(data);
var area = size * size;
for (var y = half; y < height - half; y++) {
for (var x = half; x < width - half; x++) {
var r = 0, g = 0, b = 0;
for (var dy = -half; dy <= half; dy++) {
for (var dx = -half; dx <= half; dx++) {
var i = ((y + dy) * width + (x + dx)) * 4;
r += copy[i];
g += copy[i + 1];
b += copy[i + 2];
}
}
var i = (y * width + x) * 4;
data[i] = r / area;
data[i + 1] = g / area;
data[i + 2] = b / area;
}
}
};
var blur3 = function(data, width, height, amount = 1) {
var size = Math.max(3, (amount * 2 + 1) | 1); // odd grid: 3, 5, 7...
var half = Math.floor(size / 2);
var copy = new Uint8ClampedArray(data);
var area = size * size;
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
var r = 0, g = 0, b = 0, a = 0;
var count = 0;
for (var dy = -half; dy <= half; dy++) {
for (var dx = -half; dx <= half; dx++) {
var px = Math.min(width - 1, Math.max(0, x + dx));
var py = Math.min(height - 1, Math.max(0, y + dy));
var i = (py * width + px) * 4;
r += copy[i];
g += copy[i + 1];
b += copy[i + 2];
a += copy[i + 3];
count++;
}
}
var i = (y * width + x) * 4;
data[i] = r / count;
data[i + 1] = g / count;
data[i + 2] = b / count;
data[i + 3] = a / count;
}
}
};
function blurSeparable(data, width, height, amount = 1) {
var size = Math.max(3, (amount * 2 + 1) | 1);
var half = Math.floor(size / 2);
var copy = new Uint8ClampedArray(data);
var temp = new Uint8ClampedArray(data.length);
// Horizontal pass
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
var r = 0, g = 0, b = 0, a = 0, count = 0;
for (var dx = -half; dx <= half; dx++) {
var px = Math.min(width - 1, Math.max(0, x + dx));
var i = (y * width + px) * 4;
r += copy[i];
g += copy[i + 1];
b += copy[i + 2];
a += copy[i + 3];
count++;
}
var i = (y * width + x) * 4;
temp[i] = r / count;
temp[i + 1] = g / count;
temp[i + 2] = b / count;
temp[i + 3] = a / count;
}
}
// Vertical pass (on temp)
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
var r = 0, g = 0, b = 0, a = 0, count = 0;
for (var dy = -half; dy <= half; dy++) {
var py = Math.min(height - 1, Math.max(0, y + dy));
var i = (py * width + x) * 4;
r += temp[i];
g += temp[i + 1];
b += temp[i + 2];
a += temp[i + 3];
count++;
}
var i = (y * width + x) * 4;
data[i] = r / count;
data[i + 1] = g / count;
data[i + 2] = b / count;
data[i + 3] = a / count;
}
}
}
function gaussianBlur(imageData, amount = 1.0) {
const data = imageData.data;
const width = imageData.width;
const height = imageData.height;
// Kernel size: odd number based on amount
const size = Math.max(3, Math.floor(amount * 3) | 1); // e.g. 1 → 3, 2 → 5, 3 → 7
const sigma = amount;
// Generate Gaussian kernel
const kernel = generateGaussianKernel(size, sigma);
// Apply convolution
convolve(data, width, height, kernel, size);
}
function generateGaussianKernel(size, sigma) {
const kernel = [];
let sum = 0;
const half = Math.floor(size / 2);
for (let y = -half; y <= half; y++) {
for (let x = -half; x <= half; x++) {
const value = (1 / (2 * Math.PI * sigma * sigma)) *
Math.exp(-(x * x + y * y) / (2 * sigma * sigma));
kernel.push(value);
sum += value;
}
}
// Normalize
return kernel.map(v => v / sum);
}
function convolve(data, width, height, kernel, kernelSize) {
const output = new Uint8ClampedArray(data.length);
const half = Math.floor(kernelSize / 2);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let r = 0, g = 0, b = 0;
let k = 0;
for (let ky = -half; ky <= half; ky++) {
for (let kx = -half; kx <= half; kx++) {
const px = x + kx;
const py = y + ky;
if (px < 0 || px >= width || py < 0 || py >= height) {
k++;
continue;
}
const i = (py * width + px) * 4;
const weight = kernel[k++];
r += data[i] * weight;
g += data[i + 1] * weight;
b += data[i + 2] * weight;
}
}
const i = (y * width + x) * 4;
output[i] = r;
output[i + 1] = g;
output[i + 2] = b;
output[i + 3] = data[i + 3]; // preserve alpha
}
}
// Copy result back
for (let i = 0; i < data.length; i++) {
data[i] = output[i];
}
}
function gaussianWeights(size, sigma) {
const half = Math.floor(size / 2);
const weights = [];
let sum = 0;
for (let i = -half; i <= half; i++) {
const w = Math.exp(-(i * i) / (2 * sigma * sigma));
weights.push(w);
sum += w;
}
return weights.map(w => w / sum); // normalize
}
function blurSeparableGaussian(data, width, height, amount = 1.0) {
const size = Math.max(3, (amount * 2 + 1) | 1);
const half = Math.floor(size / 2);
const sigma = amount;
const weights = gaussianWeights(size, sigma);
const copy = new Uint8ClampedArray(data);
const temp = new Uint8ClampedArray(data.length);
// Horizontal pass
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let r = 0, g = 0, b = 0, a = 0;
for (let dx = -half; dx <= half; dx++) {
let px = Math.min(width - 1, Math.max(0, x + dx));
let i = (y * width + px) * 4;
let w = weights[dx + half];
r += copy[i] * w;
g += copy[i + 1] * w;
b += copy[i + 2] * w;
a += copy[i + 3] * w;
}
let i = (y * width + x) * 4;
temp[i] = r;
temp[i + 1] = g;
temp[i + 2] = b;
temp[i + 3] = a;
}
}
// Vertical pass
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let r = 0, g = 0, b = 0, a = 0;
for (let dy = -half; dy <= half; dy++) {
let py = Math.min(height - 1, Math.max(0, y + dy));
let i = (py * width + x) * 4;
let w = weights[dy + half];
r += temp[i] * w;
g += temp[i + 1] * w;
b += temp[i + 2] * w;
a += temp[i + 3] * w;
}
let i = (y * width + x) * 4;
data[i] = r;
data[i + 1] = g;
data[i + 2] = b;
data[i + 3] = a;
}
}
}
function invertColors(data) {
for (var i = 0; i < data.length; i+= 4) {
data[i] = data[i] ^ 255; // Invert Red
data[i+1] = data[i+1] ^ 255; // Invert Green
data[i+2] = data[i+2] ^ 255; // Invert Blue
}
}
// Gray = 0.21R + 0.72G + 0.07B // Luminosity
// Gray = (R + G + B) ÷ 3 // Average Brightness
// Gray = 0.299R + 0.587G + 0.114B // rec601 standard
// Gray = 0.2126R + 0.7152G + 0.0722B // ITU-R BT.709 standard
// Gray = 0.2627R + 0.6780G + 0.0593B // ITU-R BT.2100 standard
var grayscale = function(data) {
for (var i = 0; i < data.length; i += 4) {
var avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // red
data[i + 1] = avg; // green
data[i + 2] = avg; // blue
}
};
class PathData {
constructor(pathData, point) {
this.initRadius = 100
this.position = point == undefined? new Point(100, 100, this.initRadius): point;
let scale = this.scale = 4.4
this.innerOffset = [-16, -14]
this.offset = [-364, 0]
this.lineWidth = 1
this.strokeStyle = 'green'
this.pathData = pathData
this.path = new Path2D(pathData)
}
draw(ctx) {
let p = this.path
let offset = this.offset;
let pos = this.position
/* Rescaling needs a base value (init radius) to scale from.*/
let innerScale = pos.radius / this.initRadius;
let scale = this.scale * innerScale;
ctx.save()
ctx.strokeStyle = this.strokeStyle
ctx.lineWidth = this.lineWidth / scale
let innerOffset = [scale*this.innerOffset[0], scale*this.innerOffset[1]]
ctx.translate(pos.x, pos.y)
ctx.rotate(pos.radians)
ctx.translate(offset[0]*scale + innerOffset[0], offset[1]*scale + innerOffset[1])
ctx.scale(scale, scale)
ctx.stroke(p)
ctx.restore()
}
}
copy