3 Copyright (c) 2013 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
8 <link rel="import" href="/cc/picture.html">
9 <link rel="import" href="/cc/picture_ops_chart_summary_view.html">
10 <link rel="import" href="/cc/picture_ops_chart_view.html">
11 <link rel="import" href="/cc/picture_ops_list_view.html">
12 <link rel="import" href="/tracing/analysis/generic_object_view.html">
13 <link rel="import" href="/tvcm/key_event_manager.html">
14 <link rel="import" href="/tvcm/ui/drag_handle.html">
15 <link rel="import" href="/tvcm/ui/info_bar.html">
16 <link rel="import" href="/tvcm/ui/list_view.html">
17 <link rel="import" href="/tvcm/ui/mouse_mode_selector.html">
18 <link rel="import" href="/tvcm/ui/overlay.html">
19 <link rel="import" href="/tvcm/utils.html">
21 <template id="picture-debugger-template">
24 -webkit-flex: 1 1 auto;
25 -webkit-flex-direction: row;
26 display: -webkit-flex;
29 picture-debugger > x-generic-object-view {
30 -webkit-flex-direction: column;
31 display: -webkit-flex;
35 picture-debugger > left-panel {
36 -webkit-flex-direction: column;
37 display: -webkit-flex;
41 picture-debugger > left-panel > picture-info {
42 -webkit-flex: 0 0 auto;
46 picture-debugger > left-panel > picture-info .title {
52 picture-debugger > x-drag-handle {
53 -webkit-flex: 0 0 auto;
56 picture-debugger .filename {
57 -webkit-user-select: text;
61 picture-debugger > right-panel {
62 -webkit-flex: 1 1 auto;
63 -webkit-flex-direction: column;
64 display: -webkit-flex;
67 picture-debugger > right-panel > picture-ops-chart-view {
74 /******************************************************************************/
77 background-color: #ddd;
88 <span class='title'>Skia Picture</span>
89 <span class='size'></span>
92 <input class='filename' type='text' value='skpicture.skp' />
93 <button class='export'>Export</button>
98 <picture-ops-chart-view></picture-ops-chart-view>
99 <raster-area><canvas></canvas></raster-area>
106 tvcm.exportTo('cc', function() {
107 var THIS_DOC = document.currentScript.ownerDocument;
110 * PictureDebugger is a view of a PictureSnapshot for inspecting
111 * the picture in detail. (e.g., timing information, etc.)
115 var PictureDebugger = tvcm.ui.define('picture-debugger');
117 PictureDebugger.prototype = {
118 __proto__: HTMLUnknownElement.prototype,
120 decorate: function() {
121 var node = tvcm.instantiateTemplate('#picture-debugger-template',
124 this.appendChild(node);
126 this.pictureAsImageData_ = undefined;
127 this.showOverdraw_ = false;
128 this.zoomScaleValue_ = 1;
130 this.sizeInfo_ = this.querySelector('.size');
131 this.rasterArea_ = this.querySelector('raster-area');
132 this.rasterCanvas_ = this.rasterArea_.querySelector('canvas');
133 this.rasterCtx_ = this.rasterCanvas_.getContext('2d');
135 this.filename_ = this.querySelector('.filename');
137 this.drawOpsChartSummaryView_ = new cc.PictureOpsChartSummaryView();
138 this.drawOpsChartView_ = new cc.PictureOpsChartView();
139 this.drawOpsChartView_.addEventListener(
140 'selection-changed', this.onChartBarClicked_.bind(this));
142 this.exportButton_ = this.querySelector('.export');
143 this.exportButton_.addEventListener(
144 'click', this.onSaveAsSkPictureClicked_.bind(this));
148 var overdrawCheckbox = tvcm.ui.createCheckBox(
149 this, 'showOverdraw',
150 'pictureView.showOverdraw', false,
153 var chartCheckbox = tvcm.ui.createCheckBox(
154 this, 'showSummaryChart',
155 'pictureView.showSummaryChart', false,
156 'Show timing summary');
158 var pictureInfo = this.querySelector('picture-info');
159 pictureInfo.appendChild(overdrawCheckbox);
160 pictureInfo.appendChild(chartCheckbox);
162 this.drawOpsView_ = new cc.PictureOpsListView();
163 this.drawOpsView_.addEventListener(
164 'selection-changed', this.onChangeDrawOps_.bind(this));
166 var leftPanel = this.querySelector('left-panel');
167 leftPanel.appendChild(this.drawOpsChartSummaryView_);
168 leftPanel.appendChild(this.drawOpsView_);
170 var middleDragHandle = new tvcm.ui.DragHandle();
171 middleDragHandle.horizontal = false;
172 middleDragHandle.target = leftPanel;
174 var rightPanel = this.querySelector('right-panel');
175 rightPanel.replaceChild(
176 this.drawOpsChartView_,
177 rightPanel.querySelector('picture-ops-chart-view'));
179 this.infoBar_ = new tvcm.ui.InfoBar();
180 this.rasterArea_.appendChild(this.infoBar_);
182 this.insertBefore(middleDragHandle, rightPanel);
184 this.picture_ = undefined;
186 tvcm.KeyEventManager.instance.addListener(
187 'keypress', this.onKeyPress_, this);
189 // Add a mutation observer so that when the view is resized we can
190 // update the chart summary view.
191 this.mutationObserver_ = new MutationObserver(
192 this.onMutation_.bind(this));
193 this.mutationObserver_.observe(leftPanel, { attributes: true });
196 onKeyPress_: function(e) {
197 if (e.keyCode == 'h'.charCodeAt(0)) {
198 this.moveSelectedOpBy(-1);
201 } else if (e.keyCode == 'l'.charCodeAt(0)) {
202 this.moveSelectedOpBy(1);
208 onMutation_: function(mutations) {
210 for (var m = 0; m < mutations.length; m++) {
211 // A style change would indicate that the element has resized
212 // so we should re-render the chart.
213 if (mutations[m].attributeName === 'style') {
214 this.drawOpsChartSummaryView_.requiresRedraw = true;
215 this.drawOpsChartSummaryView_.updateChartContents();
217 this.drawOpsChartView_.dimensionsHaveChanged = true;
218 this.drawOpsChartView_.updateChartContents();
224 onSaveAsSkPictureClicked_: function() {
225 // Decode base64 data into a String
226 var rawData = atob(this.picture_.getBase64SkpData());
228 // Convert this String into an Uint8Array
229 var length = rawData.length;
230 var arrayBuffer = new ArrayBuffer(length);
231 var uint8Array = new Uint8Array(arrayBuffer);
232 for (var c = 0; c < length; c++)
233 uint8Array[c] = rawData.charCodeAt(c);
235 // Create a blob URL from the binary array.
236 var blob = new Blob([uint8Array], {type: 'application/octet-binary'});
237 var blobUrl = window.webkitURL.createObjectURL(blob);
239 // Create a link and click on it. BEST API EVAR!
240 var link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
242 link.download = this.filename_.value;
243 var event = document.createEvent('MouseEvents');
244 event.initMouseEvent(
245 'click', true, false, window, 0, 0, 0, 0, 0,
246 false, false, false, false, 0, null);
247 link.dispatchEvent(event);
251 return this.picture_;
254 set picture(picture) {
255 this.drawOpsView_.picture = picture;
256 this.drawOpsChartView_.picture = picture;
257 this.drawOpsChartSummaryView_.picture = picture;
258 this.picture_ = picture;
260 this.exportButton_.disabled = !this.picture_.canSave;
263 var size = this.getRasterCanvasSize_();
264 this.rasterCanvas_.width = size.width;
265 this.rasterCanvas_.height = size.height;
268 var bounds = this.rasterArea_.getBoundingClientRect();
269 var selectorBounds = this.mouseModeSelector_.getBoundingClientRect();
270 this.mouseModeSelector_.pos = {
271 x: (bounds.right - selectorBounds.width - 10),
277 this.scheduleUpdateContents_();
280 getRasterCanvasSize_: function() {
281 var style = window.getComputedStyle(this.rasterArea_);
283 Math.max(parseInt(style.width), this.picture_.layerRect.width);
285 Math.max(parseInt(style.height), this.picture_.layerRect.height);
293 scheduleUpdateContents_: function() {
294 if (this.updateContentsPending_)
296 this.updateContentsPending_ = true;
297 tvcm.requestAnimationFrameInThisFrameIfPossible(
298 this.updateContents_.bind(this)
302 updateContents_: function() {
303 this.updateContentsPending_ = false;
306 this.sizeInfo_.textContent = '(' +
307 this.picture_.layerRect.width + ' x ' +
308 this.picture_.layerRect.height + ')';
311 this.drawOpsChartView_.updateChartContents();
312 this.drawOpsChartView_.scrollSelectedItemIntoViewIfNecessary();
314 // Return if picture hasn't finished rasterizing.
315 if (!this.pictureAsImageData_)
318 this.infoBar_.visible = false;
319 this.infoBar_.removeAllButtons();
320 if (this.pictureAsImageData_.error) {
321 this.infoBar_.message = 'Cannot rasterize...';
322 this.infoBar_.addButton('More info...', function(e) {
323 var overlay = new tvcm.ui.Overlay();
324 overlay.textContent = this.pictureAsImageData_.error;
325 overlay.visible = true;
329 this.infoBar_.visible = true;
335 drawPicture_: function() {
336 var size = this.getRasterCanvasSize_();
337 if (size.width !== this.rasterCanvas_.width)
338 this.rasterCanvas_.width = size.width;
339 if (size.height !== this.rasterCanvas_.height)
340 this.rasterCanvas_.heigth = size.height;
342 this.rasterCtx_.clearRect(0, 0, size.width, size.height);
344 if (!this.pictureAsImageData_.imageData)
347 var imgCanvas = this.pictureAsImageData_.asCanvas();
348 var w = imgCanvas.width;
349 var h = imgCanvas.height;
350 this.rasterCtx_.drawImage(imgCanvas, 0, 0, w, h,
351 0, 0, w * this.zoomScaleValue_,
352 h * this.zoomScaleValue_);
355 rasterize_: function() {
357 this.picture_.rasterize(
359 stopIndex: this.drawOpsView_.selectedOpIndex,
360 showOverdraw: this.showOverdraw_
362 this.onRasterComplete_.bind(this));
366 onRasterComplete_: function(pictureAsImageData) {
367 this.pictureAsImageData_ = pictureAsImageData;
368 this.scheduleUpdateContents_();
371 moveSelectedOpBy: function(increment) {
372 if (this.selectedOpIndex === undefined) {
373 this.selectedOpIndex = 0;
376 this.selectedOpIndex = tvcm.clamp(
377 this.selectedOpIndex + increment,
382 return this.drawOpsView_.numOps;
385 get selectedOpIndex() {
386 return this.drawOpsView_.selectedOpIndex;
389 set selectedOpIndex(index) {
390 this.drawOpsView_.selectedOpIndex = index;
391 this.drawOpsChartView_.selectedOpIndex = index;
394 onChartBarClicked_: function(e) {
395 this.drawOpsView_.selectedOpIndex =
396 this.drawOpsChartView_.selectedOpIndex;
399 onChangeDrawOps_: function(e) {
401 this.scheduleUpdateContents_();
403 this.drawOpsChartView_.selectedOpIndex =
404 this.drawOpsView_.selectedOpIndex;
407 set showOverdraw(v) {
408 this.showOverdraw_ = v;
412 set showSummaryChart(chartShouldBeVisible) {
413 if (chartShouldBeVisible)
414 this.drawOpsChartSummaryView_.show();
416 this.drawOpsChartSummaryView_.hide();
419 trackMouse_: function() {
420 this.mouseModeSelector_ = new tvcm.ui.MouseModeSelector(this.rasterArea_);
421 this.rasterArea_.appendChild(this.mouseModeSelector_);
423 this.mouseModeSelector_.supportedModeMask =
424 tvcm.ui.MOUSE_SELECTOR_MODE.ZOOM;
425 this.mouseModeSelector_.mode = tvcm.ui.MOUSE_SELECTOR_MODE.ZOOM;
426 this.mouseModeSelector_.defaultMode = tvcm.ui.MOUSE_SELECTOR_MODE.ZOOM;
427 this.mouseModeSelector_.settingsKey = 'pictureDebugger.mouseModeSelector';
429 this.mouseModeSelector_.addEventListener('beginzoom',
430 this.onBeginZoom_.bind(this));
431 this.mouseModeSelector_.addEventListener('updatezoom',
432 this.onUpdateZoom_.bind(this));
433 this.mouseModeSelector_.addEventListener('endzoom',
434 this.onEndZoom_.bind(this));
437 onBeginZoom_: function(e) {
438 this.isZooming_ = true;
440 this.lastMouseViewPos_ = this.extractRelativeMousePosition_(e);
445 onUpdateZoom_: function(e) {
446 if (!this.isZooming_)
449 var currentMouseViewPos = this.extractRelativeMousePosition_(e);
451 // Take the distance the mouse has moved and we want to zoom at about
452 // 1/1000th of that speed. 0.01 feels jumpy. This could possibly be tuned
453 // more if people feel it's too slow.
454 this.zoomScaleValue_ +=
455 ((this.lastMouseViewPos_.y - currentMouseViewPos.y) * 0.001);
456 this.zoomScaleValue_ = Math.max(this.zoomScaleValue_, 0.1);
460 this.lastMouseViewPos_ = currentMouseViewPos;
463 onEndZoom_: function(e) {
464 this.lastMouseViewPos_ = undefined;
465 this.isZooming_ = false;
469 extractRelativeMousePosition_: function(e) {
471 x: e.clientX - this.rasterArea_.offsetLeft,
472 y: e.clientY - this.rasterArea_.offsetTop
478 PictureDebugger: PictureDebugger