chart = {
const r = new Uint16Array(257);
const g = new Uint16Array(257);
const b = new Uint16Array(257);
const brush = d3.brush()
.on("start brush", brushed);
const context = DOM.context2d(width, height, 1);
context.willReadFrequently = true;
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
const histogram = svg.append("g")
.style("isolation", "isolate");
const histoarea = histogram
.selectAll("path")
.data([r, g, b])
.join("path")
.style("mix-blend-mode", "screen")
.attr("fill", (d, i) => d3.hsl(i * 120, 1, 0.5));
const histoline = histoarea.clone()
.attr("fill", "none")
.attr("stroke", "black")
.attr("shape-rendering", "crispEdges")
.style("mix-blend-mode", null)
.raise();
context.drawImage(image, 0, 0, width, height);
svg.append("g")
.call(brush)
.call(brush.move, [[width * 0.3, height * 0.1], [width * 0.7, height * 0.4]]);
function brushed({selection: [[x0, y0], [x1, y1]]}) {
x0 = Math.round(x0), y0 = Math.round(y0);
x1 = Math.round(x1), y1 = Math.round(y1);
const dx = x1 - x0, dy = y1 - y0;
r.fill(0);
g.fill(0);
b.fill(0);
if (x1 > x0 && y1 > y0) {
const data = context.getImageData(x0, y0, dx, dy).data;
let max = 0;
for (let i = 0, k = -1; i < dx; ++i) {
for (let j = 0; j < dy; ++j, ++k) {
max = Math.max(max, ++r[data[++k]], ++g[data[++k]], ++b[data[++k]]);
}
}
y.domain([0, max]);
}
histoarea.attr("d", area);
histoline.attr("d", line);
}
return html`
${Object.assign(context.canvas, {style: "position: absolute;"})}
${Object.assign(svg.node(), {style: "position: relative;"})}
`;
}