1// Copyright (c) 2012 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 7/** 8 * @fileoverview Renders an array of slices into the provided div, 9 * using a child canvas element. Uses a FastRectRenderer to draw only 10 * the visible slices. 11 */ 12 13base.requireStylesheet('tracing.tracks.track'); 14 15base.require('ui'); 16base.require('ui.container_that_decorates_its_children'); 17base.require('tracing.color_scheme'); 18 19base.exportTo('tracing.tracks', function() { 20 var highlightIdBoost = tracing.getColorPaletteHighlightIdBoost(); 21 22 /** 23 * The base class for all tracks. 24 * @constructor 25 */ 26 var Track = ui.define('track', ui.ContainerThatDecoratesItsChildren); 27 Track.prototype = { 28 __proto__: ui.ContainerThatDecoratesItsChildren.prototype, 29 30 decorate: function(viewport) { 31 ui.ContainerThatDecoratesItsChildren.prototype.decorate.call(this); 32 if (viewport === undefined) 33 throw new Error('viewport is required when creating a Track.'); 34 35 this.viewport_ = viewport; 36 this.classList.add('track'); 37 this.categoryFilter_ = undefined; 38 }, 39 40 get viewport() { 41 return this.viewport_; 42 }, 43 44 context: function() { 45 // This is a little weird here, but we have to be able to walk up the 46 // parent tree to get the context. 47 if (!this.parentNode) 48 return undefined; 49 if (!this.parentNode.context) 50 throw new Error('Parent container does not support context() method.'); 51 return this.parentNode.context(); 52 }, 53 54 get categoryFilter() { 55 return this.categoryFilter_; 56 }, 57 58 set categoryFilter(categoryFilter) { 59 if (this.categoryFilter_ == categoryFilter) 60 return; 61 this.categoryFilter_ = categoryFilter; 62 this.updateContents_(); 63 }, 64 65 decorateChild_: function(childTrack) { 66 if (childTrack instanceof Track) 67 childTrack.categoryFilter = this.categoryFilter; 68 }, 69 70 undecorateChild_: function(childTrack) { 71 if (childTrack.detach) 72 childTrack.detach(); 73 }, 74 75 updateContents_: function() { 76 }, 77 78 drawTrack: function(type) { 79 var ctx = this.context(); 80 if (ctx === undefined) 81 return; 82 83 ctx.save(); 84 var worldBounds = this.setupCanvasForDraw_(); 85 this.draw(type, worldBounds.left, worldBounds.right); 86 ctx.restore(); 87 }, 88 89 draw: function(type, viewLWorld, viewRWorld) { 90 }, 91 92 setupCanvasForDraw_: function() { 93 var ctx = this.context(); 94 var pixelRatio = window.devicePixelRatio || 1; 95 var bounds = this.getBoundingClientRect(); 96 var canvasBounds = ctx.canvas.getBoundingClientRect(); 97 98 ctx.translate(0, pixelRatio * (bounds.top - canvasBounds.top)); 99 100 var viewLWorld = this.viewport.xViewToWorld(0); 101 var viewRWorld = this.viewport.xViewToWorld(bounds.width * pixelRatio); 102 103 return {left: viewLWorld, right: viewRWorld}; 104 }, 105 106 /** 107 * Called by all the addToSelection functions on the created selection 108 * hit objects. Override this function on parent classes to add 109 * context-specific information to the hit. 110 */ 111 decorateHit: function(hit) { 112 }, 113 114 addIntersectingItemsInRangeToSelection: function( 115 loVX, hiVX, loVY, hiVY, selection) { 116 117 var pixelRatio = window.devicePixelRatio || 1; 118 var viewPixWidthWorld = this.viewport.xViewVectorToWorld(1); 119 var loWX = this.viewport.xViewToWorld(loVX * pixelRatio); 120 var hiWX = this.viewport.xViewToWorld(hiVX * pixelRatio); 121 122 var clientRect = this.getBoundingClientRect(); 123 var a = Math.max(loVY, clientRect.top); 124 var b = Math.min(hiVY, clientRect.bottom); 125 if (a > b) 126 return; 127 128 this.addIntersectingItemsInRangeToSelectionInWorldSpace( 129 loWX, hiWX, viewPixWidthWorld, selection); 130 }, 131 132 addIntersectingItemsInRangeToSelectionInWorldSpace: function( 133 loWX, hiWX, viewPixWidthWorld, selection) { 134 }, 135 136 drawInstantEvents_: function(instantEvents, viewLWorld, viewRWorld) { 137 var ctx = this.context(); 138 var pixelRatio = window.devicePixelRatio || 1; 139 140 var bounds = this.getBoundingClientRect(); 141 var height = bounds.height * pixelRatio; 142 143 // Culling parameters. 144 var vp = this.viewport; 145 var pixWidth = vp.xViewVectorToWorld(1); 146 147 var palette = tracing.getColorPalette(); 148 149 // Begin rendering in world space. 150 ctx.save(); 151 vp.applyTransformToCanvas(ctx); 152 153 var tr = new tracing.FastRectRenderer(ctx, 2 * pixWidth, 2 * pixWidth, 154 palette); 155 tr.setYandH(0, height); 156 157 var lowInstantEvent = base.findLowIndexInSortedArray( 158 instantEvents, 159 function(instantEvent) { return instantEvent.start; }, 160 viewLWorld); 161 162 for (var i = lowInstantEvent; i < instantEvents.length; ++i) { 163 var instantEvent = instantEvents[i]; 164 var x = instantEvent.start; 165 if (x > viewRWorld) 166 break; 167 168 // Less than 0.001 causes short events to disappear when zoomed in. 169 var w = Math.max(instantEvent.duration, 0.001); 170 var colorId = instantEvent.selected ? 171 instantEvent.colorId + highlightIdBoost : 172 instantEvent.colorId; 173 174 // InstantEvent: draw a triangle. If zoomed too far, collapse 175 // into the FastRectRenderer. 176 if (pixWidth > 0.001) { 177 tr.fillRect(x, pixWidth, colorId); 178 } else { 179 ctx.fillStyle = palette[colorId]; 180 ctx.beginPath(); 181 ctx.moveTo(x - (4 * pixWidth), height); 182 ctx.lineTo(x, 0); 183 ctx.lineTo(x + (4 * pixWidth), height); 184 ctx.closePath(); 185 ctx.fill(); 186 } 187 } 188 tr.flush(); 189 ctx.restore(); 190 } 191 }; 192 193 return { 194 Track: Track 195 }; 196}); 197