|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
canvas, |
|
svg { |
|
position: absolute; |
|
} |
|
|
|
.brush .selection { |
|
stroke: #fff; |
|
stroke-opacity: 0.7; |
|
shape-rendering: crispEdges; |
|
} |
|
|
|
.histogram { |
|
pointer-events: none; |
|
} |
|
|
|
.histogram-panel { |
|
fill: #111; |
|
fill-opacity: 0.5; |
|
stroke: #000; |
|
} |
|
|
|
.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; |
|
stroke-width: 0.5px; |
|
} |
|
|
|
</style> |
|
<canvas width="960" height="1452"></canvas> |
|
<svg width="960" height="1452"></svg> |
|
<script src="https://d3js.org/d3.v4.0.0-alpha.44.min.js"></script> |
|
<script src="https://npmcdn.com/babel-core@5.8.34/browser.min.js"></script> |
|
<script> |
|
|
|
d3_color = |
|
d3_dispatch = |
|
d3_ease = |
|
d3_interpolate = |
|
d3_timer = |
|
d3_selection = |
|
d3_transition = |
|
d3_drag = d3; |
|
|
|
</script> |
|
<script src="https://d3js.org/d3-brush.v0.1.min.js"></script> |
|
<script lang='babel' type='text/babel'> |
|
const canvas = document.querySelector('canvas'); |
|
const context = canvas.getContext('2d'); |
|
const width = canvas.width; |
|
const height = canvas.height; |
|
|
|
const histomargin = { top: 10, right: 10, bottom: 0, left: width - 256 - 10 }; |
|
const histowidth = width - histomargin.left - histomargin.right; |
|
const histoheight = 256; |
|
|
|
let r = d3.range(257).map(() => 0); |
|
let g = d3.range(257).map(() => 0); |
|
let b = d3.range(257).map(() => 0); |
|
let x = (d, i) => i; |
|
let y = d => histoheight - Math.round(d / n * 32 * histoheight); |
|
let n = 1; |
|
|
|
const area = d3.area() |
|
.curve(d3.curveStepBefore) |
|
.x(x) |
|
.y0(y(0)) |
|
.y1(y); |
|
|
|
const line = d3.line() |
|
.curve(d3.curveStepBefore) |
|
.x(x) |
|
.y(y); |
|
|
|
const brush = d3_brush.brush() |
|
.on('start brush', brushed) |
|
.on('end', brushended); |
|
|
|
const svg = d3.select('svg'); |
|
|
|
const histogram = svg.append('g') |
|
.attr('class', 'histogram') |
|
.attr('transform', `translate(${histomargin.left}, ${histomargin.top})`); |
|
|
|
histogram.append('rect') |
|
.attr('class', 'histogram-panel') |
|
.attr('x', -0.5) |
|
.attr('y', -0.5) |
|
.attr('width', histowidth + 1) |
|
.attr('height', histoheight + 1); |
|
|
|
const histoarea = histogram.selectAll('.histogram-area') |
|
.data([r, g, b]) |
|
.enter().append('path') |
|
.attr('class', (d, i) => `histogram-area histogram-${'rgb'[i]}`); |
|
|
|
const histoline = histogram.selectAll('.histogram-line') |
|
.data([r, g, b]) |
|
.enter().append('path') |
|
.attr('class', (d, i) => `histogram-line histogram-${'rgb'[i]}`) |
|
.attr('transform', 'translate(0.5,-0.5)'); |
|
|
|
const image = new Image; |
|
image.src = '100-7289.jpg'; |
|
image.onload = loaded; |
|
|
|
function loaded() { |
|
context.drawImage(this, 0, 0, width, height); |
|
|
|
svg.append('g') |
|
.attr('class', 'brush') |
|
.call(brush) |
|
.call(brush.move, [[260, 228], [656, 600]]); |
|
} |
|
|
|
function brushed() { |
|
const s = d3.event.selection; |
|
x = s[0][0]; |
|
y = s[0][1]; |
|
const w = s[1][0] - x; |
|
const h = s[1][1] - y; |
|
|
|
for (let i = 0; i < 256; ++i) { |
|
r[i] = g[i] = b[i] = 0; |
|
} |
|
|
|
n = w * h; |
|
if (n) { |
|
const data = context.getImageData(x, y, w, h).data; |
|
for (let i = 0; i < w; ++i) { |
|
for (let j = 0; j < h; ++j) { |
|
const k = j * w + i << 2; |
|
++r[data[k]]; |
|
++g[data[k + 1]]; |
|
++b[data[k + 2]]; |
|
} |
|
} |
|
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); |
|
} |
|
} |
|
</script> |