1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5'use strict'; 6 7base.requireStylesheet('cc.picture_debugger'); 8 9base.require('cc.picture'); 10base.require('cc.picture_ops_list_view'); 11base.require('tracing.analysis.generic_object_view'); 12base.require('ui.drag_handle'); 13base.require('ui.info_bar'); 14base.require('ui.list_view'); 15base.require('ui.overlay'); 16 17base.exportTo('cc', function() { 18 19 /** 20 * PictureDebugger is a view of a PictureSnapshot for inspecting 21 * the picture in detail. (e.g., timing information, etc.) 22 * 23 * @constructor 24 */ 25 var PictureDebugger = ui.define('picture-debugger'); 26 27 PictureDebugger.prototype = { 28 __proto__: HTMLUnknownElement.prototype, 29 30 decorate: function() { 31 this.pictureAsCanvas_ = undefined; 32 33 this.leftPanel_ = document.createElement('left-panel'); 34 35 this.pictureInfo_ = document.createElement('picture-info'); 36 37 this.title_ = document.createElement('span'); 38 this.title_.textContent = 'Skia Picture'; 39 this.title_.classList.add('title'); 40 this.sizeInfo_ = document.createElement('span'); 41 this.sizeInfo_.classList.add('size'); 42 this.filename_ = document.createElement('input'); 43 this.filename_.classList.add('filename'); 44 this.filename_.type = 'text'; 45 this.filename_.value = 'skpicture.skp'; 46 var exportButton = document.createElement('button'); 47 exportButton.textContent = 'Export'; 48 exportButton.addEventListener( 49 'click', this.onSaveAsSkPictureClicked_.bind(this)); 50 this.pictureInfo_.appendChild(this.title_); 51 this.pictureInfo_.appendChild(this.sizeInfo_); 52 this.pictureInfo_.appendChild(document.createElement('br')); 53 this.pictureInfo_.appendChild(this.filename_); 54 this.pictureInfo_.appendChild(exportButton); 55 56 this.titleDragHandle_ = new ui.DragHandle(); 57 this.titleDragHandle_.horizontal = true; 58 this.titleDragHandle_.target = this.pictureInfo_; 59 60 this.drawOpsView_ = new cc.PictureOpsListView(); 61 this.drawOpsView_.addEventListener( 62 'selection-changed', this.onChangeDrawOps_.bind(this)); 63 64 this.leftPanel_.appendChild(this.pictureInfo_); 65 this.leftPanel_.appendChild(this.titleDragHandle_); 66 this.leftPanel_.appendChild(this.drawOpsView_); 67 68 this.middleDragHandle_ = new ui.DragHandle(); 69 this.middleDragHandle_.horizontal = false; 70 this.middleDragHandle_.target = this.leftPanel_; 71 72 this.infoBar_ = new ui.InfoBar(); 73 this.rasterArea_ = document.createElement('raster-area'); 74 75 this.appendChild(this.leftPanel_); 76 this.appendChild(this.middleDragHandle_); 77 this.rasterArea_.appendChild(this.infoBar_); 78 this.appendChild(this.rasterArea_); 79 80 this.picture_ = undefined; 81 }, 82 83 onSaveAsSkPictureClicked_: function() { 84 // Decode base64 data into a String 85 var rawData = atob(this.picture_.getBase64SkpData()); 86 87 // Convert this String into an Uint8Array 88 var length = rawData.length; 89 var arrayBuffer = new ArrayBuffer(length); 90 var uint8Array = new Uint8Array(arrayBuffer); 91 for (var c = 0; c < length; c++) 92 uint8Array[c] = rawData.charCodeAt(c); 93 94 // Create a blob URL from the binary array. 95 var blob = new Blob([uint8Array], {type: 'application/octet-binary'}); 96 var blobUrl = window.webkitURL.createObjectURL(blob); 97 98 // Create a link and click on it. BEST API EVAR! 99 var link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a'); 100 link.href = blobUrl; 101 link.download = this.filename_.value; 102 var event = document.createEvent('MouseEvents'); 103 event.initMouseEvent( 104 'click', true, false, window, 0, 0, 0, 0, 0, 105 false, false, false, false, 0, null); 106 link.dispatchEvent(event); 107 }, 108 109 get picture() { 110 return this.picture_; 111 }, 112 113 set picture(picture) { 114 this.drawOpsView_.picture = picture; 115 this.picture_ = picture; 116 this.rasterize_(); 117 118 this.scheduleUpdateContents_(); 119 }, 120 121 scheduleUpdateContents_: function() { 122 if (this.updateContentsPending_) 123 return; 124 this.updateContentsPending_ = true; 125 base.requestAnimationFrameInThisFrameIfPossible( 126 this.updateContents_.bind(this) 127 ); 128 }, 129 130 updateContents_: function() { 131 this.updateContentsPending_ = false; 132 133 if (this.picture_) { 134 this.sizeInfo_.textContent = '(' + 135 this.picture_.layerRect.width + ' x ' + 136 this.picture_.layerRect.height + ')'; 137 } 138 139 // Return if picture hasn't finished rasterizing. 140 if (!this.pictureAsCanvas_) 141 return; 142 143 this.infoBar_.visible = false; 144 this.infoBar_.removeAllButtons(); 145 if (this.pictureAsCanvas_.error) { 146 this.infoBar_.message = 'Cannot rasterize...'; 147 this.infoBar_.addButton('More info...', function() { 148 var overlay = new ui.Overlay(); 149 overlay.textContent = this.pictureAsCanvas_.error; 150 overlay.visible = true; 151 overlay.obeyCloseEvents = true; 152 }.bind(this)); 153 this.infoBar_.visible = true; 154 } 155 156 // FIXME(pdr): Append the canvas instead of using a background image. 157 if (this.pictureAsCanvas_.canvas) { 158 var imageUrl = this.pictureAsCanvas_.canvas.toDataURL(); 159 this.rasterArea_.style.backgroundImage = 'url("' + imageUrl + '")'; 160 } else { 161 this.rasterArea_.style.backgroundImage = ''; 162 } 163 }, 164 165 rasterize_: function() { 166 if (this.picture_) { 167 this.picture_.rasterize( 168 {stopIndex: this.drawOpsView_.selectedOpIndex}, 169 this.onRasterComplete_.bind(this)); 170 } 171 }, 172 173 onRasterComplete_: function(pictureAsCanvas) { 174 this.pictureAsCanvas_ = pictureAsCanvas; 175 this.scheduleUpdateContents_(); 176 }, 177 178 onChangeDrawOps_: function() { 179 this.rasterize_(); 180 this.scheduleUpdateContents_(); 181 } 182 }; 183 184 return { 185 PictureDebugger: PictureDebugger 186 }; 187}); 188