1/* 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26WebInspector.ColorWheel = function() 27{ 28 WebInspector.Object.call(this); 29 30 this._rawCanvas = document.createElement("canvas"); 31 this._tintedCanvas = document.createElement("canvas"); 32 this._finalCanvas = document.createElement("canvas"); 33 34 this._crosshair = document.createElement("div"); 35 this._crosshair.className = "crosshair"; 36 37 this._element = document.createElement("div"); 38 this._element.className = "color-wheel"; 39 40 this._element.appendChild(this._finalCanvas); 41 this._element.appendChild(this._crosshair); 42 43 this._finalCanvas.addEventListener("mousedown", this); 44} 45 46WebInspector.ColorWheel.prototype = { 47 contructor: WebInspector.ColorWheel, 48 __proto__: WebInspector.Object.prototype, 49 50 // Public 51 52 set dimension(dimension) 53 { 54 this._finalCanvas.width = this._tintedCanvas.width = this._rawCanvas.width = dimension * window.devicePixelRatio; 55 this._finalCanvas.height = this._tintedCanvas.height = this._rawCanvas.height = dimension * window.devicePixelRatio; 56 57 this._finalCanvas.style.width = this._finalCanvas.style.height = dimension + "px"; 58 59 this._dimension = dimension; 60 // We shrink the radius a bit for better anti-aliasing. 61 this._radius = dimension / 2 - 2; 62 63 this._setCrosshairPosition(new WebInspector.Point(dimension / 2, dimension / 2)); 64 65 this._drawRawCanvas(); 66 this._draw(); 67 }, 68 69 get element() 70 { 71 return this._element; 72 }, 73 74 get brightness() 75 { 76 return this._brightness; 77 }, 78 79 set brightness(brightness) 80 { 81 this._brightness = brightness; 82 this._draw(); 83 }, 84 85 get tintedColor() 86 { 87 if (this._crosshairPosition) 88 return this._colorAtPointWithBrightness(this._crosshairPosition.x * window.devicePixelRatio, this._crosshairPosition.y * window.devicePixelRatio, this._brightness); 89 90 return new WebInspector.Color(WebInspector.Color.Format.RGBA, [0, 0, 0, 0]); 91 }, 92 93 set tintedColor(tintedColor) 94 { 95 var data = this._tintedColorToPointAndBrightness(tintedColor); 96 this._setCrosshairPosition(data.point); 97 this.brightness = data.brightness; 98 }, 99 100 get rawColor() 101 { 102 if (this._crosshairPosition) 103 return this._colorAtPointWithBrightness(this._crosshairPosition.x * window.devicePixelRatio, this._crosshairPosition.y * window.devicePixelRatio, 1); 104 105 return new WebInspector.Color(WebInspector.Color.Format.RGBA, [0, 0, 0, 0]); 106 }, 107 108 // Protected 109 110 handleEvent: function(event) 111 { 112 switch (event.type) { 113 case "mousedown": 114 this._handleMousedown(event); 115 break; 116 case "mousemove": 117 this._handleMousemove(event); 118 break; 119 case "mouseup": 120 this._handleMouseup(event); 121 break; 122 } 123 }, 124 125 // Private 126 127 _handleMousedown: function(event) 128 { 129 window.addEventListener("mousemove", this, true); 130 window.addEventListener("mouseup", this, true); 131 132 this._updateColorForMouseEvent(event); 133 }, 134 135 _handleMousemove: function(event) 136 { 137 this._updateColorForMouseEvent(event); 138 }, 139 140 _handleMouseup: function(event) 141 { 142 window.removeEventListener("mousemove", this, true); 143 window.removeEventListener("mouseup", this, true); 144 }, 145 146 _pointInCircleForEvent: function(event) 147 { 148 function distance(a, b) 149 { 150 return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)); 151 } 152 153 function angleFromCenterToPoint(center, point) 154 { 155 return Math.atan2(point.y - center.y, point.x - center.x); 156 } 157 158 function pointOnCircumference(c, r, a) 159 { 160 return new WebInspector.Point(c.x + r * Math.cos(a), c.y + r * Math.sin(a)); 161 } 162 163 var dimension = this._dimension; 164 var point = window.webkitConvertPointFromPageToNode(this._finalCanvas, new WebKitPoint(event.pageX, event.pageY)); 165 var center = new WebInspector.Point(dimension / 2, dimension / 2); 166 if (distance(point, center) > this._radius) { 167 var angle = angleFromCenterToPoint(center, point); 168 point = pointOnCircumference(center, this._radius, angle); 169 } 170 return point; 171 }, 172 173 _updateColorForMouseEvent: function(event) 174 { 175 var point = this._pointInCircleForEvent(event); 176 177 this._setCrosshairPosition(point); 178 179 if (this.delegate && typeof this.delegate.colorWheelColorDidChange === "function") 180 this.delegate.colorWheelColorDidChange(this); 181 }, 182 183 _setCrosshairPosition: function(point) 184 { 185 this._crosshairPosition = point; 186 this._crosshair.style.webkitTransform = "translate(" + Math.round(point.x) + "px, " + Math.round(point.y) + "px)"; 187 }, 188 189 _tintedColorToPointAndBrightness: function(color) 190 { 191 var rgb = color.rgb; 192 var hsv = WebInspector.Color.rgb2hsv(rgb[0], rgb[1], rgb[2]); 193 var cosHue = Math.cos(hsv[0] * Math.PI / 180); 194 var sinHue = Math.sin(hsv[0] * Math.PI / 180); 195 var center = this._dimension / 2; 196 var x = center + (center * cosHue * hsv[1]); 197 var y = center - (center * sinHue * hsv[1]); 198 return { 199 point: new WebInspector.Point(x, y), 200 brightness: hsv[2] 201 }; 202 }, 203 204 _drawRawCanvas: function() { 205 var ctx = this._rawCanvas.getContext("2d"); 206 207 var dimension = this._dimension * window.devicePixelRatio; 208 var center = dimension / 2; 209 210 ctx.fillStyle = "white"; 211 ctx.fillRect(0, 0, dimension, dimension); 212 213 var imageData = ctx.getImageData(0, 0, dimension, dimension); 214 var data = imageData.data; 215 for (var j = 0; j < dimension; ++j) { 216 for (var i = 0; i < dimension; ++i) { 217 var color = this._colorAtPointWithBrightness(i, j, 1); 218 if (!color) 219 continue; 220 var pos = (j * dimension + i) * 4; 221 data[pos] = color.rgb[0]; 222 data[pos + 1] = color.rgb[1]; 223 data[pos + 2] = color.rgb[2]; 224 } 225 } 226 ctx.putImageData(imageData, 0, 0); 227 }, 228 229 _colorAtPointWithBrightness: function(x, y, brightness) 230 { 231 var center = this._dimension / 2 * window.devicePixelRatio; 232 var xDis = x - center; 233 var yDis = y - center; 234 var distance = Math.sqrt(xDis * xDis + yDis * yDis); 235 236 if (distance - center > 0.001) 237 return new WebInspector.Color(WebInspector.Color.Format.RGBA, [0, 0, 0, 0]); 238 239 var h = Math.atan2(y - center, center - x) * 180 / Math.PI; 240 h = (h + 180) % 360; 241 var v = brightness; 242 var s = Math.max(0, distance) / center; 243 244 var rgb = WebInspector.Color.hsv2rgb(h, s, v); 245 return new WebInspector.Color(WebInspector.Color.Format.RGBA, [ 246 Math.round(rgb[0] * 255), 247 Math.round(rgb[1] * 255), 248 Math.round(rgb[2] * 255), 249 1 250 ]); 251 }, 252 253 _drawTintedCanvas: function() 254 { 255 var ctx = this._tintedCanvas.getContext("2d"); 256 var dimension = this._dimension * window.devicePixelRatio; 257 258 ctx.save(); 259 ctx.drawImage(this._rawCanvas, 0, 0, dimension, dimension); 260 if (this._brightness !== 1) { 261 ctx.globalAlpha = 1 - this._brightness; 262 ctx.fillStyle = "black"; 263 ctx.fillRect(0, 0, dimension, dimension); 264 } 265 ctx.restore(); 266 }, 267 268 _draw: function() 269 { 270 this._drawTintedCanvas(); 271 272 var ctx = this._finalCanvas.getContext("2d"); 273 var dimension = this._dimension * window.devicePixelRatio; 274 var radius = this._radius * window.devicePixelRatio; 275 276 ctx.save(); 277 ctx.clearRect(0, 0, dimension, dimension); 278 ctx.beginPath(); 279 ctx.arc(dimension / 2, dimension / 2, radius + 1, 0, Math.PI * 2, true); 280 ctx.closePath(); 281 ctx.clip(); 282 ctx.drawImage(this._tintedCanvas, 0, 0, dimension, dimension); 283 ctx.restore(); 284 } 285}; 286