Public
Edited
Sep 20, 2024
Insert cell
Insert cell
Insert cell
<div>
<h2>Image container</h2>
<div id='imageContainerDiv' style="position: relative">
<canvas id='bgCanvas'></canvas>
<canvas id='fgCanvas' style='position:absolute; top:0px; left: 0px'></canvas>
</div>
</div>
Insert cell
{
let img = new Image();

img.crossOrigin = "Anonymous";
img.src = selectedImage.url;

img.onload = () => {
console.log("loaded", img);
let { w, h } = fitImage(img);
image2Canvas(img, w, h);
};

return;
}
Insert cell
fitImage = (img) => {
let { width: w, height: h } = img,
height = (h / w) * width;

console.log("Fit image size from", [w, h], "to", [width, height]);
return { w: width, h: height };
}
Insert cell
mkHistogram = (data) => {
let r = [...new Array(255)].map(() => 0),
g = [...new Array(255)].map(() => 0),
b = [...new Array(255)].map(() => 0),
n = data.length / 4;

for (let i = 0; i < data.length; i += 4) {
r[data[i]] += 1;
g[data[i + 1]] += 1;
b[data[i + 2]] += 1;
}

let m1 = d3.max(r),
m2 = d3.max(g),
m3 = d3.max(b),
m = d3.max([m1, m2, m3]);

return {
r: r.map((d) => d / m),
g: g.map((d) => d / m),
b: b.map((d) => d / m)
};
}
Insert cell
image2Canvas = (img, width, height) => {
let bgCtx = document.getElementById("bgCanvas").getContext("2d"),
fgCtx = document.getElementById("fgCanvas").getContext("2d"),
fontSize = width / 80,
headerComponentsWidth = width * 0.2,
headerComponentsHeight = (width * 0.2 * height) / width,
data,
hist;

Object.assign(bgCtx.canvas, { width, height });
Object.assign(fgCtx.canvas, { width, height });

// Draw the background into the canvas
bgCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height);
data = bgCtx.getImageData(0, 0, width, height).data;
hist = mkHistogram(data);
console.log(hist);
((ctx) => {
// Draw the south-east water print into the canvas
ctx.font = fontSize + "px Monospace";
ctx.textAlign = "right";
ctx.fillStyle = "#FFFA";
ctx.fillText(
selectedImage.contents
.map((s) => s[0].toUpperCase() + s.slice(1))
.join(" | "),
width - fontSize / 2,
height - fontSize / 2
);

// Draw the south-west water print into the canvas
ctx.font = fontSize + "px Monospace";
ctx.textAlign = "left";
ctx.fillStyle = "#FFFA";
ctx.fillText(
selectedImage.url.slice(0, 80) +
(selectedImage.url.length > 80 ? "..." : ""),
0 + fontSize / 2,
height - fontSize / 2
);

// Draw the histogram
{
let w = headerComponentsWidth,
h = headerComponentsHeight,
grad;

ctx.save();
ctx.translate(width - w, 0);
grad = ctx.createLinearGradient(0, 0, w, 0);
grad.addColorStop(0.1, "#FFF5");
grad.addColorStop(1, "#FFFA");
ctx.fillStyle = grad;
ctx.fillRect(0, 0, w, h);

ctx.beginPath();
ctx.moveTo(0, 0);
hist["r"].map((d, i) => ctx.lineTo((i / 255) * w, d * h));
grad = ctx.createLinearGradient(0, 0, w, 0);
grad.addColorStop(0, "#000");
grad.addColorStop(1, "#F00");
ctx.strokeStyle = grad;
ctx.stroke();

ctx.beginPath();
ctx.moveTo(0, 0);
hist["g"].map((d, i) => ctx.lineTo((i / 255) * w, d * h));
grad = ctx.createLinearGradient(0, 0, w, 0);
grad.addColorStop(0, "#000");
grad.addColorStop(1, "#0F0");
ctx.strokeStyle = grad;
ctx.stroke();

ctx.beginPath();
ctx.moveTo(0, 0);
hist["b"].map((d, i) => ctx.lineTo((i / 255) * w, d * h));
grad = ctx.createLinearGradient(0, 0, w, 0);
grad.addColorStop(0, "#000");
grad.addColorStop(1, "#00F");
ctx.strokeStyle = grad;
ctx.stroke();

ctx.restore();
}
})(bgCtx);

// OnMouseMove drawing
fgCtx.canvas.onmousemove = function (evt) {
let { offsetX: x, offsetY: y } = evt,
idx = y * width * 4 + x * 4,
numbers = data.slice(idx + 0, idx + 4),
color = d3.rgb(numbers[0], numbers[1], numbers[2], numbers[3]);

// Draw the north-west label
((ctx) => {
let w = headerComponentsWidth,
h = headerComponentsHeight * 0.6,
scaleY = d3.scaleLinear().domain([-1, 3]).range([0, h]).nice(),
scale = d3
.scaleLinear()
.domain([-1, 255])
.range([0, w - fontSize * 5]);

ctx.clearRect(0, 0, width, height);

let grad = ctx.createLinearGradient(0, 0, fontSize * 18, 0);
grad.addColorStop(0, "#FFFA");
grad.addColorStop(0.9, "#FFF5");
ctx.fillStyle = grad;
ctx.fillRect(0, 0, fontSize * 18, h);

ctx.font = "bold " + fontSize + "px Monospace";
ctx.textAlign = "left";
ctx.textBaseline = "middle";
ctx.fillStyle = "#FFFA";

ctx.fillStyle = "red";
ctx.fillText("R " + color.r, fontSize, scaleY(0));
ctx.fillRect(fontSize * 5, scaleY(0), scale(color.r), fontSize * 0.2);

ctx.fillStyle = "green";
ctx.fillText("G " + color.g, fontSize, scaleY(1));
ctx.fillRect(fontSize * 5, scaleY(1), scale(color.g), fontSize * 0.2);

ctx.fillStyle = "blue";
ctx.fillText("B " + color.b, fontSize, scaleY(2));
ctx.fillRect(fontSize * 5, scaleY(2), scale(color.b), fontSize * 0.2);
})(fgCtx);

// Draw the mouse label
((ctx) => {
ctx.strokeStyle = "white";

// Draw the y line
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, height);
ctx.stroke();
ctx.font = fontSize + "px Monospace";
ctx.textAlign = "left";
ctx.fillStyle = "#FFFA";
ctx.fillText(
[x, parseInt((x / width) * img.width)].join(" "),
x + fontSize / 2,
fontSize
);

// Draw the x line
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(width, y);
ctx.stroke();
ctx.textAlign = "right";
ctx.textBaseline = "bottom";
ctx.fillStyle = "#FFFA";
ctx.fillText(
[y, parseInt((y / height) * img.height)].join(" "),
width - fontSize / 2,
y
);

ctx.fillStyle = color;
ctx.strokeRect(x - 10, y - 10, 20, 20);
ctx.fillRect(x - 10, y - 10, 20, 20);
})(fgCtx);
};
}
Insert cell
images
Insert cell
import { images } from "@listenzcc/image-resource"
Insert cell
require("d3")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more