mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-31 11:20:09 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			398 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			398 lines
		
	
	
		
			8.7 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???
 | |
| 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') }) } 
 | |
| 
 | |
| 	// 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(mode)
 | |
| 
 | |
| 	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')))
 | |
| }
 | |
| 
 | |
| </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>
 |