Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / trace_viewer / tracing / draw_helpers.html
1 <!DOCTYPE html>
2 <!--
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.
6 -->
7
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">
11
12 <script>
13 'use strict';
14
15 /**
16  * @fileoverview Provides various helper methods for drawing to a provided
17  * canvas.
18  */
19 tvcm.exportTo('tracing', function() {
20   var elidedTitleCache = new tracing.ElidedTitleCache();
21   var palette = tvcm.ui.getColorPalette();
22   var EventPresenter = tracing.EventPresenter;
23
24   /**
25    * This value is used to allow for consistent style UI elements.
26    * Thread time visualisation uses a smaller rectangle that has this height.
27    * @const
28    */
29   var THIN_SLICE_HEIGHT = 4;
30
31   /**
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.
35    * @const
36    */
37   var SLICE_WAITING_WIDTH_DRAW_THRESHOLD = 3;
38
39   /**
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
43    * threshold.
44    * @const
45    */
46   var SLICE_ACTIVE_WIDTH_DRAW_THRESHOLD = 1;
47
48   /**
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.
53    * @const
54    */
55   var SHOULD_ELIDE_TEXT = true;
56
57   /**
58    * Draw the define line into |ctx|.
59    *
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.
65    */
66   function drawLine(ctx, x1, y1, x2, y2) {
67     ctx.moveTo(x1, y1);
68     ctx.lineTo(x2, y2);
69   }
70
71   /**
72    * Draw the defined triangle into |ctx|.
73    *
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.
81    */
82   function drawTriangle(ctx, x1, y1, x2, y2, x3, y3) {
83     ctx.beginPath();
84     ctx.moveTo(x1, y1);
85     ctx.lineTo(x2, y2);
86     ctx.lineTo(x3, y3);
87     ctx.closePath();
88   }
89
90   /**
91    * Draw an arrow into |ctx|.
92    *
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.
100    */
101   function drawArrow(ctx, x1, y1, x2, y2, arrowLength, arrowWidth) {
102     var dx = x2 - x1;
103     var dy = y2 - y1;
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;
108     var ux = dx / len;
109     var uy = dy / len;
110     var ax = uy * arrowWidth;
111     var ay = -ux * arrowWidth;
112
113     ctx.beginPath();
114     drawLine(ctx, x1, y1, x2, y2);
115     ctx.stroke();
116
117     drawTriangle(ctx,
118         bx + ax, by + ay,
119         x2, y2,
120         bx - ax, by - ay);
121     ctx.fill();
122   }
123
124   /**
125    * Draw the provided slices to the screen.
126    *
127    * Each of the elements in |slices| must provide the follow methods:
128    *   * start
129    *   * duration
130    *   * colorId
131    *   * selected
132    *
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.
140    */
141   function drawSlices(ctx, dt, viewLWorld, viewRWorld, viewHeight, slices,
142                       async) {
143     var pixelRatio = window.devicePixelRatio || 1;
144     var pixWidth = dt.xViewVectorToWorld(1);
145     var height = viewHeight * pixelRatio;
146
147     var darkRectHeight = THIN_SLICE_HEIGHT * pixelRatio;
148
149     // Not enough space for both colors, use light color only.
150     if (height < darkRectHeight)
151       darkRectHeight = 0;
152
153     var lightRectHeight = height - darkRectHeight;
154
155     // Begin rendering in world space.
156     ctx.save();
157     dt.applyTransformToCanvas(ctx);
158
159     var tr = new tracing.FastRectRenderer(
160         ctx, 2 * pixWidth, 2 * pixWidth, palette);
161     tr.setYandH(0, height);
162
163     var lowSlice = tvcm.findLowIndexInSortedArray(
164         slices,
165         function(slice) { return slice.start + slice.duration; },
166         viewLWorld);
167
168     for (var i = lowSlice; i < slices.length; ++i) {
169       var slice = slices[i];
170       var x = slice.start;
171       if (x > viewRWorld)
172         break;
173
174       var w = pixWidth;
175       if (slice.duration > 0) {
176         w = Math.max(slice.duration, 0.001);
177         if (w < pixWidth)
178           w = pixWidth;
179       }
180
181       var colorId = EventPresenter.getSliceColorId(slice);
182       var alpha = EventPresenter.getSliceAlpha(slice, async);
183       var lightAlpha = alpha * 0.70;
184
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);
190         continue;
191       }
192
193       var activeWidth = w * (slice.cpuDuration / slice.duration);
194       var waitingWidth = w - activeWidth;
195
196       // Check if we have enough screen space to draw the whole slice, with
197       // both color tones.
198       //
199       // Truncate the activeWidth to 0 if it is less than 'threshold' pixels.
200       if (activeWidth < SLICE_ACTIVE_WIDTH_DRAW_THRESHOLD * pixWidth) {
201         activeWidth = 0;
202         waitingWidth = w;
203       }
204
205       // Truncate the waitingWidth to 0 if it is less than 'threshold' pixels.
206       if (waitingWidth < SLICE_WAITING_WIDTH_DRAW_THRESHOLD * pixWidth) {
207         activeWidth = w;
208         waitingWidth = 0;
209       }
210
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.
214       //
215       // First draw the solid color, representing the 'active' part.
216       if (activeWidth > 0) {
217         tr.fillRect(x, activeWidth, colorId, alpha);
218       }
219
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);
235       }
236     }
237     tr.flush();
238     ctx.restore();
239   }
240
241   /**
242    * Draw the provided instant slices as lines to the screen.
243    *
244    * Each of the elements in |slices| must provide the follow methods:
245    *   * start
246    *   * duration with value of 0.
247    *   * colorId
248    *   * selected
249    *
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.
257    */
258   function drawInstantSlicesAsLines(
259       ctx, dt, viewLWorld, viewRWorld, viewHeight, slices, lineWidthInPixels) {
260     var pixelRatio = window.devicePixelRatio || 1;
261     var height = viewHeight * pixelRatio;
262
263     var pixWidth = dt.xViewVectorToWorld(1);
264
265     // Begin rendering in world space.
266     ctx.save();
267     ctx.lineWidth = pixWidth * lineWidthInPixels;
268     dt.applyTransformToCanvas(ctx);
269     ctx.beginPath();
270
271     var lowSlice = tvcm.findLowIndexInSortedArray(
272         slices,
273         function(slice) { return slice.start; },
274         viewLWorld);
275
276     for (var i = lowSlice; i < slices.length; ++i) {
277       var slice = slices[i];
278       var x = slice.start;
279       if (x > viewRWorld)
280         break;
281
282       ctx.strokeStyle = EventPresenter.getInstantSliceColor(slice);
283
284       ctx.moveTo(x, 0);
285       ctx.lineTo(x, height);
286     }
287     ctx.stroke();
288     ctx.restore();
289   }
290
291   /**
292    * Draws the labels for the given slices.
293    *
294    * The |slices| array must contain objects with the following API:
295    *   * start
296    *   * duration
297    *   * title
298    *   * didNotFinish (optional)
299    *
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.
306    */
307   function drawLabels(ctx, dt, viewLWorld, viewRWorld, slices, async) {
308     var pixelRatio = window.devicePixelRatio || 1;
309     var pixWidth = dt.xViewVectorToWorld(1);
310
311     ctx.save();
312
313     ctx.textAlign = 'center';
314     ctx.textBaseline = 'top';
315     ctx.font = (10 * pixelRatio) + 'px sans-serif';
316
317     if (async)
318       ctx.font = 'italic ' + ctx.font;
319
320     var lowSlice = tvcm.findLowIndexInSortedArray(
321         slices,
322         function(slice) { return slice.start + slice.duration; },
323         viewLWorld);
324
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)
330         break;
331
332       if (slice.duration <= quickDiscardThresshold)
333         continue;
334
335       var title = slice.title +
336           (slice.didNotFinish ? ' (Did Not Finish)' : '');
337
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(
344             ctx, pixWidth,
345             drawnTitle, drawnWidth,
346             slice.duration);
347         drawnTitle = elidedValues.string;
348         drawnWidth = elidedValues.width;
349       }
350
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);
355       }
356     }
357     ctx.restore();
358   }
359
360   return {
361     drawSlices: drawSlices,
362     drawInstantSlicesAsLines: drawInstantSlicesAsLines,
363     drawLabels: drawLabels,
364
365     drawLine: drawLine,
366     drawTriangle: drawTriangle,
367     drawArrow: drawArrow,
368
369     elidedTitleCache_: elidedTitleCache,
370
371     THIN_SLICE_HEIGHT: THIN_SLICE_HEIGHT
372   };
373 });
374 </script>