ImageGrid/ui (gen4)/experiments/canvas-waveform.html
Alex A. Naanou c9e1f8d3e6 tweaking...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2019-11-16 01:00:07 +03:00

411 lines
9.1 KiB
HTML

<!DOCTYPE html>
<html>
<style>
.graph {
position: relative;
display: inline-block;
width: attr(image-width);
height: attr(graph-height);
}
.graph canvas {
width: 100%;
height: 100%;
}
.graph .controls {
display: inline-block;
position: absolute;
top: 2px;
right: 2px;
}
.graph .controls button {
background: transparent;
border: none;
color: white;
opacity: 0.7;
}
.graph .controls button.current {
text-decoration: underline;
opacity: 0.9;
}
.graph .controls button.R:hover,
.graph .controls button.current.R {
background: red;
}
.graph .controls button.G:hover,
.graph .controls button.current.G {
background: green;
}
.graph .controls button.B:hover,
.graph .controls button.current.B {
background: blue;
}
.graph .controls button:hover {
opacity: 1;
}
</style>
<script src="../ext-lib/jquery.js"></script>
<script src="../ext-lib/jquery-ui.js"></script>
<script src="../lib/jli.js"></script>
<script>
Filters = {}
// as input takes an HTML Image object...
Filters.getPixels = function(img, w, h){
var w = w || img.width
var h = h || img.height
var c = this.getCanvas(w, h)
var context = c.getContext('2d')
if(img == null){
context.rect(0, 0, w, h)
context.fillStyle = "black"
context.fill()
} else {
context.drawImage(img, 0, 0, w, h)
}
return context.getImageData(0,0,c.width,c.height)
}
Filters.setPixels = function(c, data, w, h){
c.width = data.width
c.height = data.height
var context = c.getContext('2d')
context.putImageData(data, 0, 0)
//context.drawImage(data, 0, 0, w, h)
}
Filters.getCanvas = function(w, h){
var c = document.createElement('canvas')
c.width = w
c.height = h
return c
}
Filters.filterImage = function(filter, image, var_args){
var args = [this.getPixels(image)]
for(var i=2; i<arguments.length; i++){
args.push(arguments[i])
}
return filter.apply(null, args)
}
Filters.grayscale = function(pixels, args){
var d = pixels.data
for(var i=0; i<d.length; i+=4){
var r = d[i]
var g = d[i+1]
var b = d[i+2]
// CIE luminance for the RGB
// The human eye is bad at seeing red and blue, so we de-emphasize them.
var v = 0.2126*r + 0.7152*g + 0.0722*b
d[i] = d[i+1] = d[i+2] = v
}
return pixels
}
// XXX need to resize this...
Filters.histogram = function(pixels, mode){
mode = mode || 'luminance'
var w = 255
var h = 255
// output buffer...
var out = this.getPixels(null, w, h)
// pixel hit buffer...
var count = []
var od = out.data
var d = pixels.data
// get the stats...
for(var i=0; i<d.length; i+=4){
var r = d[i]
var g = d[i+1]
var b = d[i+2]
if(mode == 'luminance'){
var v = Math.round(0.2126*r + 0.7152*g + 0.0722*b) * 4
count[v] = count[v+1] = count[v+2] = (count[v] || 0) + 1
} else {
if(mode == 'color' || mode == 'R'){
count[r*4] = (count[r*4] || 0) + 1
}
if(mode == 'color' || mode == 'G'){
count[g*4+1] = (count[g*4+1] || 0) + 1
}
if(mode == 'color' || mode == 'B'){
count[b*4+2] = (count[b*4+2] || 0) + 1
}
}
}
var m = 255 / Math.max(...count.filter(function(){ return true }))
var pos = function(i, value){
return (
// horixontal position...
i*4
// value vertical offset...
+ (255-Math.round(value*m))*w*4) }
count.forEach(function(v, i){
var j = pos(i/4, v)
od[j] = 255
// correct for blue visibility...
if(mode != 'luminance' && (i-2)%4 == 0){
od[j-1] = od[j-2] = 180
}
})
return out
}
Filters.waveform = function(pixels, mode, color){
mode = mode || 'luminance'
color = color || 'match'
var w = pixels.width
// normalize pixel ratio...
var m = (1/pixels.height)*255
var offsetTop = 0
var offsetBottom = 0
// output buffer...
var out = this.getPixels(null,
w,
offsetTop + 255 + offsetBottom)
// pixel hit buffer...
var count = []
var od = out.data
var d = pixels.data
var pos = function(i, value){
return (
// top margin...
offsetTop*w*4
// horixontal position...
+ i%(w*4)
// value vertical offset...
+ (255-Math.round(value))*w*4) }
var gain = 100
for(var i=0; i<d.length; i+=4){
var r = d[i]
var g = d[i+1]
var b = d[i+2]
var c
var j
var f
var x
var y
if(mode == 'luminance'){
// CIE luminance for RGB
var v = 0.2126*r + 0.7152*g + 0.0722*b
c = count[j = pos(i, v)] = (count[j] || 0) + m
od[j] = od[j+1] = od[j+2] = c * gain
} else {
if(mode == 'color' || mode == 'R'){
f = 0.2126
x = 1
y = 2
j = pos(i, r)
c = count[j] = (count[j] || 0) + m
od[j] = c * gain
}
if(mode == 'color' || mode == 'G'){
f = 0.7152
x = -1
y = 1
j = pos(i, g) + 1
c = count[j] = (count[j] || 0) + m
od[j] = c * gain
}
if(mode == 'color' || mode == 'B'){
f = 0.0722
x = -2
y = -1
j = pos(i, b) + 2
c = count[j] = (count[j] || 0) + m
od[j] = c * gain
}
// normalize...
mode != 'color'
&& (color == 'white' ?
(od[j+x] = od[j+y] = c * gain)
: color == 'normalized' ?
(od[j+x] = od[j+y] = c * gain/2 * (1-f))
: null)
}
}
return out
}
var WAVEFORM_SIZE = 1000
var waveform = function(img, canvas, mode, color){
var d = Filters.getPixels(img, WAVEFORM_SIZE)
var w = Filters.waveform(d, mode, color)
Filters.setPixels(canvas, w)
}
var HISTOGRAM_SIZE = 1000
var histogram = function(img, canvas, mode, color){
var d = Filters.getPixels(img)
var w = Filters.histogram(d, mode)
Filters.setPixels(canvas, w)
}
// XXX make defaults externally savable -- options???
// XXX add support for img URL...
var makeWaveform = function(img, mode, color, controls){
mode = mode || 'color'
var color_modes = ['white', 'normalized', 'color']
color = color || color_modes[0]
// XXX configurable...
var type = 'waveform'
var graph = waveform
var buttons
var update = function(m){
m = m || mode || 'color'
graph(img, canvas, m, color)
;(buttons || [])
.forEach(function(b){
b.classList.contains(m) ?
b.classList.add('current')
: b.classList.remove('current') }) }
// handle img urls...
if(typeof(img) == typeof('str')){
var src = img
img = document.createElement('img')
img.onload = function(){
container.setAttribute('image-width', img.width)
container.setAttribute('image-height', img.height)
update() }
img.src = src }
// container...
var container = document.createElement('div')
container.classList.add('graph', type)
// canvas...
var canvas = document.createElement('canvas')
container.appendChild(canvas)
// controls...
if(controls || controls === undefined){
var controls = document.createElement('div')
controls.classList.add('controls')
// buttons...
buttons = ['luminance', 'color', 'R', 'G', 'B']
.map(function(m){
var button = document.createElement('button')
button.innerText = m
button.classList.add(m)
button.onclick = function(){
update(mode = m) }
controls.appendChild(button)
return button })
// color mode switch...
var button = document.createElement('button')
button.innerText = '('+ color[0] +')'
button.onclick = function(){
color = color_modes[(color_modes.indexOf(color) + 1) % color_modes.length]
this.innerText = '('+ color[0] +')'
update() }
controls.appendChild(button)
// add to block...
container.appendChild(controls) }
// meta stuff...
container.setAttribute('graph-width', canvas.width)
container.setAttribute('graph-height', canvas.height)
container.setAttribute('image-width', img.width)
container.setAttribute('image-height', img.height)
// init...
update()
return container
}
var start = function(){
//waveform(document.getElementById('input'), document.getElementById('waveform'), 'color')
//histogram(document.getElementById('input'), document.getElementById('histogram'), 'color')
//document.body.appendChild(makeWaveform(document.getElementById('input'), 'color', 'normalized'))
document.body.appendChild(makeWaveform(document.getElementById('input')))
document.body.appendChild(makeWaveform('../images/splash-800x500.jpg'))
}
</script>
<body>
<img id="input" src="../images/splash-800x500.jpg" onload="start()"/>
<!--
<br>
<canvas id="waveform"></canvas>
<br>
<button onclick="waveform(getElementById('input'), getElementById('waveform'), 'luminance')">Luminance</button>
<button onclick="waveform(getElementById('input'), getElementById('waveform'), 'color')">Color</button>
<button onclick="waveform(getElementById('input'), getElementById('waveform'), 'R')">R</button>
<button onclick="waveform(getElementById('input'), getElementById('waveform'), 'G')">G</button>
<button onclick="waveform(getElementById('input'), getElementById('waveform'), 'B')">B</button>
<br>
<canvas id="histogram"></canvas>
<br>
<button onclick="histogram(getElementById('input'), getElementById('histogram'), 'luminance')">Luminance</button>
<button onclick="histogram(getElementById('input'), getElementById('histogram'), 'color')">Color</button>
<button onclick="histogram(getElementById('input'), getElementById('histogram'), 'R')">R</button>
<button onclick="histogram(getElementById('input'), getElementById('histogram'), 'G')">G</button>
<button onclick="histogram(getElementById('input'), getElementById('histogram'), 'B')">B</button>
-->
<br>
<br>
<br>
</body>
</html>