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="/tracing/color_scheme.html">
9 <link rel="import" href="/tracing/elided_cache.html">
10 <link rel="import" href="/tvcm/sorted_array_utils.html">
16 * @fileoverview Provides various helper methods for drawing to a provided
19 tvcm.exportTo('tracing', function() {
20 var elidedTitleCache = new tracing.ElidedTitleCache();
21 var palette = tvcm.ui.getColorPalette();
22 var EventPresenter = tracing.EventPresenter;
25 * This value is used to allow for consistent style UI elements.
26 * Thread time visualisation uses a smaller rectangle that has this height.
29 var THIN_SLICE_HEIGHT = 4;
32 * This value is used to for performance considerations when drawing large
33 * zoomed out traces that feature cpu time in the slices. If the waiting
34 * width is less than the threshold, we only draw the rectangle as a solid.
37 var SLICE_WAITING_WIDTH_DRAW_THRESHOLD = 3;
40 * If the slice has mostly been waiting to be scheduled on the cpu, the
41 * wall clock will be far greater than the cpu clock. Draw the slice
42 * only as an idle slice, if the active width is not thicker than the
46 var SLICE_ACTIVE_WIDTH_DRAW_THRESHOLD = 1;
49 * Should we elide text on trace labels?
50 * Without eliding, text that is too wide isn't drawn at all.
51 * Disable if you feel this causes a performance problem.
52 * This is a default value that can be overridden in tracks for testing.
55 var SHOULD_ELIDE_TEXT = true;
58 * Draw the define line into |ctx|.
60 * @param {Context} ctx The context to draw into.
61 * @param {float} x1 The start x position of the line.
62 * @param {float} y1 The start y position of the line.
63 * @param {float} x2 The end x position of the line.
64 * @param {float} y2 The end y position of the line.
66 function drawLine(ctx, x1, y1, x2, y2) {
72 * Draw the defined triangle into |ctx|.
74 * @param {Context} ctx The context to draw into.
75 * @param {float} x1 The first corner x.
76 * @param {float} y1 The first corner y.
77 * @param {float} x2 The second corner x.
78 * @param {float} y2 The second corner y.
79 * @param {float} x3 The third corner x.
80 * @param {float} y3 The third corner y.
82 function drawTriangle(ctx, x1, y1, x2, y2, x3, y3) {
91 * Draw an arrow into |ctx|.
93 * @param {Context} ctx The context to draw into.
94 * @param {float} x1 The shaft x.
95 * @param {float} y1 The shaft y.
96 * @param {float} x2 The head x.
97 * @param {float} y2 The head y.
98 * @param {float} arrowLength The length of the head.
99 * @param {float} arrowWidth The width of the head.
101 function drawArrow(ctx, x1, y1, x2, y2, arrowLength, arrowWidth) {
104 var len = Math.sqrt(dx * dx + dy * dy);
105 var perc = (len - arrowLength) / len;
106 var bx = x1 + perc * dx;
107 var by = y1 + perc * dy;
110 var ax = uy * arrowWidth;
111 var ay = -ux * arrowWidth;
114 drawLine(ctx, x1, y1, x2, y2);
125 * Draw the provided slices to the screen.
127 * Each of the elements in |slices| must provide the follow methods:
133 * @param {Context} ctx The canvas context.
134 * @param {TimelineDrawTransform} dt The draw transform.
135 * @param {float} viewLWorld The left most point of the world viewport.
136 * @param {float} viewRWorld The right most point of the world viewport.
137 * @param {float} viewHeight The height of the viewport.
138 * @param {Array} slices The slices to draw.
139 * @param {bool} async Whether the slices are drawn with async style.
141 function drawSlices(ctx, dt, viewLWorld, viewRWorld, viewHeight, slices,
143 var pixelRatio = window.devicePixelRatio || 1;
144 var pixWidth = dt.xViewVectorToWorld(1);
145 var height = viewHeight * pixelRatio;
147 var darkRectHeight = THIN_SLICE_HEIGHT * pixelRatio;
149 // Not enough space for both colors, use light color only.
150 if (height < darkRectHeight)
153 var lightRectHeight = height - darkRectHeight;
155 // Begin rendering in world space.
157 dt.applyTransformToCanvas(ctx);
159 var tr = new tracing.FastRectRenderer(
160 ctx, 2 * pixWidth, 2 * pixWidth, palette);
161 tr.setYandH(0, height);
163 var lowSlice = tvcm.findLowIndexInSortedArray(
165 function(slice) { return slice.start + slice.duration; },
168 for (var i = lowSlice; i < slices.length; ++i) {
169 var slice = slices[i];
175 if (slice.duration > 0) {
176 w = Math.max(slice.duration, 0.001);
181 var colorId = EventPresenter.getSliceColorId(slice);
182 var alpha = EventPresenter.getSliceAlpha(slice, async);
183 var lightAlpha = alpha * 0.70;
185 // If cpuDuration is available, draw rectangles proportional to the
186 // amount of cpu time taken.
187 if (!slice.cpuDuration) {
188 // No cpuDuration available, draw using only one alpha.
189 tr.fillRect(x, w, colorId, alpha);
193 var activeWidth = w * (slice.cpuDuration / slice.duration);
194 var waitingWidth = w - activeWidth;
196 // Check if we have enough screen space to draw the whole slice, with
199 // Truncate the activeWidth to 0 if it is less than 'threshold' pixels.
200 if (activeWidth < SLICE_ACTIVE_WIDTH_DRAW_THRESHOLD * pixWidth) {
205 // Truncate the waitingWidth to 0 if it is less than 'threshold' pixels.
206 if (waitingWidth < SLICE_WAITING_WIDTH_DRAW_THRESHOLD * pixWidth) {
211 // We now draw the two rectangles making up the event slice.
212 // NOTE: The if statements are necessary for performance considerations.
213 // We do not want to force draws, if the width of the rectangle is 0.
215 // First draw the solid color, representing the 'active' part.
216 if (activeWidth > 0) {
217 tr.fillRect(x, activeWidth, colorId, alpha);
220 // Next draw the two toned 'idle' part.
221 // NOTE: Substracting pixWidth and drawing one extra pixel is done to
222 // prevent drawing artifacts. Without it, the two parts of the slice,
223 // ('active' and 'idle') may appear split apart.
224 if (waitingWidth > 0) {
225 // First draw the light toned top part.
226 tr.setYandH(0, lightRectHeight);
227 tr.fillRect(x + activeWidth - pixWidth,
228 waitingWidth + pixWidth, colorId, lightAlpha);
229 // Then the solid bottom half.
230 tr.setYandH(lightRectHeight, darkRectHeight);
231 tr.fillRect(x + activeWidth - pixWidth,
232 waitingWidth + pixWidth, colorId, alpha);
233 // Reset for the next slice.
234 tr.setYandH(0, height);
242 * Draw the provided instant slices as lines to the screen.
244 * Each of the elements in |slices| must provide the follow methods:
246 * * duration with value of 0.
250 * @param {Context} ctx The canvas context.
251 * @param {TimelineDrawTransform} dt The draw transform.
252 * @param {float} viewLWorld The left most point of the world viewport.
253 * @param {float} viewRWorld The right most point of the world viewport.
254 * @param {float} viewHeight The height of the viewport.
255 * @param {Array} slices The slices to draw.
256 * @param {Numer} lineWidthInPixels The width of the lines.
258 function drawInstantSlicesAsLines(
259 ctx, dt, viewLWorld, viewRWorld, viewHeight, slices, lineWidthInPixels) {
260 var pixelRatio = window.devicePixelRatio || 1;
261 var height = viewHeight * pixelRatio;
263 var pixWidth = dt.xViewVectorToWorld(1);
265 // Begin rendering in world space.
267 ctx.lineWidth = pixWidth * lineWidthInPixels;
268 dt.applyTransformToCanvas(ctx);
271 var lowSlice = tvcm.findLowIndexInSortedArray(
273 function(slice) { return slice.start; },
276 for (var i = lowSlice; i < slices.length; ++i) {
277 var slice = slices[i];
282 ctx.strokeStyle = EventPresenter.getInstantSliceColor(slice);
285 ctx.lineTo(x, height);
292 * Draws the labels for the given slices.
294 * The |slices| array must contain objects with the following API:
298 * * didNotFinish (optional)
300 * @param {Context} ctx The graphics context.
301 * @param {TimelineDrawTransform} dt The draw transform.
302 * @param {float} viewLWorld The left most point of the world viewport.
303 * @param {float} viewRWorld The right most point of the world viewport.
304 * @param {Array} slices The slices to label.
305 * @param {bool} async Whether the slice labels are drawn with async style.
307 function drawLabels(ctx, dt, viewLWorld, viewRWorld, slices, async) {
308 var pixelRatio = window.devicePixelRatio || 1;
309 var pixWidth = dt.xViewVectorToWorld(1);
313 ctx.textAlign = 'center';
314 ctx.textBaseline = 'top';
315 ctx.font = (10 * pixelRatio) + 'px sans-serif';
318 ctx.font = 'italic ' + ctx.font;
320 var lowSlice = tvcm.findLowIndexInSortedArray(
322 function(slice) { return slice.start + slice.duration; },
325 // Don't render text until until it is 20px wide
326 var quickDiscardThresshold = pixWidth * 20;
327 for (var i = lowSlice; i < slices.length; ++i) {
328 var slice = slices[i];
329 if (slice.start > viewRWorld)
332 if (slice.duration <= quickDiscardThresshold)
335 var title = slice.title +
336 (slice.didNotFinish ? ' (Did Not Finish)' : '');
338 var drawnTitle = title;
339 var drawnWidth = elidedTitleCache.labelWidth(ctx, drawnTitle);
340 var fullLabelWidth = elidedTitleCache.labelWidthWorld(
341 ctx, drawnTitle, pixWidth);
342 if (SHOULD_ELIDE_TEXT && fullLabelWidth > slice.duration) {
343 var elidedValues = elidedTitleCache.get(
345 drawnTitle, drawnWidth,
347 drawnTitle = elidedValues.string;
348 drawnWidth = elidedValues.width;
351 if (drawnWidth * pixWidth < slice.duration) {
352 ctx.fillStyle = EventPresenter.getTextColor(slice);
353 var cX = dt.xWorldToView(slice.start + 0.5 * slice.duration);
354 ctx.fillText(drawnTitle, cX, 2.5 * pixelRatio, drawnWidth);
361 drawSlices: drawSlices,
362 drawInstantSlicesAsLines: drawInstantSlicesAsLines,
363 drawLabels: drawLabels,
366 drawTriangle: drawTriangle,
367 drawArrow: drawArrow,
369 elidedTitleCache_: elidedTitleCache,
371 THIN_SLICE_HEIGHT: THIN_SLICE_HEIGHT