mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-29 18:30:09 +00:00
ig-image-graph experiment seems to be feature complete...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
b19b8c22b5
commit
7aa071eb08
@ -113,7 +113,8 @@ Filters.grayscale = function(pixels, args){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// XXX need to resize this...
|
// XXX need to resize this...
|
||||||
Filters.histogram = function(pixels, mode){
|
Filters.histogram = function(pixels, mode, color){
|
||||||
|
color = color || 'fill'
|
||||||
mode = mode || 'luminance'
|
mode = mode || 'luminance'
|
||||||
|
|
||||||
var w = 255
|
var w = 255
|
||||||
@ -140,35 +141,34 @@ Filters.histogram = function(pixels, mode){
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
if(mode == 'color' || mode == 'R'){
|
if(mode == 'color' || mode == 'R'){
|
||||||
count[r*4] = (count[r*4] || 0) + 1
|
count[r*4] = (count[r*4] || 0) + 1 }
|
||||||
}
|
|
||||||
if(mode == 'color' || mode == 'G'){
|
if(mode == 'color' || mode == 'G'){
|
||||||
count[g*4+1] = (count[g*4+1] || 0) + 1
|
count[g*4+1] = (count[g*4+1] || 0) + 1 }
|
||||||
}
|
|
||||||
if(mode == 'color' || mode == 'B'){
|
if(mode == 'color' || mode == 'B'){
|
||||||
count[b*4+2] = (count[b*4+2] || 0) + 1
|
count[b*4+2] = (count[b*4+2] || 0) + 1 } }
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var m = 255 / Math.max(...count.filter(function(){ return true }))
|
var m = 255 / Math.max(...count.filter(function(){ return true }))
|
||||||
|
|
||||||
var pos = function(i, value){
|
var pos = function(i, value){
|
||||||
return (
|
return (
|
||||||
// horixontal position...
|
// horizontal position...
|
||||||
i*4
|
i*4
|
||||||
// value vertical offset...
|
// value vertical offset...
|
||||||
+ (255-Math.round(value*m))*w*4) }
|
+ (255-Math.round(value*m))*w*4) }
|
||||||
|
|
||||||
|
// XXX would be nice to have an option to draw full columns...
|
||||||
count.forEach(function(v, i){
|
count.forEach(function(v, i){
|
||||||
var j = pos(i/4, v)
|
var j = pos(i/4, v)
|
||||||
od[j] = 255
|
while(j < od.length){
|
||||||
|
j += w*4
|
||||||
// correct for blue visibility...
|
od[j] = 255
|
||||||
if(mode != 'luminance' && (i-2)%4 == 0){
|
if(color == 'point'){
|
||||||
od[j-1] = od[j-2] = 180
|
// correct for blue visibility...
|
||||||
}
|
mode != 'luminance'
|
||||||
})
|
&& (i-2)%4 == 0
|
||||||
|
&& (od[j-1] = od[j-2] = 180)
|
||||||
|
break } } })
|
||||||
|
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
@ -176,7 +176,7 @@ Filters.histogram = function(pixels, mode){
|
|||||||
|
|
||||||
Filters.waveform = function(pixels, mode, color){
|
Filters.waveform = function(pixels, mode, color){
|
||||||
mode = mode || 'luminance'
|
mode = mode || 'luminance'
|
||||||
color = color || 'match'
|
color = color || 'normalized'
|
||||||
|
|
||||||
var w = pixels.width
|
var w = pixels.width
|
||||||
|
|
||||||
@ -266,20 +266,16 @@ Filters.waveform = function(pixels, mode, color){
|
|||||||
|
|
||||||
|
|
||||||
var WAVEFORM_SIZE = 1000
|
var WAVEFORM_SIZE = 1000
|
||||||
|
|
||||||
var waveform = function(img, canvas, mode, color){
|
var waveform = function(img, canvas, mode, color){
|
||||||
var d = Filters.getPixels(img, WAVEFORM_SIZE)
|
var d = Filters.getPixels(img, WAVEFORM_SIZE)
|
||||||
var w = Filters.waveform(d, mode, color)
|
var w = Filters.waveform(d, mode, color)
|
||||||
Filters.setPixels(canvas, w)
|
Filters.setPixels(canvas, w) }
|
||||||
}
|
|
||||||
|
|
||||||
var HISTOGRAM_SIZE = 1000
|
var HISTOGRAM_SIZE = 1000
|
||||||
|
|
||||||
var histogram = function(img, canvas, mode, color){
|
var histogram = function(img, canvas, mode, color){
|
||||||
var d = Filters.getPixels(img)
|
var d = Filters.getPixels(img)
|
||||||
var w = Filters.histogram(d, mode)
|
var w = Filters.histogram(d, mode, color)
|
||||||
Filters.setPixels(canvas, w)
|
Filters.setPixels(canvas, w) }
|
||||||
}
|
|
||||||
|
|
||||||
// XXX should we make this a web components???
|
// XXX should we make this a web components???
|
||||||
// + would make everything transparent
|
// + would make everything transparent
|
||||||
@ -382,43 +378,27 @@ var start = function(){
|
|||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
// XXX can't get the non-class version to work...
|
||||||
/* XXX for some reason this does not work...
|
// XXX for some reason this takes some time to draw... slower than makeWaveform(..)
|
||||||
var igImageGraph = function(){
|
|
||||||
// XXX how do we call super here???
|
|
||||||
//HTMLElement.call(this, ...arguments)
|
|
||||||
}
|
|
||||||
//igImageGraph.__proto__ = HTMLElement
|
|
||||||
igImageGraph.prototype = {
|
|
||||||
__proto__: HTMLElement.prototype,
|
|
||||||
//constructor: igImageGraph,
|
|
||||||
|
|
||||||
get observedAttributes() {
|
|
||||||
return ['src'] },
|
|
||||||
|
|
||||||
get src(){},
|
|
||||||
set src(value){},
|
|
||||||
|
|
||||||
//attributeChangedCallback: function(name, from, to){
|
|
||||||
//},
|
|
||||||
|
|
||||||
//connectedCallback: function(){ },
|
|
||||||
//disconnectedCallback: function(){ },
|
|
||||||
|
|
||||||
}
|
|
||||||
/*/
|
|
||||||
class igImageGraph extends HTMLElement {
|
class igImageGraph extends HTMLElement {
|
||||||
|
graphs = {
|
||||||
|
waveform,
|
||||||
|
histogram,
|
||||||
|
}
|
||||||
|
modes = ['luminance', 'color', 'R', 'G', 'B']
|
||||||
|
color_modes = ['normalized', 'white', 'point']
|
||||||
|
|
||||||
constructor(src){
|
constructor(src){
|
||||||
super()
|
super()
|
||||||
|
|
||||||
// shadow DOM
|
// shadow DOM
|
||||||
var shadow = this.attachShadow({mode: 'open'})
|
var shadow = this.__shadow =
|
||||||
|
this.attachShadow({mode: 'open'})
|
||||||
shadow.appendChild(
|
shadow.appendChild(
|
||||||
document.getElementById('ig-image-graph')
|
document.getElementById('ig-image-graph')
|
||||||
.content.cloneNode(true))
|
.content.cloneNode(true)) }
|
||||||
|
connectedCallback(){
|
||||||
this.update()
|
this.update_controls()
|
||||||
}
|
this.update() }
|
||||||
|
|
||||||
// attributes...
|
// attributes...
|
||||||
get observedAttributes(){
|
get observedAttributes(){
|
||||||
@ -426,50 +406,203 @@ class igImageGraph extends HTMLElement {
|
|||||||
'src',
|
'src',
|
||||||
'mode',
|
'mode',
|
||||||
'color',
|
'color',
|
||||||
'controls',
|
'nocontrols',
|
||||||
|
'graph',
|
||||||
]}
|
]}
|
||||||
|
|
||||||
get src(){}
|
|
||||||
set src(value){}
|
|
||||||
|
|
||||||
get mode(){}
|
|
||||||
set mode(value){}
|
|
||||||
|
|
||||||
get color(){}
|
|
||||||
set color(value){}
|
|
||||||
|
|
||||||
get controls(){}
|
|
||||||
set controls(value){}
|
|
||||||
|
|
||||||
attributeChangedCallback(name, from, to){
|
attributeChangedCallback(name, from, to){
|
||||||
this.update()
|
name == 'nocontrols'
|
||||||
|
&& this.update_controls()
|
||||||
|
this.update() }
|
||||||
|
get graph(){
|
||||||
|
return this.getAttribute('graph') || 'waveform' }
|
||||||
|
set graph(value){
|
||||||
|
value in this.graphs
|
||||||
|
&& this.setAttribute('graph', value)
|
||||||
|
value == ''
|
||||||
|
&& this.removeAttribute('graph')
|
||||||
|
this.update() }
|
||||||
|
get src(){
|
||||||
|
return this.getAttribute('src') }
|
||||||
|
set src(value){
|
||||||
|
var that = this
|
||||||
|
this.__update_handler = this.__update_handler
|
||||||
|
|| this.update.bind(this)
|
||||||
|
var url = typeof(value) == typeof('str')
|
||||||
|
// get/create image...
|
||||||
|
var img = this.image =
|
||||||
|
url ?
|
||||||
|
(this.image || document.createElement('img'))
|
||||||
|
: value
|
||||||
|
img.removeEventListener('load', this.__update_handler)
|
||||||
|
img.addEventListener('load', this.__update_handler)
|
||||||
|
// set .src and img.src...
|
||||||
|
this.setAttribute('src',
|
||||||
|
url ?
|
||||||
|
(img.src = value)
|
||||||
|
: img.src)
|
||||||
|
}
|
||||||
|
get mode(){
|
||||||
|
return this.getAttribute('mode') || 'color' }
|
||||||
|
set mode(value){
|
||||||
|
this.modes.includes(value)
|
||||||
|
&& this.setAttribute('mode', value)
|
||||||
|
value === undefined
|
||||||
|
&& this.removeAttribute('color')
|
||||||
|
this.update_controls()
|
||||||
|
this.update() }
|
||||||
|
get color(){
|
||||||
|
return this.getAttribute('color') || 'normalized' }
|
||||||
|
set color(value){
|
||||||
|
this.color_modes.includes(value)
|
||||||
|
&& this.setAttribute('color', value)
|
||||||
|
value === undefined
|
||||||
|
&& this.removeAttribute('color')
|
||||||
|
this.update() }
|
||||||
|
get nocontrols(){
|
||||||
|
return this.getAttribute('nocontrols') != null }
|
||||||
|
set nocontrols(value){
|
||||||
|
value ?
|
||||||
|
this.setAttribute('nocontrols', '')
|
||||||
|
: this.removeAttribute('nocontrols')
|
||||||
|
this.update_controls()
|
||||||
|
this.update() }
|
||||||
|
|
||||||
|
// API...
|
||||||
|
update_controls(){
|
||||||
|
var that = this
|
||||||
|
var mode = this.mode
|
||||||
|
|
||||||
|
var controls = this.__shadow.querySelector('.controls')
|
||||||
|
controls.innerHTML = ''
|
||||||
|
// modes...
|
||||||
|
var buttons = (this.nocontrols ?
|
||||||
|
[]
|
||||||
|
: this.modes)
|
||||||
|
// mode buttons...
|
||||||
|
.map(function(m){
|
||||||
|
var button = document.createElement('button')
|
||||||
|
button.innerText = m
|
||||||
|
button.classList.add(m, ...(m == mode ? ['current'] : []))
|
||||||
|
button.onclick = function(){
|
||||||
|
that.mode = m }
|
||||||
|
return button })
|
||||||
|
.concat([
|
||||||
|
/*
|
||||||
|
// color mode switch...
|
||||||
|
function(){
|
||||||
|
var button = document.createElement('button')
|
||||||
|
button.innerText = '('+ that.color[0] +')'
|
||||||
|
button.onclick = function(){
|
||||||
|
that.color = that.color_modes[
|
||||||
|
(that.color_modes.indexOf(that.color) + 1)
|
||||||
|
% that.color_modes.length]
|
||||||
|
this.innerText = '('+ that.color[0] +')' }
|
||||||
|
return button }(),
|
||||||
|
//*/
|
||||||
|
// reload...
|
||||||
|
function(){
|
||||||
|
var button = document.createElement('button')
|
||||||
|
button.classList.add('update')
|
||||||
|
button.innerHTML = '⟳'
|
||||||
|
button.onclick = function(){ that.update() }
|
||||||
|
return button }(),
|
||||||
|
])
|
||||||
|
.reverse()
|
||||||
|
.forEach(function(button){
|
||||||
|
controls.appendChild(button) })
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
update(){
|
||||||
|
var that = this
|
||||||
|
var mode = this.mode
|
||||||
|
|
||||||
|
// controls...
|
||||||
|
// remove...
|
||||||
|
if(!this.nocontrols){
|
||||||
|
var controls = this.__shadow.querySelector('.controls')
|
||||||
|
// current button state...
|
||||||
|
var button = controls.querySelector('button.'+this.mode)
|
||||||
|
button
|
||||||
|
&& button.classList.add('current')
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX configurable...
|
||||||
|
var type = this.graph
|
||||||
|
var graph = this.graphs[type]
|
||||||
|
|
||||||
|
var canvas = this.__shadow.querySelector('canvas')
|
||||||
|
|
||||||
|
if(this.image){
|
||||||
|
graph(this.image, canvas, this.mode, this.color)
|
||||||
|
|
||||||
|
} else if(this.src){
|
||||||
|
this.src = this.src
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
// events...
|
// events...
|
||||||
// XXX
|
// XXX
|
||||||
|
|
||||||
|
|
||||||
// API...
|
|
||||||
update(){
|
|
||||||
// XXX
|
|
||||||
// - add/update/remove controls
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//*/
|
|
||||||
window.customElements.define('ig-image-graph', igImageGraph)
|
window.customElements.define('ig-image-graph', igImageGraph)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<template id="ig-image-graph">
|
<template id="ig-image-graph">
|
||||||
<style>
|
<style>
|
||||||
|
:host {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
background: black;
|
||||||
|
|
||||||
|
width: attr(image-width);
|
||||||
|
height: attr(graph-height);
|
||||||
|
}
|
||||||
|
:host canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
:host .controls {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
right: 2px;
|
||||||
|
left: 2px;
|
||||||
|
}
|
||||||
|
:host .controls button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
opacity: 0.7;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
:host .controls button.current {
|
||||||
|
text-decoration: underline;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
:host .controls button.R:hover,
|
||||||
|
:host .controls button.current.R {
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
:host .controls button.G:hover,
|
||||||
|
:host .controls button.current.G {
|
||||||
|
background: green;
|
||||||
|
}
|
||||||
|
:host .controls button.B:hover,
|
||||||
|
:host .controls button.current.B {
|
||||||
|
background: blue;
|
||||||
|
}
|
||||||
|
:host .controls button:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<canvas class="graph"></canvas>
|
<canvas class="graph"></canvas>
|
||||||
|
<div class="controls"></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
@ -481,11 +614,11 @@ window.customElements.define('ig-image-graph', igImageGraph)
|
|||||||
<br>
|
<br>
|
||||||
|
|
||||||
<ig-image-graph
|
<ig-image-graph
|
||||||
|
graph="histogram"
|
||||||
src="../images/splash-800x500.jpg"
|
src="../images/splash-800x500.jpg"
|
||||||
graph="waveform"
|
|
||||||
mode="color"
|
mode="color"
|
||||||
color="normalized"
|
color="normalized"
|
||||||
controls="yes" />
|
></ig-image-graph>
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user