Home | History | Annotate | Download | only in tracing
      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 Provides a mechanism for drawing massive numbers of
      9  * colored rectangles into a canvas in an efficient manner, provided
     10  * they are drawn left to right with fixed y and height throughout.
     11  *
     12  * The basic idea used here is to fuse subpixel rectangles together so that
     13  * we never issue a canvas fillRect for them. It turns out Javascript can
     14  * do this quite efficiently, compared to asking Canvas2D to do the same.
     15  *
     16  * A few extra things are done by this class in the name of speed:
     17  * - Viewport culling: off-viewport rectangles are discarded.
     18  *
     19  * - The actual discarding operation is done in world space,
     20  *   e.g. pre-transform.
     21  *
     22  * - Rather than expending compute cycles trying to figure out an average
     23  *   color for fused rectangles from css strings, you instead draw using
     24  *   palletized colors. The fused rect is the max pallete index encountered.
     25  *
     26  * Make sure to flush the trackRenderer before finishing drawing in order
     27  * to commit any queued drawing operations.
     28  */
     29 base.exportTo('tracing', function() {
     30 
     31   /**
     32    * Creates a fast rect renderer with a specific set of culling rules
     33    * and color pallette.
     34    * @param {GraphicsContext2D} ctx Canvas2D drawing context.
     35    * @param {number} minRectSize Only rectangles with width < minRectSize are
     36    *    considered for merging.
     37    * @param {number} maxMergeDist Controls how many successive small rectangles
     38    *    can be merged together before issuing a rectangle.
     39    * @param {Array} pallette The color pallete for drawing. Pallette slots
     40    *    should map to valid Canvas fillStyle strings.
     41    *
     42    * @constructor
     43    */
     44   function FastRectRenderer(ctx, minRectSize, maxMergeDist, pallette) {
     45     this.ctx_ = ctx;
     46     this.minRectSize_ = minRectSize;
     47     this.maxMergeDist_ = maxMergeDist;
     48     this.pallette_ = pallette;
     49   }
     50 
     51   FastRectRenderer.prototype = {
     52     y_: 0,
     53     h_: 0,
     54     merging_: false,
     55     mergeStartX_: 0,
     56     mergeCurRight_: 0,
     57 
     58     /**
     59      * Changes the y position and height for subsequent fillRect
     60      * calls. x and width are specifieid on the fillRect calls.
     61      */
     62     setYandH: function(y, h) {
     63       this.flush();
     64       this.y_ = y;
     65       this.h_ = h;
     66     },
     67 
     68     /**
     69      * Fills rectangle at the specified location, if visible. If the
     70      * rectangle is subpixel, it will be merged with adjacent rectangles.
     71      * The drawing operation may not take effect until flush is called.
     72      * @param {number} colorId The color of this rectangle, as an index
     73      *     in the renderer's color pallete.
     74      */
     75     fillRect: function(x, w, colorId) {
     76       var r = x + w;
     77       if (w < this.minRectSize_) {
     78         if (r - this.mergeStartX_ > this.maxMergeDist_)
     79           this.flush();
     80         if (!this.merging_) {
     81           this.merging_ = true;
     82           this.mergeStartX_ = x;
     83           this.mergeCurRight_ = r;
     84           this.mergedColorId = colorId;
     85         } else {
     86           this.mergeCurRight_ = r;
     87           this.mergedColorId = Math.max(this.mergedColorId, colorId);
     88         }
     89       } else {
     90         if (this.merging_)
     91           this.flush();
     92         this.ctx_.fillStyle = this.pallette_[colorId];
     93         this.ctx_.fillRect(x, this.y_, w, this.h_);
     94       }
     95     },
     96 
     97     /**
     98      * Commits any pending fillRect operations to the underlying graphics
     99      * context.
    100      */
    101     flush: function() {
    102       if (this.merging_) {
    103         this.ctx_.fillStyle = this.pallette_[this.mergedColorId];
    104         this.ctx_.fillRect(this.mergeStartX_, this.y_,
    105                            this.mergeCurRight_ - this.mergeStartX_, this.h_);
    106         this.merging_ = false;
    107       }
    108     }
    109   };
    110 
    111   return {
    112     FastRectRenderer: FastRectRenderer
    113   };
    114 
    115 });
    116