diff --git a/canvas/image-loading.js b/canvas/image-loading.js
new file mode 100644
index 0000000..11def24
--- /dev/null
+++ b/canvas/image-loading.js
@@ -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());
diff --git a/image/test1.html b/image/test1.html
index 6587f9b..d33d5af 100644
--- a/image/test1.html
+++ b/image/test1.html
@@ -141,7 +141,8 @@
padding: '0.5em 1em',
borderRadius: '5px',
opacity: '0.9',
- zIndex: 1000
+ zIndex: 1000,
+ pointerEvents: 'none',
});
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 1500);
@@ -155,7 +156,7 @@
(AltD)
(AltS)
-
Paste an image here (Ctrl+V)
+ Paste an image here (CtrlV)
Data URL
EcmaScript
diff --git a/lib/image-functions.js b/lib/image-functions.js
new file mode 100644
index 0000000..b19e1ec
--- /dev/null
+++ b/lib/image-functions.js
@@ -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