Worked on image functions
This commit is contained in:
38
canvas/image-loading.js
Normal file
38
canvas/image-loading.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
//Create useful variables
|
||||||
|
const aspect = W / H;
|
||||||
|
const scale = aspect >= 1 ? H / 2 : W / 2;
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
// Reset transform
|
||||||
|
C.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
|
|
||||||
|
// Clear canvas
|
||||||
|
C.clearRect(0, 0, W, H);
|
||||||
|
|
||||||
|
// Reset common properties
|
||||||
|
C.globalCompositeOperation = 'source-over';
|
||||||
|
C.fillStyle = 'rgba(255,200,100,1)';
|
||||||
|
C.strokeStyle = 'rgba(0,0,0,1)';
|
||||||
|
|
||||||
|
|
||||||
|
// Set up transform to map -1,-1..1,1 to canvas with aspect fit
|
||||||
|
const offsetX = W / 2;
|
||||||
|
const offsetY = H / 2;
|
||||||
|
|
||||||
|
//Apply transform
|
||||||
|
C.translate(offsetX, offsetY);
|
||||||
|
C.scale(scale, -scale);
|
||||||
|
|
||||||
|
// Set 1 pixel wide lines
|
||||||
|
C.lineWidth = 1 / scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setup();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const { load_image_from_src, Image_Data_Sampler } = await import('../lib/image-functions.js');
|
||||||
|
const sampler = new Image_Data_Sampler(await load_image_from_src("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAFVJREFUOE9jZGBg+A/EZAMmsnVCNQ4DA1hAXvn/HxGOjIyMJAULC0gzsiZ0PiHTwC5AB8guQpdD52M1gJA3YK4E0SwgxZSEASjERlMieryQyKc4MwEA45UXJyonWOMAAAAASUVORK5CYII="));
|
||||||
|
|
||||||
|
console.log(sampler.encode_as_bitfield());
|
||||||
@@ -141,7 +141,8 @@
|
|||||||
padding: '0.5em 1em',
|
padding: '0.5em 1em',
|
||||||
borderRadius: '5px',
|
borderRadius: '5px',
|
||||||
opacity: '0.9',
|
opacity: '0.9',
|
||||||
zIndex: 1000
|
zIndex: 1000,
|
||||||
|
pointerEvents: 'none',
|
||||||
});
|
});
|
||||||
document.body.appendChild(toast);
|
document.body.appendChild(toast);
|
||||||
setTimeout(() => toast.remove(), 1500);
|
setTimeout(() => toast.remove(), 1500);
|
||||||
@@ -155,7 +156,7 @@
|
|||||||
<button id="copy_url">Copy <i>Data URL</i></button>(<kbd>Alt</kbd><kbd>D</kbd>)
|
<button id="copy_url">Copy <i>Data URL</i></button>(<kbd>Alt</kbd><kbd>D</kbd>)
|
||||||
<button id="copy_script">Copy loader script</button>(<kbd>Alt</kbd><kbd>S</kbd>)
|
<button id="copy_script">Copy loader script</button>(<kbd>Alt</kbd><kbd>S</kbd>)
|
||||||
</div>
|
</div>
|
||||||
<div id="dropZone">Paste an image here (Ctrl+V)</div>
|
<div id="dropZone">Paste an image here (<kbd>Ctrl</kbd><kbd>V</kbd>)</div>
|
||||||
<h2>Data URL</h2>
|
<h2>Data URL</h2>
|
||||||
<code id="data_url_bin"></code>
|
<code id="data_url_bin"></code>
|
||||||
<h2>EcmaScript</h2>
|
<h2>EcmaScript</h2>
|
||||||
|
|||||||
115
lib/image-functions.js
Normal file
115
lib/image-functions.js
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
export function load_image_from_src(src) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const image = new Image();
|
||||||
|
image.src = src;
|
||||||
|
|
||||||
|
image.addEventListener('load', ({ target }) => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
canvas.width = target.width;
|
||||||
|
canvas.height = target.height;
|
||||||
|
|
||||||
|
ctx.drawImage(target, 0, 0);
|
||||||
|
|
||||||
|
resolve(ctx.getImageData(0, 0, target.width, target.height));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Image_Row_Reference {
|
||||||
|
constructor(sampler, row) {
|
||||||
|
Object.assign(this, { sampler, row });
|
||||||
|
}
|
||||||
|
|
||||||
|
*[Symbol.iterator]() {
|
||||||
|
yield* this.sampler.iter_row(this.row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RGBA_unorm8 {
|
||||||
|
constructor(R=0, G=0, B=0, A=255) {
|
||||||
|
Object.assign(this, { R, G, B, A });
|
||||||
|
}
|
||||||
|
|
||||||
|
*[Symbol.iterator]() {
|
||||||
|
const {R, G, B, A} = this;
|
||||||
|
yield* [R, G, B, A];
|
||||||
|
}
|
||||||
|
|
||||||
|
get non_zero() {
|
||||||
|
const {R, G, B, A} = this;
|
||||||
|
return (A > 0) && ((R > 0) || (G > 0) || (B > 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Image_Data_Sampler {
|
||||||
|
constructor(image_data) {
|
||||||
|
Object.assign(this, { image_data });
|
||||||
|
}
|
||||||
|
|
||||||
|
encode_as_bitfield() {
|
||||||
|
//TODO - add alignment option
|
||||||
|
const { width, height } = this.image_data;
|
||||||
|
let result = '';
|
||||||
|
const bytes_per_row = Math.ceil(width / 8);
|
||||||
|
|
||||||
|
for (const row of this) {
|
||||||
|
let pending_value = 0;
|
||||||
|
let bit_value = 1;
|
||||||
|
for (const pixel of row) {
|
||||||
|
|
||||||
|
if (pixel.non_zero) {
|
||||||
|
pending_value |= bit_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bit_value <<= 1;
|
||||||
|
if (bit_value === 256) {
|
||||||
|
result += String.fromCharCode(pending_value);
|
||||||
|
pending_value = 0;
|
||||||
|
bit_value = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bit_value > 1) { //If we have a tail we flush it here.
|
||||||
|
result += String.fromCharCode(pending_value);
|
||||||
|
pending_value = 0;
|
||||||
|
bit_value = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `${width},${height},${btoa(result)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
*iter_row(row) {
|
||||||
|
const { pixelFormat, data, width, height } = this.image_data;
|
||||||
|
switch(pixelFormat) {
|
||||||
|
case 'rgba-unorm8': {
|
||||||
|
const pixel_offset_y = row * width;
|
||||||
|
for (let x=0; x<width; x++) {
|
||||||
|
const pixel_offset_x = pixel_offset_y + x;
|
||||||
|
const data_offset = pixel_offset_x * 4;
|
||||||
|
yield new RGBA_unorm8(...data.slice(data_offset, data_offset + 4));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Format ${pixelFormat} is not yet supported by ${this.constructor.name}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*[Symbol.iterator]() {
|
||||||
|
const { pixelFormat, data, width, height } = this.image_data;
|
||||||
|
switch(pixelFormat) {
|
||||||
|
case 'rgba-unorm8': {
|
||||||
|
for (let y=0; y<height; y++) {
|
||||||
|
yield new Image_Row_Reference(this, y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Format ${pixelFormat} is not yet supported by ${this.constructor.name}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user