Home | History | Annotate | Download | only in tracks
      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 
     13 base.requireStylesheet('tracing.tracks.track');
     14 
     15 base.require('ui');
     16 base.require('ui.container_that_decorates_its_children');
     17 base.require('tracing.color_scheme');
     18 
     19 base.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