blob: fcf03db6b27168e479bb4389c44714f43c7c775b [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2015 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<link rel="import" href="/extras/chrome/cc/picture.html">
<link rel="import" href="/ui/analysis/generic_object_view.html">
<link rel="import" href="/ui/base/drag_handle.html">
<link rel="import" href="/ui/base/info_bar.html">
<link rel="import" href="/ui/base/hotkey_controller.html">
<link rel="import" href="/ui/base/list_view.html">
<link rel="import" href="/ui/base/mouse_mode_selector.html">
<link rel="import" href="/ui/base/overlay.html">
<link rel="import" href="/ui/base/utils.html">
<link rel="import" href="/ui/extras/chrome/cc/picture_ops_list_view.html">
<template id="tr-ui-e-chrome-cc-display-item-debugger-template">
<style>
* /deep/ tr-ui-e-chrome-cc-display-item-debugger {
-webkit-flex: 1 1 auto;
display: -webkit-flex;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > left-panel {
-webkit-flex-direction: column;
display: -webkit-flex;
min-width: 300px;
overflow-y: auto;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > left-panel >
display-item-info {
-webkit-flex: 1 1 auto;
padding-top: 2px;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > left-panel >
display-item-info .title {
font-weight: bold;
margin-left: 5px;
margin-right: 5px;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > left-panel >
display-item-info .export {
margin: 5px;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > x-drag-handle {
-webkit-flex: 0 0 auto;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > right-panel {
-webkit-flex: 1 1 auto;
display: -webkit-flex;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > left-panel >
display-item-info > header {
border-bottom: 1px solid #555;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > left-panel >
display-item-info > .x-list-view > div {
border-bottom: 1px solid #555;
padding-top: 3px;
padding-bottom: 3px;
padding-left: 5px;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > left-panel >
display-item-info > .x-list-view > div:hover {
background-color: #f0f0f0;
cursor: pointer;
}
/*************************************************/
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > right-panel >
tr-ui-e-chrome-cc-picture-ops-list-view.hasPictureOps {
display: block;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > right-panel >
x-drag-handle.hasPictureOps {
display: block;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > right-panel >
tr-ui-e-chrome-cc-picture-ops-list-view {
display: none;
overflow-y: auto;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger > right-panel >
x-drag-handle {
display: none;
}
* /deep/ tr-ui-e-chrome-cc-display-item-debugger raster-area {
-webkit-flex: 1 1 auto;
background-color: #ddd;
min-height: 200px;
min-width: 200px;
overflow-y: auto;
padding-left: 5px;
}
</style>
<left-panel>
<display-item-info>
<header>
<span class='title'>Display Item List</span>
<span class='size'></span>
<div class='export'>
<input class='dlfilename' type='text' value='displayitemlist.json' />
<button class='dlexport'>Export display item list</button>
</div>
<div class='export'>
<input class='skpfilename' type='text' value='skpicture.skp' />
<button class='skpexport'>Export list as SkPicture</button>
</div>
</header>
</display-item-info>
</left-panel>
<right-panel>
<raster-area><canvas></canvas></raster-area>
</right-panel>
</template>
<script>
'use strict';
tr.exportTo('tr.ui.e.chrome.cc', function() {
var THIS_DOC = document.currentScript.ownerDocument;
/**
* DisplayItemDebugger is a view of a DisplayItemListSnapshot for inspecting
* a display item list and the pictures within it.
*
* @constructor
*/
var DisplayItemDebugger = tr.ui.b.define(
'tr-ui-e-chrome-cc-display-item-debugger');
DisplayItemDebugger.prototype = {
__proto__: HTMLUnknownElement.prototype,
decorate: function() {
var node = tr.ui.b.instantiateTemplate(
'#tr-ui-e-chrome-cc-display-item-debugger-template', THIS_DOC);
this.appendChild(node);
this.pictureAsImageData_ = undefined;
this.zoomScaleValue_ = 1;
this.sizeInfo_ = this.querySelector('.size');
this.rasterArea_ = this.querySelector('raster-area');
this.rasterCanvas_ = this.rasterArea_.querySelector('canvas');
this.rasterCtx_ = this.rasterCanvas_.getContext('2d');
this.trackMouse_();
this.displayItemInfo_ = this.querySelector('display-item-info');
this.displayItemInfo_.addEventListener(
'click', this.onDisplayItemInfoClick_.bind(this), false);
this.displayItemListView_ = new tr.ui.b.ListView();
this.displayItemListView_.addEventListener('selection-changed',
this.onDisplayItemListSelection_.bind(this));
this.displayItemInfo_.appendChild(this.displayItemListView_);
this.displayListFilename_ = this.querySelector('.dlfilename');
this.displayListExportButton_ = this.querySelector('.dlexport');
this.displayListExportButton_.addEventListener(
'click', this.onExportDisplayListClicked_.bind(this));
this.skpFilename_ = this.querySelector('.skpfilename');
this.skpExportButton_ = this.querySelector('.skpexport');
this.skpExportButton_.addEventListener(
'click', this.onExportSkPictureClicked_.bind(this));
var leftPanel = this.querySelector('left-panel');
var middleDragHandle = new tr.ui.b.DragHandle();
middleDragHandle.horizontal = false;
middleDragHandle.target = leftPanel;
var rightPanel = this.querySelector('right-panel');
this.infoBar_ = document.createElement('tr-ui-b-info-bar');
this.rasterArea_.insertBefore(this.infoBar_, this.rasterCanvas_);
this.insertBefore(middleDragHandle, rightPanel);
this.picture_ = undefined;
this.pictureOpsListView_ = new tr.ui.e.chrome.cc.PictureOpsListView();
rightPanel.insertBefore(this.pictureOpsListView_, this.rasterArea_);
this.pictureOpsListDragHandle_ = new tr.ui.b.DragHandle();
this.pictureOpsListDragHandle_.horizontal = false;
this.pictureOpsListDragHandle_.target = this.pictureOpsListView_;
rightPanel.insertBefore(this.pictureOpsListDragHandle_, this.rasterArea_);
},
get picture() {
return this.picture_;
},
set displayItemList(displayItemList) {
this.displayItemList_ = displayItemList;
this.picture = this.displayItemList_;
this.displayItemListView_.clear();
this.displayItemList_.items.forEach(function(item) {
var newListItem = document.createElement('div');
newListItem.innerText = item;
// FIXME: We should improve our output to better format this.
var text = item.skp64 ? item.name : item;
this.displayItemListView_.addItem(text);
}.bind(this));
},
set picture(picture) {
this.picture_ = picture;
// Hide the ops list if we are showing the "main" display item list.
var showOpsList = picture && picture !== this.displayItemList_;
this.updateDrawOpsList_(showOpsList);
if (picture) {
var size = this.getRasterCanvasSize_();
this.rasterCanvas_.width = size.width;
this.rasterCanvas_.height = size.height;
}
var bounds = this.rasterArea_.getBoundingClientRect();
var selectorBounds = this.mouseModeSelector_.getBoundingClientRect();
this.mouseModeSelector_.pos = {
x: (bounds.right - selectorBounds.width - 10),
y: bounds.top
};
this.rasterize_();
this.scheduleUpdateContents_();
},
getRasterCanvasSize_: function() {
var style = window.getComputedStyle(this.rasterArea_);
var width = parseInt(style.width);
var height = parseInt(style.height);
if (this.picture_) {
width = Math.max(width, this.picture_.layerRect.width);
height = Math.max(height, this.picture_.layerRect.height);
}
return {
width: width,
height: height
};
},
scheduleUpdateContents_: function() {
if (this.updateContentsPending_)
return;
this.updateContentsPending_ = true;
tr.b.requestAnimationFrameInThisFrameIfPossible(
this.updateContents_.bind(this)
);
},
updateContents_: function() {
this.updateContentsPending_ = false;
if (this.picture_) {
this.sizeInfo_.textContent = '(' +
this.picture_.layerRect.width + ' x ' +
this.picture_.layerRect.height + ')';
}
// Return if picture hasn't finished rasterizing.
if (!this.pictureAsImageData_)
return;
this.infoBar_.visible = false;
this.infoBar_.removeAllButtons();
if (this.pictureAsImageData_.error) {
this.infoBar_.message = 'Cannot rasterize...';
this.infoBar_.addButton('More info...', function(e) {
var overlay = new tr.ui.b.Overlay();
overlay.textContent = this.pictureAsImageData_.error;
overlay.visible = true;
e.stopPropagation();
return false;
}.bind(this));
this.infoBar_.visible = true;
}
this.drawPicture_();
},
drawPicture_: function() {
var size = this.getRasterCanvasSize_();
if (size.width !== this.rasterCanvas_.width)
this.rasterCanvas_.width = size.width;
if (size.height !== this.rasterCanvas_.height)
this.rasterCanvas_.height = size.height;
this.rasterCtx_.clearRect(0, 0, size.width, size.height);
if (!this.picture_ || !this.pictureAsImageData_.imageData)
return;
var imgCanvas = this.pictureAsImageData_.asCanvas();
var w = imgCanvas.width;
var h = imgCanvas.height;
this.rasterCtx_.drawImage(imgCanvas, 0, 0, w, h,
0, 0, w * this.zoomScaleValue_,
h * this.zoomScaleValue_);
},
rasterize_: function() {
if (this.picture_) {
this.picture_.rasterize(
{
showOverdraw: false
},
this.onRasterComplete_.bind(this));
}
},
onRasterComplete_: function(pictureAsImageData) {
this.pictureAsImageData_ = pictureAsImageData;
this.scheduleUpdateContents_();
},
onDisplayItemListSelection_: function(e) {
var selected = this.displayItemListView_.selectedElement;
if (!selected) {
this.picture = this.displayItemList_;
return;
}
var index = Array.prototype.indexOf.call(
this.displayItemListView_.children, selected);
var displayItem = this.displayItemList_.items[index];
if (displayItem && displayItem.skp64)
this.picture = new tr.e.cc.Picture(
displayItem.skp64, this.displayItemList_.layerRect);
else
this.picture = undefined;
},
onDisplayItemInfoClick_: function(e) {
if (e && e.target == this.displayItemInfo_) {
this.displayItemListView_.selectedElement = undefined;
}
},
updateDrawOpsList_: function(showOpsList) {
if (showOpsList) {
this.pictureOpsListView_.picture = this.picture_;
if (this.pictureOpsListView_.numOps > 0) {
this.pictureOpsListView_.classList.add('hasPictureOps');
this.pictureOpsListDragHandle_.classList.add('hasPictureOps');
}
} else {
this.pictureOpsListView_.classList.remove('hasPictureOps');
this.pictureOpsListDragHandle_.classList.remove('hasPictureOps');
}
},
trackMouse_: function() {
this.mouseModeSelector_ = document.createElement(
'tr-ui-b-mouse-mode-selector');
this.mouseModeSelector_.targetElement = this.rasterArea_;
this.rasterArea_.appendChild(this.mouseModeSelector_);
this.mouseModeSelector_.supportedModeMask =
tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;
this.mouseModeSelector_.mode = tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;
this.mouseModeSelector_.defaultMode = tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;
this.mouseModeSelector_.settingsKey = 'pictureDebugger.mouseModeSelector';
this.mouseModeSelector_.addEventListener('beginzoom',
this.onBeginZoom_.bind(this));
this.mouseModeSelector_.addEventListener('updatezoom',
this.onUpdateZoom_.bind(this));
this.mouseModeSelector_.addEventListener('endzoom',
this.onEndZoom_.bind(this));
},
onBeginZoom_: function(e) {
this.isZooming_ = true;
this.lastMouseViewPos_ = this.extractRelativeMousePosition_(e);
e.preventDefault();
},
onUpdateZoom_: function(e) {
if (!this.isZooming_)
return;
var currentMouseViewPos = this.extractRelativeMousePosition_(e);
// Take the distance the mouse has moved and we want to zoom at about
// 1/1000th of that speed. 0.01 feels jumpy. This could possibly be tuned
// more if people feel it's too slow.
this.zoomScaleValue_ +=
((this.lastMouseViewPos_.y - currentMouseViewPos.y) * 0.001);
this.zoomScaleValue_ = Math.max(this.zoomScaleValue_, 0.1);
this.drawPicture_();
this.lastMouseViewPos_ = currentMouseViewPos;
},
onEndZoom_: function(e) {
this.lastMouseViewPos_ = undefined;
this.isZooming_ = false;
e.preventDefault();
},
extractRelativeMousePosition_: function(e) {
return {
x: e.clientX - this.rasterArea_.offsetLeft,
y: e.clientY - this.rasterArea_.offsetTop
};
},
saveFile_: function(filename, rawData) {
if (!rawData)
return;
// Convert this String into an Uint8Array
var length = rawData.length;
var arrayBuffer = new ArrayBuffer(length);
var uint8Array = new Uint8Array(arrayBuffer);
for (var c = 0; c < length; c++)
uint8Array[c] = rawData.charCodeAt(c);
// Create a blob URL from the binary array.
var blob = new Blob([uint8Array], {type: 'application/octet-binary'});
var blobUrl = window.URL.createObjectURL(blob);
// Create a link and click on it.
var link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
link.href = blobUrl;
link.download = filename;
var event = document.createEvent('MouseEvents');
event.initMouseEvent(
'click', true, false, window, 0, 0, 0, 0, 0,
false, false, false, false, 0, null);
link.dispatchEvent(event);
},
onExportDisplayListClicked_: function() {
var rawData = JSON.stringify(this.displayItemList_.items);
this.saveFile_(this.displayListFilename_.value, rawData);
},
onExportSkPictureClicked_: function() {
var rawData = atob(this.picture_.getBase64SkpData());
this.saveFile_(this.skpFilename_.value, rawData);
}
};
return {
DisplayItemDebugger: DisplayItemDebugger
};
});
</script>