JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.4.4/d3.min.js"></script>
JavaScript
// Part 1
viewof image1 = imageInput({
height: 350,
crossOrigin: "anonymous",
initialUrl: rioMoscovaUrl,
})
// Part 2
html`
${image} ${image.src === rioMoscovaUrl ? rioMoscovaAttribution : ""}
`
// Part 3
{
const height = width / 4;
const margin = 2;
const x = d3.scaleLinear()
.domain([0, 256])
.range([0 + margin, width - margin])
const y = d3.scaleLinear()
.domain([0, d3.max(histogram.map(x => d3.max(x.data)))])
.range([height - margin, 0 + margin])
const area = d3.area()
.x((_, i) => x(i))
.y0(y(0))
.y1((d, _) => y(d))
.curve(d3.curveStepAfter)
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("class", "histogram")
.style("background-color", "#333")
.style("isolation", "isolate");
for (const channel of histogram) {
svg.append("path")
.attr("class", channel.name)
// duplicate the last data point so that the right-most step is drawn correctly
.attr("d", area([...channel.data, channel.data.slice(-1)]))
.attr("fill", channel.color)
.attr("stroke", channel.color)
.style("mix-blend-mode", "screen")
}
return svg.node()
}
// Part 4
{
// dividing the histogram into channels like this is @lemonnish's idea
const histogram = [
{ name: "red", color: "#ff0000", data: new Uint32Array(256) },
{ name: "green", color: "#00ff00", data: new Uint32Array(256) },
{ name: "blue", color: "#0000ff", data: new Uint32Array(256) },
];
const { data } = getImageData(image);
for (let i = 0; i < data.length; i += 4) {
let pixel = data.slice(i, i + 3);
++histogram[0].data[pixel[0]];
++histogram[1].data[pixel[1]];
++histogram[2].data[pixel[2]];
}
return histogram;
}
// Part 5
getImageData = ƒ(…)
function getImageData(image, width = image.naturalWidth, height = image.naturalHeight) {
const context = DOM.context2d(width, height, 1);
context.drawImage(image, 0, 0,...