block by mbostock 0d20834e3d5a46138752f86b9b79727e

Mona Lisa Histogram

Full Screen

Updated Example →

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>

canvas,
svg {
  position: absolute;
}

.histogram {
  isolation: isolate;
}

.histogram-area {
  mix-blend-mode: screen;
}

.histogram-area.histogram-r {
  fill: #f00;
}

.histogram-area.histogram-g {
  fill: #0f0;
}

.histogram-area.histogram-b {
  fill: #00f;
}

.histogram-line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

</style>
<canvas width="960" height="1452"></canvas>
<svg width="960" height="1452"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

var canvas = document.querySelector("canvas"),
    context = canvas.getContext("2d"),
    width = canvas.width,
    height = canvas.height;

var histowidth = width,
    histoheight = width / 2;

var x = d3.scaleLinear().domain([0, 256]).rangeRound([0, histowidth]),
    y = d3.scaleLinear().rangeRound([0, histoheight]);

var r = new Array(257),
    g = new Array(257),
    b = new Array(257);

var area = d3.area()
    .curve(d3.curveStepAfter)
    .x(function(d, i) { return x(i); })
    .y0(y(0))
    .y1(y);

var line = d3.line()
    .curve(curveStepBelow)
    .x(function(d, i) { return x(i); })
    .y(y);

var brush = d3.brush()
    .on("start brush", brushed)
    .on("end", brushended);

var svg = d3.select("svg");

var histogram = svg.append("g")
    .attr("class", "histogram");

var histoarea = histogram.selectAll(".histogram-area")
    .data([r, g, b])
  .enter().append("path")
    .attr("class", function(d, i) { return "histogram-area histogram-" + "rgb"[i]; });

var histoline = histogram.selectAll(".histogram-line")
    .data([r, g, b])
  .enter().append("path")
    .attr("class", function(d, i) { return "histogram-line histogram-" + "rgb"[i]; });

var image = new Image;
image.src = "mona-lisa.jpg";
image.onload = loaded;

function loaded() {
  context.drawImage(this, 0, 0);

  svg.append("g")
      .attr("class", "brush")
      .call(brush)
      .call(brush.move, [[307, 167], [611, 539]]);
}

function brushed() {
  var s = d3.event.selection,
      x0 = s[0][0],
      y0 = s[0][1],
      dx = s[1][0] - x0,
      dy = s[1][1] - y0,
      max = 0;

  for (var i = 0; i < 257; ++i) {
    r[i] = g[i] = b[i] = 0;
  }

  if (dx && dy) {
    var data = context.getImageData(x0, y0, dx, dy).data;
    for (var i = 0; i < dx; ++i) {
      for (var j = 0; j < dy; ++j) {
        var k = j * dx + i << 2;
        max = Math.max(max, ++r[data[k]], ++g[data[k + 1]], ++b[data[k + 2]]);
      }
    }
    y.domain([0, max]);
    histoarea.attr("d", area);
    histoline.attr("d", line);
  } else {
    histoarea.attr("d", null);
    histoline.attr("d", null);
  }
}

function brushended() {
  if (!d3.event.selection) {
    histoarea.attr("d", null);
    histoline.attr("d", null);
  }
}

function curveStepBelow(context) {
  var y0, i;
  return {
    lineStart: function() { y0 = NaN, i = 0; },
    lineEnd: function() {},
    point: function(x, y) {
      x -= y0 < y ? -0.5 : +0.5, y += 0.5;
      if (++i === 1) context.moveTo(x, y0 = y);
      else context.lineTo(x, y0), context.lineTo(x, y0 = y);
    }
  };
}

</script>