diff --git a/ui (gen4)/experiments/canvas-waveform.html b/ui (gen4)/experiments/canvas-waveform.html
index e68099b6..93676f68 100644
--- a/ui (gen4)/experiments/canvas-waveform.html
+++ b/ui (gen4)/experiments/canvas-waveform.html
@@ -113,7 +113,8 @@ Filters.grayscale = function(pixels, args){
}
// XXX need to resize this...
-Filters.histogram = function(pixels, mode){
+Filters.histogram = function(pixels, mode, color){
+ color = color || 'fill'
mode = mode || 'luminance'
var w = 255
@@ -140,35 +141,34 @@ Filters.histogram = function(pixels, mode){
} else {
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'){
- 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'){
- 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 pos = function(i, value){
return (
- // horixontal position...
+ // horizontal position...
i*4
// value vertical offset...
+ (255-Math.round(value*m))*w*4) }
+ // XXX would be nice to have an option to draw full columns...
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
- }
- })
+ while(j < od.length){
+ j += w*4
+ od[j] = 255
+ if(color == 'point'){
+ // correct for blue visibility...
+ mode != 'luminance'
+ && (i-2)%4 == 0
+ && (od[j-1] = od[j-2] = 180)
+ break } } })
return out
}
@@ -176,7 +176,7 @@ Filters.histogram = function(pixels, mode){
Filters.waveform = function(pixels, mode, color){
mode = mode || 'luminance'
- color = color || 'match'
+ color = color || 'normalized'
var w = pixels.width
@@ -266,20 +266,16 @@ Filters.waveform = function(pixels, mode, color){
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)
-}
+ 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)
-}
+ var w = Filters.histogram(d, mode, color)
+ Filters.setPixels(canvas, w) }
// XXX should we make this a web components???
// + would make everything transparent
@@ -382,43 +378,27 @@ var start = function(){
//---------------------------------------------------------------------
-
-/* XXX for some reason this does not work...
-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(){ },
-
-}
-/*/
+// XXX can't get the non-class version to work...
+// XXX for some reason this takes some time to draw... slower than makeWaveform(..)
class igImageGraph extends HTMLElement {
+ graphs = {
+ waveform,
+ histogram,
+ }
+ modes = ['luminance', 'color', 'R', 'G', 'B']
+ color_modes = ['normalized', 'white', 'point']
+
constructor(src){
super()
-
// shadow DOM
- var shadow = this.attachShadow({mode: 'open'})
+ var shadow = this.__shadow =
+ this.attachShadow({mode: 'open'})
shadow.appendChild(
document.getElementById('ig-image-graph')
- .content.cloneNode(true))
-
- this.update()
- }
+ .content.cloneNode(true)) }
+ connectedCallback(){
+ this.update_controls()
+ this.update() }
// attributes...
get observedAttributes(){
@@ -426,50 +406,203 @@ class igImageGraph extends HTMLElement {
'src',
'mode',
'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){
- 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...
// XXX
-
-
- // API...
- update(){
- // XXX
- // - add/update/remove controls
- }
}
-//*/
window.customElements.define('ig-image-graph', igImageGraph)
-
-
-
+
@@ -481,11 +614,11 @@ window.customElements.define('ig-image-graph', igImageGraph)
+>