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_as_image_data.html">
9 <link rel="import" href="/cc/util.html">
10 <link rel="import" href="/tvcm/guid.html">
11 <link rel="import" href="/tvcm/rect.html">
12 <link rel="import" href="/tvcm/raf.html">
13 <link rel="import" href="/tracing/trace_model/object_instance.html">
18 tvcm.exportTo('cc', function() {
19 var ObjectSnapshot = tracing.trace_model.ObjectSnapshot;
21 // Number of pictures created. Used as an uniqueId because we are immutable.
23 var OPS_TIMING_ITERATIONS = 3;
25 function Picture(skp64, layerRect) {
27 this.layerRect_ = layerRect;
29 this.guid_ = tvcm.GUID.allocate();
38 return this.layerRect_;
45 getBase64SkpData: function() {
50 if (!PictureSnapshot.CanGetOps()) {
51 console.error(PictureSnapshot.HowToEnablePictureDebugging());
55 var ops = window.chrome.skiaBenchmarking.getOps({
58 layer_rect: this.layerRect_.toArray(),
63 console.error('Failed to get picture ops.');
68 getOpTimings: function() {
69 if (!PictureSnapshot.CanGetOpTimings()) {
70 console.error(PictureSnapshot.HowToEnablePictureDebugging());
74 var opTimings = window.chrome.skiaBenchmarking.getOpTimings({
77 layer_rect: this.layerRect_.toArray(),
82 console.error('Failed to get picture op timings.');
88 * Tag each op with the time it takes to rasterize.
90 * FIXME: We should use real statistics to get better numbers here, see
91 * https://code.google.com/p/trace-viewer/issues/detail?id=357
93 * @param {Array} ops Array of Skia operations.
94 * @return {Array} Skia ops where op.cmd_time contains the associated time
97 tagOpsWithTimings: function(ops) {
98 var opTimings = new Array();
99 for (var iteration = 0; iteration < OPS_TIMING_ITERATIONS; iteration++) {
100 opTimings[iteration] = this.getOpTimings();
101 if (!opTimings[iteration] || !opTimings[iteration].cmd_times)
103 if (opTimings[iteration].cmd_times.length != ops.length)
107 for (var opIndex = 0; opIndex < ops.length; opIndex++) {
108 var min = Number.MAX_VALUE;
109 for (var i = 0; i < OPS_TIMING_ITERATIONS; i++)
110 min = Math.min(min, opTimings[i].cmd_times[opIndex]);
111 ops[opIndex].cmd_time = min;
118 * Rasterize the picture.
120 * @param {{opt_stopIndex: number, params}} The SkPicture operation to
121 * rasterize up to. If not defined, the entire SkPicture is rasterized.
122 * @param {{opt_showOverdraw: bool, params}} Defines whether pixel overdraw
123 should be visualized in the image.
124 * @param {function(cc.PictureAsImageData)} The callback function that is
125 * called after rasterization is complete or fails.
127 rasterize: function(params, rasterCompleteCallback) {
128 if (!PictureSnapshot.CanRasterize() || !PictureSnapshot.CanGetOps()) {
129 rasterCompleteCallback(new cc.PictureAsImageData(
130 this, cc.PictureSnapshot.HowToEnablePictureDebugging()));
134 var raster = window.chrome.skiaBenchmarking.rasterize(
138 layer_rect: this.layerRect_.toArray(),
142 stop: params.stopIndex === undefined ? -1 : params.stopIndex,
143 overdraw: !!params.showOverdraw,
148 var canvas = document.createElement('canvas');
149 var ctx = canvas.getContext('2d');
150 canvas.width = raster.width;
151 canvas.height = raster.height;
152 var imageData = ctx.createImageData(raster.width, raster.height);
153 imageData.data.set(new Uint8ClampedArray(raster.data));
154 rasterCompleteCallback(new cc.PictureAsImageData(this, imageData));
156 var error = 'Failed to rasterize picture. ' +
157 'Your recording may be from an old Chrome version. ' +
158 'The SkPicture format is not backward compatible.';
159 rasterCompleteCallback(new cc.PictureAsImageData(this, error));
164 function LayeredPicture(pictures) {
165 this.guid_ = tvcm.GUID.allocate();
166 this.pictures_ = pictures;
167 this.layerRect_ = undefined;
170 LayeredPicture.prototype = {
171 __proto__: Picture.prototype,
178 return 'cc::LayeredPicture';
182 if (this.layerRect_ !== undefined)
183 return this.layerRect_;
192 for (var i = 0; i < this.pictures_.length; ++i) {
193 var rect = this.pictures_[i].layerRect;
194 this.layerRect_.x = Math.min(this.layerRect_.x, rect.x);
195 this.layerRect_.y = Math.min(this.layerRect_.y, rect.y);
196 this.layerRect_.width =
197 Math.max(this.layerRect_.width, rect.x + rect.width);
198 this.layerRect_.height =
199 Math.max(this.layerRect_.height, rect.y + rect.height);
201 return this.layerRect_;
208 getBase64SkpData: function() {
209 throw new Error('Not available with a LayeredPicture.');
214 for (var i = 0; i < this.pictures_.length; ++i)
215 ops = ops.concat(this.pictures_[i].getOps());
219 getOpTimings: function() {
220 var opTimings = this.pictures_[0].getOpTimings();
221 for (var i = 1; i < this.pictures_.length; ++i) {
222 var timings = this.pictures_[i].getOpTimings();
223 opTimings.cmd_times = opTimings.cmd_times.concat(timings.cmd_times);
224 opTimings.total_time += timings.total_time;
229 tagOpsWithTimings: function(ops) {
230 var opTimings = new Array();
231 for (var iteration = 0; iteration < OPS_TIMING_ITERATIONS; iteration++) {
232 opTimings[iteration] = this.getOpTimings();
233 if (!opTimings[iteration] || !opTimings[iteration].cmd_times)
237 for (var opIndex = 0; opIndex < ops.length; opIndex++) {
238 var min = Number.MAX_VALUE;
239 for (var i = 0; i < OPS_TIMING_ITERATIONS; i++)
240 min = Math.min(min, opTimings[i].cmd_times[opIndex]);
241 ops[opIndex].cmd_time = min;
246 rasterize: function(params, rasterCompleteCallback) {
247 this.picturesAsImageData_ = [];
248 var rasterCallback = function(pictureAsImageData) {
249 this.picturesAsImageData_.push(pictureAsImageData);
250 if (this.picturesAsImageData_.length !== this.pictures_.length)
253 var canvas = document.createElement('canvas');
254 var ctx = canvas.getContext('2d');
255 canvas.width = this.layerRect.width;
256 canvas.height = this.layerRect.height;
258 // TODO(dsinclair): Verify these finish in the order started.
259 // Do the rasterize calls run sync or asyn? As the imageData
260 // going to be in the same order as the pictures_ list?
261 for (var i = 0; i < this.picturesAsImageData_.length; ++i) {
262 ctx.putImageData(this.picturesAsImageData_[i].imageData,
263 this.pictures_[i].layerRect.x,
264 this.pictures_[i].layerRect.y);
266 this.picturesAsImageData_ = [];
268 rasterCompleteCallback(new cc.PictureAsImageData(this,
269 ctx.getImageData(this.layerRect.x, this.layerRect.y,
270 this.layerRect.width, this.layerRect.height)));
273 for (var i = 0; i < this.pictures_.length; ++i)
274 this.pictures_[i].rasterize(params, rasterCallback);
282 function PictureSnapshot() {
283 ObjectSnapshot.apply(this, arguments);
286 PictureSnapshot.HasSkiaBenchmarking = function() {
289 if (!window.chrome.skiaBenchmarking)
294 PictureSnapshot.CanRasterize = function() {
295 if (!PictureSnapshot.HasSkiaBenchmarking())
297 if (!window.chrome.skiaBenchmarking.rasterize)
302 PictureSnapshot.CanGetOps = function() {
303 if (!PictureSnapshot.HasSkiaBenchmarking())
305 if (!window.chrome.skiaBenchmarking.getOps)
310 PictureSnapshot.CanGetOpTimings = function() {
311 if (!PictureSnapshot.HasSkiaBenchmarking())
313 if (!window.chrome.skiaBenchmarking.getOpTimings)
318 PictureSnapshot.CanGetInfo = function() {
319 if (!PictureSnapshot.HasSkiaBenchmarking())
321 if (!window.chrome.skiaBenchmarking.getInfo)
326 PictureSnapshot.HowToEnablePictureDebugging = function() {
328 'For pictures to show up, you need to have Chrome running with ',
329 '--enable-skia-benchmarking. Please restart chrome with this flag ',
335 if (!window.chrome.skiaBenchmarking)
337 if (!window.chrome.skiaBenchmarking.rasterize)
338 return 'Your chrome is old';
339 if (!window.chrome.skiaBenchmarking.getOps)
340 return 'Your chrome is old: skiaBenchmarking.getOps not found';
341 if (!window.chrome.skiaBenchmarking.getOpTimings)
342 return 'Your chrome is old: skiaBenchmarking.getOpTimings not found';
343 if (!window.chrome.skiaBenchmarking.getInfo)
344 return 'Your chrome is old: skiaBenchmarking.getInfo not found';
345 return 'Rasterizing is on';
348 PictureSnapshot.prototype = {
349 __proto__: ObjectSnapshot.prototype,
351 preInitialize: function() {
352 cc.preInitializeObject(this);
353 this.rasterResult_ = undefined;
356 initialize: function() {
357 // If we have an alias args, that means this picture was represented
358 // by an alias, and the real args is in alias.args.
360 this.args = this.args.alias.args;
362 if (!this.args.params.layerRect)
363 throw new Error('Missing layer rect');
365 this.layerRect_ = this.args.params.layerRect;
366 this.picture_ = new Picture(this.args.skp64, this.args.params.layerRect);
369 set picture(picture) {
370 this.picture_ = picture;
374 return this.picture_.canSave;
378 return this.layerRect_ ? this.layerRect_ : this.picture_.layerRect;
382 return this.picture_.guid;
385 getBase64SkpData: function() {
386 return this.picture_.getBase64SkpData();
390 return this.picture_.getOps();
393 getOpTimings: function() {
394 return this.picture_.getOpTimings();
397 tagOpsWithTimings: function(ops) {
398 return this.picture_.tagOpsWithTimings(ops);
401 rasterize: function(params, rasterCompleteCallback) {
402 this.picture_.rasterize(params, rasterCompleteCallback);
406 ObjectSnapshot.register('cc::Picture', PictureSnapshot);
409 PictureSnapshot: PictureSnapshot,
411 LayeredPicture: LayeredPicture