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