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 7base.requireStylesheet('tracing.tracks.counter_track'); 8 9base.require('tracing.tracks.heading_track'); 10base.require('tracing.color_scheme'); 11base.require('ui'); 12 13base.exportTo('tracing.tracks', function() { 14 15 var palette = tracing.getColorPalette(); 16 17 /** 18 * A track that displays a Counter object. 19 * @constructor 20 * @extends {HeadingTrack} 21 */ 22 23 var CounterTrack = 24 ui.define('counter-track', tracing.tracks.HeadingTrack); 25 26 CounterTrack.prototype = { 27 28 __proto__: tracing.tracks.HeadingTrack.prototype, 29 30 decorate: function(viewport) { 31 tracing.tracks.HeadingTrack.prototype.decorate.call(this, viewport); 32 this.classList.add('counter-track'); 33 this.selectedSamples_ = {}; 34 this.categoryFilter_ = new tracing.Filter(); 35 }, 36 37 /** 38 * Called by all the addToSelection functions on the created selection 39 * hit objects. Override this function on parent classes to add 40 * context-specific information to the hit. 41 */ 42 decorateHit: function(hit) { 43 }, 44 45 get counter() { 46 return this.counter_; 47 }, 48 49 set counter(counter) { 50 this.counter_ = counter; 51 this.heading = counter.name + ': '; 52 }, 53 54 get categoryFilter() { 55 return this.categoryFilter_; 56 }, 57 58 set categoryFilter(v) { 59 this.categoryFilter_ = v; 60 }, 61 62 /** 63 * @return {Object} A sparse, mutable map from sample index to bool. Samples 64 * indices the map that are true are drawn as selected. 65 */ 66 get selectedSamples() { 67 return this.selectedSamples_; 68 }, 69 70 draw: function(type, viewLWorld, viewRWorld) { 71 switch (type) { 72 case tracing.tracks.DrawType.SLICE: 73 this.drawSlices_(viewLWorld, viewRWorld); 74 break; 75 } 76 }, 77 78 drawSlices_: function(viewLWorld, viewRWorld) { 79 var ctx = this.context(); 80 var pixelRatio = window.devicePixelRatio || 1; 81 82 var bounds = this.getBoundingClientRect(); 83 var height = bounds.height * pixelRatio; 84 85 var counter = this.counter_; 86 87 // Culling parametrs. 88 var vp = this.viewport; 89 var pixWidth = vp.xViewVectorToWorld(1); 90 91 // Drop sampels that are less than skipDistancePix apart. 92 var skipDistancePix = 1; 93 var skipDistanceWorld = vp.xViewVectorToWorld(skipDistancePix); 94 95 // Begin rendering in world space. 96 ctx.save(); 97 vp.applyTransformToCanvas(ctx); 98 99 // Figure out where drawing should begin. 100 var numSeries = counter.numSeries; 101 var numSamples = counter.numSamples; 102 var startIndex = base.findLowIndexInSortedArray( 103 counter.timestamps, 104 function(x) { return x; }, 105 viewLWorld); 106 107 startIndex = startIndex - 1 > 0 ? startIndex - 1 : 0; 108 // Draw indices one by one until we fall off the viewRWorld. 109 var yScale = height / counter.maxTotal; 110 for (var seriesIndex = counter.numSeries - 1; 111 seriesIndex >= 0; seriesIndex--) { 112 var colorId = counter.series[seriesIndex].color; 113 ctx.fillStyle = palette[colorId]; 114 ctx.beginPath(); 115 116 // Set iLast and xLast such that the first sample we draw is the 117 // startIndex sample. 118 var iLast = startIndex - 1; 119 var xLast = iLast >= 0 ? 120 counter.timestamps[iLast] - skipDistanceWorld : -1; 121 var yLastView = height; 122 123 // Iterate over samples from iLast onward until we either fall off the 124 // viewRWorld or we run out of samples. To avoid drawing too much, after 125 // drawing a sample at xLast, skip subsequent samples that are less than 126 // skipDistanceWorld from xLast. 127 var hasMoved = false; 128 129 while (true) { 130 var i = iLast + 1; 131 if (i >= numSamples) { 132 ctx.lineTo(xLast, yLastView); 133 ctx.lineTo(xLast + 8 * pixWidth, yLastView); 134 ctx.lineTo(xLast + 8 * pixWidth, height); 135 break; 136 } 137 138 var x = counter.timestamps[i]; 139 var y = counter.totals[i * numSeries + seriesIndex]; 140 var yView = height - (yScale * y); 141 142 if (x > viewRWorld) { 143 ctx.lineTo(x, yLastView); 144 ctx.lineTo(x, height); 145 break; 146 } 147 148 if (i + 1 < numSamples) { 149 var xNext = counter.timestamps[i + 1]; 150 if (xNext - xLast <= skipDistanceWorld && xNext < viewRWorld) { 151 iLast = i; 152 continue; 153 } 154 } 155 156 if (!hasMoved) { 157 ctx.moveTo(viewLWorld, height); 158 hasMoved = true; 159 } 160 161 if (x - xLast < skipDistanceWorld) { 162 // We know that xNext > xLast + skipDistanceWorld, so we can 163 // safely move this sample's x over that much without passing 164 // xNext. This ensure that the previous sample is visible when 165 // zoomed out very far. 166 x = xLast + skipDistanceWorld; 167 } 168 ctx.lineTo(x, yLastView); 169 ctx.lineTo(x, yView); 170 171 iLast = i; 172 xLast = x; 173 yLastView = yView; 174 } 175 ctx.closePath(); 176 ctx.fill(); 177 } 178 179 ctx.fillStyle = 'rgba(255, 0, 0, 1)'; 180 for (var i in this.selectedSamples_) { 181 if (!this.selectedSamples_[i]) 182 continue; 183 184 var x = counter.timestamps[i]; 185 for (var seriesIndex = counter.numSeries - 1; 186 seriesIndex >= 0; seriesIndex--) { 187 var y = counter.totals[i * numSeries + seriesIndex]; 188 var yView = height - (yScale * y); 189 ctx.fillRect(x - pixWidth, yView - 1, 3 * pixWidth, 3); 190 } 191 } 192 ctx.restore(); 193 }, 194 195 addIntersectingItemsInRangeToSelectionInWorldSpace: function( 196 loWX, hiWX, viewPixWidthWorld, selection) { 197 198 function getSampleWidth(x, i) { 199 if (i === counter.timestamps.length - 1) 200 return 0; 201 return counter.timestamps[i + 1] - counter.timestamps[i]; 202 } 203 204 var counter = this.counter_; 205 var iLo = base.findLowIndexInSortedIntervals(counter.timestamps, 206 function(x) { return x; }, 207 getSampleWidth, 208 loWX); 209 var iHi = base.findLowIndexInSortedIntervals(counter.timestamps, 210 function(x) { return x; }, 211 getSampleWidth, 212 hiWX); 213 214 // Iterate over every sample intersecting.. 215 for (var i = iLo; i <= iHi; i++) { 216 if (i < 0) 217 continue; 218 if (i >= counter.timestamps.length) 219 continue; 220 221 // TODO(nduca): Pick the seriesIndexHit based on the loY - hiY values. 222 var hit = selection.addCounterSample(this, this.counter, i); 223 this.decorateHit(hit); 224 } 225 }, 226 227 addAllObjectsMatchingFilterToSelection: function(filter, selection) { 228 } 229 }; 230 231 return { 232 CounterTrack: CounterTrack 233 }; 234}); 235