1 /* 2 * Copyright (C) 2014 Google 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 are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /** 32 * @constructor 33 * @extends {WebInspector.VBox} 34 */ 35 WebInspector.Layers3DView = function() 36 { 37 WebInspector.VBox.call(this); 38 this.element.classList.add("layers-3d-view"); 39 this._emptyView = new WebInspector.EmptyView(WebInspector.UIString("Not in the composited mode.\nConsider forcing composited mode in Settings.")); 40 this._canvasElement = this.element.createChild("canvas"); 41 this._transformController = new WebInspector.TransformController(this._canvasElement); 42 this._transformController.addEventListener(WebInspector.TransformController.Events.TransformChanged, this._update, this); 43 this._canvasElement.addEventListener("dblclick", this._onDoubleClick.bind(this), false); 44 this._canvasElement.addEventListener("mousedown", this._onMouseDown.bind(this), false); 45 this._canvasElement.addEventListener("mouseup", this._onMouseUp.bind(this), false); 46 this._canvasElement.addEventListener("mouseout", this._onMouseMove.bind(this), false); 47 this._canvasElement.addEventListener("mousemove", this._onMouseMove.bind(this), false); 48 this._canvasElement.addEventListener("contextmenu", this._onContextMenu.bind(this), false); 49 this._lastActiveObject = {}; 50 this._picturesForLayer = {}; 51 this._scrollRectQuadsForLayer = {}; 52 this._isVisible = {}; 53 this._layerTree = null; 54 WebInspector.settings.showPaintRects.addChangeListener(this._update, this); 55 } 56 57 /** @typedef {{layer: !WebInspector.Layer, scrollRectIndex: number}|{layer: !WebInspector.Layer}} */ 58 WebInspector.Layers3DView.ActiveObject; 59 60 /** @typedef {{color: !Array.<number>, borderColor: !Array.<number>, borderWidth: number}} */ 61 WebInspector.Layers3DView.LayerStyle; 62 63 /** @typedef {{layerId: string, rect: !Array.<number>, imageURL: string}} */ 64 WebInspector.Layers3DView.Tile; 65 66 /** 67 * @enum {string} 68 */ 69 WebInspector.Layers3DView.OutlineType = { 70 Hovered: "hovered", 71 Selected: "selected" 72 } 73 74 /** 75 * @enum {string} 76 */ 77 WebInspector.Layers3DView.Events = { 78 ObjectHovered: "ObjectHovered", 79 ObjectSelected: "ObjectSelected", 80 LayerSnapshotRequested: "LayerSnapshotRequested" 81 } 82 83 /** 84 * @enum {string} 85 */ 86 WebInspector.Layers3DView.ScrollRectTitles = { 87 RepaintsOnScroll: WebInspector.UIString("repaints on scroll"), 88 TouchEventHandler: WebInspector.UIString("touch event listener"), 89 WheelEventHandler: WebInspector.UIString("mousewheel event listener") 90 } 91 92 WebInspector.Layers3DView.FragmentShader = "\ 93 precision mediump float;\ 94 varying vec4 vColor;\ 95 varying vec2 vTextureCoord;\ 96 uniform sampler2D uSampler;\ 97 void main(void)\ 98 {\ 99 gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)) * vColor;\ 100 }"; 101 102 WebInspector.Layers3DView.VertexShader = "\ 103 attribute vec3 aVertexPosition;\ 104 attribute vec2 aTextureCoord;\ 105 attribute vec4 aVertexColor;\ 106 uniform mat4 uPMatrix;\ 107 varying vec2 vTextureCoord;\ 108 varying vec4 vColor;\ 109 void main(void)\ 110 {\ 111 gl_Position = uPMatrix * vec4(aVertexPosition, 1.0);\ 112 vColor = aVertexColor;\ 113 vTextureCoord = aTextureCoord;\ 114 }"; 115 116 WebInspector.Layers3DView.SelectedBackgroundColor = [20, 40, 110, 0.66]; 117 WebInspector.Layers3DView.BackgroundColor = [0, 0, 0, 0]; 118 WebInspector.Layers3DView.HoveredBorderColor = [0, 0, 255, 1]; 119 WebInspector.Layers3DView.SelectedBorderColor = [0, 255, 0, 1]; 120 WebInspector.Layers3DView.BorderColor = [0, 0, 0, 1]; 121 WebInspector.Layers3DView.ScrollRectBackgroundColor = [178, 0, 0, 0.4]; 122 WebInspector.Layers3DView.SelectedScrollRectBackgroundColor = [178, 0, 0, 0.6]; 123 WebInspector.Layers3DView.ScrollRectBorderColor = [178, 0, 0, 1]; 124 WebInspector.Layers3DView.BorderWidth = 1; 125 WebInspector.Layers3DView.SelectedBorderWidth = 2; 126 127 WebInspector.Layers3DView.LayerSpacing = 20; 128 WebInspector.Layers3DView.ScrollRectSpacing = 4; 129 130 WebInspector.Layers3DView.prototype = { 131 /** 132 * @param {function(!Array.<!WebInspector.KeyboardShortcut.Descriptor>, function(?Event=))} registerShortcutDelegate 133 */ 134 registerShortcuts: function(registerShortcutDelegate) 135 { 136 this._transformController.registerShortcuts(registerShortcutDelegate); 137 }, 138 139 onResize: function() 140 { 141 this._update(); 142 }, 143 144 willHide: function() 145 { 146 }, 147 148 wasShown: function() 149 { 150 if (this._needsUpdate) 151 this._update(); 152 }, 153 154 /** 155 * @param {!WebInspector.Layers3DView.OutlineType} type 156 * @param {?WebInspector.Layers3DView.ActiveObject} activeObject 157 */ 158 _setOutline: function(type, activeObject) 159 { 160 this._lastActiveObject[type] = activeObject; 161 this._update(); 162 }, 163 164 /** 165 * @param {?WebInspector.Layers3DView.ActiveObject} activeObject 166 */ 167 hoverObject: function(activeObject) 168 { 169 this._setOutline(WebInspector.Layers3DView.OutlineType.Hovered, activeObject); 170 }, 171 172 /** 173 * @param {?WebInspector.Layers3DView.ActiveObject} activeObject 174 */ 175 selectObject: function(activeObject) 176 { 177 this._setOutline(WebInspector.Layers3DView.OutlineType.Hovered, null); 178 this._setOutline(WebInspector.Layers3DView.OutlineType.Selected, activeObject); 179 }, 180 181 /** 182 * @param {!WebInspector.Layer} layer 183 * @param {string=} imageURL 184 */ 185 showImageForLayer: function(layer, imageURL) 186 { 187 this.setTiles([{layerId: layer.id(), rect: [0, 0, layer.width(), layer.height()], imageURL: imageURL}]) 188 }, 189 190 /** 191 * @param {!Array.<!WebInspector.Layers3DView.Tile>} tiles 192 */ 193 setTiles: function(tiles) 194 { 195 this._picturesForLayer = {}; 196 tiles.forEach(this._setTile, this); 197 }, 198 199 /** 200 * @param {!WebInspector.Layers3DView.Tile} tile 201 */ 202 _setTile: function(tile) 203 { 204 var texture = this._gl.createTexture(); 205 texture.image = new Image(); 206 texture.image.addEventListener("load", this._handleLoadedTexture.bind(this, texture, tile.layerId, tile.rect), false); 207 texture.image.src = tile.imageURL; 208 }, 209 210 /** 211 * @param {!Element} canvas 212 * @return {!Object} 213 */ 214 _initGL: function(canvas) 215 { 216 var gl = canvas.getContext("webgl"); 217 gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); 218 gl.enable(gl.BLEND); 219 gl.clearColor(0.0, 0.0, 0.0, 0.0); 220 gl.enable(gl.DEPTH_TEST); 221 return gl; 222 }, 223 224 /** 225 * @param {!Object} type 226 * @param {string} script 227 */ 228 _createShader: function(type, script) 229 { 230 var shader = this._gl.createShader(type); 231 this._gl.shaderSource(shader, script); 232 this._gl.compileShader(shader); 233 this._gl.attachShader(this._shaderProgram, shader); 234 }, 235 236 /** 237 * @param {string} attributeName 238 * @param {string} glName 239 */ 240 _enableVertexAttribArray: function(attributeName, glName) 241 { 242 this._shaderProgram[attributeName] = this._gl.getAttribLocation(this._shaderProgram, glName); 243 this._gl.enableVertexAttribArray(this._shaderProgram[attributeName]); 244 }, 245 246 _initShaders: function() 247 { 248 this._shaderProgram = this._gl.createProgram(); 249 this._createShader(this._gl.FRAGMENT_SHADER, WebInspector.Layers3DView.FragmentShader); 250 this._createShader(this._gl.VERTEX_SHADER, WebInspector.Layers3DView.VertexShader); 251 this._gl.linkProgram(this._shaderProgram); 252 this._gl.useProgram(this._shaderProgram); 253 254 this._shaderProgram.vertexPositionAttribute = this._gl.getAttribLocation(this._shaderProgram, "aVertexPosition"); 255 this._gl.enableVertexAttribArray(this._shaderProgram.vertexPositionAttribute); 256 this._shaderProgram.vertexColorAttribute = this._gl.getAttribLocation(this._shaderProgram, "aVertexColor"); 257 this._gl.enableVertexAttribArray(this._shaderProgram.vertexColorAttribute); 258 this._shaderProgram.textureCoordAttribute = this._gl.getAttribLocation(this._shaderProgram, "aTextureCoord"); 259 this._gl.enableVertexAttribArray(this._shaderProgram.textureCoordAttribute); 260 261 this._shaderProgram.pMatrixUniform = this._gl.getUniformLocation(this._shaderProgram, "uPMatrix"); 262 this._shaderProgram.samplerUniform = this._gl.getUniformLocation(this._shaderProgram, "uSampler"); 263 }, 264 265 _resizeCanvas: function() 266 { 267 this._canvasElement.width = this._canvasElement.offsetWidth * window.devicePixelRatio; 268 this._canvasElement.height = this._canvasElement.offsetHeight * window.devicePixelRatio; 269 this._gl.viewportWidth = this._canvasElement.width; 270 this._gl.viewportHeight = this._canvasElement.height; 271 }, 272 273 /** 274 * @return {!CSSMatrix} 275 */ 276 _calculateProjectionMatrix: function() 277 { 278 var scaleFactorForMargins = 1.2; 279 var viewport = this._layerTree.viewportSize(); 280 var baseWidth = viewport ? viewport.width : this._layerTree.contentRoot().width(); 281 var baseHeight = viewport ? viewport.height : this._layerTree.contentRoot().height(); 282 var canvasWidth = this._canvasElement.width; 283 var canvasHeight = this._canvasElement.height; 284 var scaleX = canvasWidth / baseWidth / scaleFactorForMargins; 285 var scaleY = canvasHeight / baseHeight / scaleFactorForMargins; 286 var viewScale = Math.min(scaleX, scaleY); 287 var scale = this._transformController.scale(); 288 var offsetX = this._transformController.offsetX() * window.devicePixelRatio; 289 var offsetY = this._transformController.offsetY() * window.devicePixelRatio; 290 var rotateX = this._transformController.rotateX(); 291 var rotateY = this._transformController.rotateY(); 292 return new WebKitCSSMatrix().translate(offsetX, offsetY, 0).scale(scale, scale, scale).translate(canvasWidth / 2, canvasHeight / 2, 0) 293 .rotate(rotateX, rotateY, 0).scale(viewScale, viewScale, viewScale).translate(-baseWidth / 2, -baseHeight / 2, 0); 294 }, 295 296 _initProjectionMatrix: function() 297 { 298 this._pMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0) 299 .scale(2 / this._canvasElement.width, 2 / this._canvasElement.height, 1 / 1000000).multiply(this._calculateProjectionMatrix()); 300 this._gl.uniformMatrix4fv(this._shaderProgram.pMatrixUniform, false, this._arrayFromMatrix(this._pMatrix)); 301 }, 302 303 /** 304 * @param {!Object} texture 305 * @param {string} layerId 306 * @param {!Array.<number>} rect 307 */ 308 _handleLoadedTexture: function(texture, layerId, rect) 309 { 310 this._gl.bindTexture(this._gl.TEXTURE_2D, texture); 311 this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, true); 312 this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, this._gl.RGBA, this._gl.UNSIGNED_BYTE, texture.image); 313 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, this._gl.LINEAR); 314 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, this._gl.LINEAR); 315 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_S, this._gl.CLAMP_TO_EDGE); 316 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_T, this._gl.CLAMP_TO_EDGE); 317 this._gl.bindTexture(this._gl.TEXTURE_2D, null); 318 if (!this._picturesForLayer[layerId]) 319 this._picturesForLayer[layerId] = []; 320 this._picturesForLayer[layerId].push({texture: texture, rect: rect}); 321 this._update(); 322 }, 323 324 _initWhiteTexture: function() 325 { 326 this._whiteTexture = this._gl.createTexture(); 327 this._gl.bindTexture(this._gl.TEXTURE_2D, this._whiteTexture); 328 var whitePixel = new Uint8Array([255, 255, 255, 255]); 329 this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, 1, 1, 0, this._gl.RGBA, this._gl.UNSIGNED_BYTE, whitePixel); 330 }, 331 332 _initGLIfNecessary: function() 333 { 334 if (this._gl) 335 return this._gl; 336 this._gl = this._initGL(this._canvasElement); 337 this._initShaders(); 338 this._initWhiteTexture(); 339 return this._gl; 340 }, 341 342 /** 343 * @param {!CSSMatrix} m 344 * @return {!Float32Array} 345 */ 346 _arrayFromMatrix: function(m) 347 { 348 return new Float32Array([m.m11, m.m12, m.m13, m.m14, m.m21, m.m22, m.m23, m.m24, m.m31, m.m32, m.m33, m.m34, m.m41, m.m42, m.m43, m.m44]); 349 }, 350 351 /** 352 * @param {!Array.<number>} color 353 * @return {!Array.<number>} 354 */ 355 _makeColorsArray: function(color) 356 { 357 var colors = []; 358 var normalizedColor = [color[0] / 255, color[1] / 255, color[2] / 255, color[3]]; 359 for (var i = 0; i < 4; i++) { 360 colors = colors.concat(normalizedColor); 361 } 362 return colors; 363 }, 364 365 /** 366 * @param {!Object} attribute 367 * @param {!Array.<number>} array 368 * @param {!number} length 369 */ 370 _setVertexAttribute: function(attribute, array, length) 371 { 372 var gl = this._gl; 373 var buffer = gl.createBuffer(); 374 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 375 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(array), gl.STATIC_DRAW); 376 gl.vertexAttribPointer(attribute, length, gl.FLOAT, false, 0, 0); 377 }, 378 379 /** 380 * @param {!Array.<number>} vertices 381 * @param {!Array.<number>} color 382 * @param {!Object} glMode 383 * @param {!Object=} texture 384 */ 385 _drawRectangle: function(vertices, color, glMode, texture) 386 { 387 this._setVertexAttribute(this._shaderProgram.vertexPositionAttribute, vertices, 3); 388 this._setVertexAttribute(this._shaderProgram.textureCoordAttribute, [0, 1, 1, 1, 1, 0, 0, 0], 2); 389 390 if (texture) { 391 var white = [255, 255, 255, 1]; 392 this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(white), white.length); 393 this._gl.activeTexture(this._gl.TEXTURE0); 394 this._gl.bindTexture(this._gl.TEXTURE_2D, texture); 395 this._gl.uniform1i(this._shaderProgram.samplerUniform, 0); 396 } else { 397 this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(color), color.length); 398 this._gl.bindTexture(this._gl.TEXTURE_2D, this._whiteTexture); 399 } 400 401 var numberOfVertices = 4; 402 this._gl.drawArrays(glMode, 0, numberOfVertices); 403 }, 404 405 /** 406 * @param {!WebInspector.Layers3DView.OutlineType} type 407 * @param {!WebInspector.Layer} layer 408 * @param {number=} scrollRectIndex 409 */ 410 _isObjectActive: function(type, layer, scrollRectIndex) 411 { 412 var activeObject = this._lastActiveObject[type]; 413 return activeObject && activeObject.layer && activeObject.layer.id() === layer.id() && (typeof scrollRectIndex !== "number" || activeObject.scrollRectIndex === scrollRectIndex); 414 }, 415 416 /** 417 * @param {!WebInspector.Layer} layer 418 * @return {!WebInspector.Layers3DView.LayerStyle} 419 */ 420 _styleForLayer: function(layer) 421 { 422 var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer); 423 var isHovered = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Hovered, layer); 424 var color = isSelected ? WebInspector.Layers3DView.SelectedBackgroundColor : WebInspector.Layers3DView.BackgroundColor; 425 var borderColor; 426 if (isSelected) 427 borderColor = WebInspector.Layers3DView.SelectedBorderColor; 428 else if (isHovered) 429 borderColor = WebInspector.Layers3DView.HoveredBorderColor; 430 else 431 borderColor = WebInspector.Layers3DView.BorderColor; 432 var borderWidth = isSelected ? WebInspector.Layers3DView.SelectedBorderWidth : WebInspector.Layers3DView.BorderWidth; 433 return {color: color, borderColor: borderColor, borderWidth: borderWidth}; 434 }, 435 436 /** 437 * @param {!Array.<number>} quad 438 * @param {number} z 439 * @return {!Array.<number>} 440 */ 441 _calculateVerticesForQuad: function(quad, z) 442 { 443 return [quad[0], quad[1], z, quad[2], quad[3], z, quad[4], quad[5], z, quad[6], quad[7], z]; 444 }, 445 446 /** 447 * Finds coordinates of point on layer quad, having offsets (ratioX * width) and (ratioY * height) 448 * from the left corner of the initial layer rect, where width and heigth are layer bounds. 449 * @param {!Array.<number>} quad 450 * @param {number} ratioX 451 * @param {number} ratioY 452 * @return {!Array.<number>} 453 */ 454 _calculatePointOnQuad: function(quad, ratioX, ratioY) 455 { 456 var x0 = quad[0]; 457 var y0 = quad[1]; 458 var x1 = quad[2]; 459 var y1 = quad[3]; 460 var x2 = quad[4]; 461 var y2 = quad[5]; 462 var x3 = quad[6]; 463 var y3 = quad[7]; 464 // Point on the first quad side clockwise 465 var firstSidePointX = x0 + ratioX * (x1 - x0); 466 var firstSidePointY = y0 + ratioX * (y1 - y0); 467 // Point on the third quad side clockwise 468 var thirdSidePointX = x3 + ratioX * (x2 - x3); 469 var thirdSidePointY = y3 + ratioX * (y2 - y3); 470 var x = firstSidePointX + ratioY * (thirdSidePointX - firstSidePointX); 471 var y = firstSidePointY + ratioY * (thirdSidePointY - firstSidePointY); 472 return [x, y]; 473 }, 474 475 /** 476 * @param {!WebInspector.Layer} layer 477 * @param {!DOMAgent.Rect} rect 478 * @return {!Array.<number>} 479 */ 480 _calculateRectQuad: function(layer, rect) 481 { 482 var quad = layer.quad(); 483 var rx1 = rect.x / layer.width(); 484 var rx2 = (rect.x + rect.width) / layer.width(); 485 var ry1 = rect.y / layer.height(); 486 var ry2 = (rect.y + rect.height) / layer.height(); 487 return this._calculatePointOnQuad(quad, rx1, ry1).concat(this._calculatePointOnQuad(quad, rx2, ry1)) 488 .concat(this._calculatePointOnQuad(quad, rx2, ry2)).concat(this._calculatePointOnQuad(quad, rx1, ry2)); 489 }, 490 491 /** 492 * @param {!WebInspector.Layer} layer 493 * @return {!Array.<!Array.<number>>} 494 */ 495 _calculateScrollRectQuadsForLayer: function(layer) 496 { 497 var quads = []; 498 for (var i = 0; i < layer.scrollRects().length; ++i) 499 quads.push(this._calculateRectQuad(layer, layer.scrollRects()[i].rect)); 500 return quads; 501 }, 502 503 /** 504 * @param {!WebInspector.Layer} layer 505 * @param {number} index 506 * @return {number} 507 */ 508 _calculateScrollRectDepth: function(layer, index) 509 { 510 return this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing + index * WebInspector.Layers3DView.ScrollRectSpacing + 1; 511 }, 512 513 /** 514 * @param {!WebInspector.Layer} layer 515 */ 516 _drawLayer: function(layer) 517 { 518 var gl = this._gl; 519 var vertices; 520 var style = this._styleForLayer(layer); 521 var layerDepth = this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing; 522 if (this._isVisible[layer.id()]) { 523 vertices = this._calculateVerticesForQuad(layer.quad(), layerDepth); 524 gl.lineWidth(style.borderWidth); 525 this._drawRectangle(vertices, style.borderColor, gl.LINE_LOOP); 526 gl.lineWidth(1); 527 } 528 this._scrollRectQuadsForLayer[layer.id()] = this._calculateScrollRectQuadsForLayer(layer); 529 var scrollRectQuads = this._scrollRectQuadsForLayer[layer.id()]; 530 for (var i = 0; i < scrollRectQuads.length; ++i) { 531 vertices = this._calculateVerticesForQuad(scrollRectQuads[i], this._calculateScrollRectDepth(layer, i)); 532 var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer, i); 533 var color = isSelected ? WebInspector.Layers3DView.SelectedScrollRectBackgroundColor : WebInspector.Layers3DView.ScrollRectBackgroundColor; 534 this._drawRectangle(vertices, color, gl.TRIANGLE_FAN); 535 this._drawRectangle(vertices, WebInspector.Layers3DView.ScrollRectBorderColor, gl.LINE_LOOP); 536 } 537 var tiles = this._picturesForLayer[layer.id()] || []; 538 for (var i = 0; i < tiles.length; ++i) { 539 var tile = tiles[i]; 540 var quad = this._calculateRectQuad(layer, {x: tile.rect[0], y: tile.rect[1], width: tile.rect[2] - tile.rect[0], height: tile.rect[3] - tile.rect[1]}); 541 vertices = this._calculateVerticesForQuad(quad, layerDepth); 542 this._drawRectangle(vertices, style.color, gl.TRIANGLE_FAN, tile.texture); 543 } 544 }, 545 546 _drawViewport: function() 547 { 548 var viewport = this._layerTree.viewportSize(); 549 var vertices = [0, 0, 0, viewport.width, 0, 0, viewport.width, viewport.height, 0, 0, viewport.height, 0]; 550 var color = [0, 0, 0, 1]; 551 this._gl.lineWidth(3.0); 552 this._drawRectangle(vertices, color, this._gl.LINE_LOOP); 553 this._gl.lineWidth(1.0); 554 }, 555 556 _calculateDepths: function() 557 { 558 this._depthByLayerId = {}; 559 this._isVisible = {}; 560 var depth = 0; 561 var root = this._layerTree.root(); 562 var queue = [root]; 563 this._depthByLayerId[root.id()] = 0; 564 this._isVisible[root.id()] = false; 565 while (queue.length > 0) { 566 var layer = queue.shift(); 567 var children = layer.children(); 568 for (var i = 0; i < children.length; ++i) { 569 this._depthByLayerId[children[i].id()] = ++depth; 570 this._isVisible[children[i].id()] = children[i] === this._layerTree.contentRoot() || this._isVisible[layer.id()]; 571 queue.push(children[i]); 572 } 573 } 574 }, 575 576 577 /** 578 * @param {?WebInspector.LayerTreeBase} layerTree 579 */ 580 setLayerTree: function(layerTree) 581 { 582 this._layerTree = layerTree; 583 this._update(); 584 }, 585 586 _update: function() 587 { 588 if (!this.isShowing()) { 589 this._needsUpdate = true; 590 return; 591 } 592 var contentRoot = this._layerTree && this._layerTree.contentRoot(); 593 if (!contentRoot || !this._layerTree.root()) { 594 this._emptyView.show(this.element); 595 return; 596 } 597 this._emptyView.detach(); 598 599 var gl = this._initGLIfNecessary(); 600 this._resizeCanvas(); 601 this._initProjectionMatrix(); 602 this._calculateDepths(); 603 604 gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); 605 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 606 607 if (this._layerTree.viewportSize()) 608 this._drawViewport(); 609 this._layerTree.forEachLayer(this._drawLayer.bind(this)); 610 }, 611 612 /** 613 * Intersects quad with given transform matrix and line l(t) = (x0, y0, t) 614 * @param {!Array.<number>} vertices 615 * @param {!CSSMatrix} matrix 616 * @param {!number} x0 617 * @param {!number} y0 618 * @return {(number|undefined)} 619 */ 620 _intersectLineAndRect: function(vertices, matrix, x0, y0) 621 { 622 var epsilon = 1e-8; 623 var i; 624 // Vertices of the quad with transform matrix applied 625 var points = []; 626 for (i = 0; i < 4; ++i) 627 points[i] = WebInspector.Geometry.multiplyVectorByMatrixAndNormalize(new WebInspector.Geometry.Vector(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]), matrix); 628 // Calculating quad plane normal 629 var normal = WebInspector.Geometry.crossProduct(WebInspector.Geometry.subtract(points[1], points[0]), WebInspector.Geometry.subtract(points[2], points[1])); 630 // General form of the equation of the quad plane: A * x + B * y + C * z + D = 0 631 var A = normal.x; 632 var B = normal.y; 633 var C = normal.z; 634 var D = -(A * points[0].x + B * points[0].y + C * points[0].z); 635 // Finding t from the equation 636 var t = -(D + A * x0 + B * y0) / C; 637 // Point of the intersection 638 var pt = new WebInspector.Geometry.Vector(x0, y0, t); 639 // Vectors from the intersection point to vertices of the quad 640 var tVects = points.map(WebInspector.Geometry.subtract.bind(null, pt)); 641 // Intersection point lies inside of the polygon if scalar products of normal of the plane and 642 // cross products of successive tVects are all nonstrictly above or all nonstrictly below zero 643 for (i = 0; i < tVects.length; ++i) { 644 var product = WebInspector.Geometry.scalarProduct(normal, WebInspector.Geometry.crossProduct(tVects[i], tVects[(i + 1) % tVects.length])); 645 if (product < 0) 646 return undefined; 647 } 648 return t; 649 }, 650 651 /** 652 * @param {?Event} event 653 * @return {?WebInspector.Layers3DView.ActiveObject} 654 */ 655 _activeObjectFromEventPoint: function(event) 656 { 657 if (!this._layerTree) 658 return null; 659 var closestIntersectionPoint = Infinity; 660 var closestLayer = null; 661 var projectionMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0).multiply(this._calculateProjectionMatrix()); 662 var x0 = (event.clientX - this._canvasElement.totalOffsetLeft()) * window.devicePixelRatio; 663 var y0 = -(event.clientY - this._canvasElement.totalOffsetTop()) * window.devicePixelRatio; 664 665 /** 666 * @param {!WebInspector.Layer} layer 667 * @this {WebInspector.Layers3DView} 668 */ 669 function checkIntersection(layer) 670 { 671 var t; 672 if (this._isVisible[layer.id()]) { 673 t = this._intersectLineAndRect(this._calculateVerticesForQuad(layer.quad(), this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing), projectionMatrix, x0, y0); 674 if (t < closestIntersectionPoint) { 675 closestIntersectionPoint = t; 676 closestLayer = {layer: layer}; 677 } 678 } 679 var scrollRectQuads = this._scrollRectQuadsForLayer[layer.id()]; 680 for (var i = 0; i < scrollRectQuads.length; ++i) { 681 t = this._intersectLineAndRect(this._calculateVerticesForQuad(scrollRectQuads[i], this._calculateScrollRectDepth(layer, i)), projectionMatrix, x0, y0); 682 if (t < closestIntersectionPoint) { 683 closestIntersectionPoint = t; 684 closestLayer = {layer: layer, scrollRectIndex: i}; 685 } 686 } 687 } 688 689 this._layerTree.forEachLayer(checkIntersection.bind(this)); 690 return closestLayer; 691 }, 692 693 /** 694 * @param {?Event} event 695 */ 696 _onContextMenu: function(event) 697 { 698 var activeObject = this._activeObjectFromEventPoint(event); 699 var node = activeObject && activeObject.layer && activeObject.layer.nodeForSelfOrAncestor(); 700 var contextMenu = new WebInspector.ContextMenu(event); 701 contextMenu.appendItem("Reset view", this._transformController._resetAndNotify.bind(this._transformController), false); 702 if (node) 703 contextMenu.appendApplicableItems(node); 704 contextMenu.show(); 705 }, 706 707 /** 708 * @param {?Event} event 709 */ 710 _onMouseMove: function(event) 711 { 712 if (event.which) 713 return; 714 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.ObjectHovered, this._activeObjectFromEventPoint(event)); 715 }, 716 717 /** 718 * @param {?Event} event 719 */ 720 _onMouseDown: function(event) 721 { 722 this._mouseDownX = event.clientX; 723 this._mouseDownY = event.clientY; 724 }, 725 726 /** 727 * @param {?Event} event 728 */ 729 _onMouseUp: function(event) 730 { 731 const maxDistanceInPixels = 6; 732 if (this._mouseDownX && Math.abs(event.clientX - this._mouseDownX) < maxDistanceInPixels && Math.abs(event.clientY - this._mouseDownY) < maxDistanceInPixels) 733 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.ObjectSelected, this._activeObjectFromEventPoint(event)); 734 delete this._mouseDownX; 735 delete this._mouseDownY; 736 }, 737 738 /** 739 * @param {?Event} event 740 */ 741 _onDoubleClick: function(event) 742 { 743 var object = this._activeObjectFromEventPoint(event); 744 if (object && object.layer) 745 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.LayerSnapshotRequested, object.layer); 746 event.stopPropagation(); 747 }, 748 749 __proto__: WebInspector.VBox.prototype 750 } 751