Home | History | Annotate | Download | only in gpu_internals
      1 // Copyright (c) 2011 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 cr.define('gpu', 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} vpLeft The leftmost visible part of the drawing viewport.
     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 {number} vpRight The rightmost visible part of the viewport.
     40    * @param {Array} pallette The color pallete for drawing. Pallette slots
     41    *    should map to valid Canvas fillStyle strings.
     42    *
     43    * @constructor
     44    */
     45   function FastRectRenderer(ctx, vpLeft, minRectSize, maxMergeDist, vpRight,
     46                             pallette) {
     47     this.ctx_ = ctx;
     48     this.vpLeft_ = vpLeft;
     49     this.minRectSize_ = minRectSize;
     50     this.maxMergeDist_ = maxMergeDist;
     51     this.vpRight_ = vpRight;
     52     this.pallette_ = pallette;
     53   }
     54 
     55   FastRectRenderer.prototype = {
     56     y_: 0,
     57     h_: 0,
     58     merging_: false,
     59     mergeStartX_: 0,
     60     mergeCurRight_: 0,
     61 
     62     /**
     63      * Changes the y position and height for subsequent fillRect
     64      * calls. x and width are specifieid on the fillRect calls.
     65      */
     66     setYandH: function(y, h) {
     67       this.flush();
     68       this.y_ = y;
     69       this.h_ = h;
     70     },
     71 
     72     /**
     73      * Fills rectangle at the specified location, if visible. If the
     74      * rectangle is subpixel, it will be merged with adjacent rectangles.
     75      * The drawing operation may not take effect until flush is called.
     76      * @param {number} colorId The color of this rectangle, as an index
     77      *     in the renderer's color pallete.
     78      */
     79     fillRect: function(x, w, colorId) {
     80       var r = x + w;
     81       if (r < this.vpLeft_ || x > this.vpRight_) return;
     82       if (w < this.minRectSize_) {
     83         if (r - this.mergeStartX_ > this.maxMergeDist_)
     84           this.flush();
     85         if (!this.merging_) {
     86           this.merging_ = true;
     87           this.mergeStartX_ = x;
     88           this.mergeCurRight_ = r;
     89           this.mergedColorId = colorId;
     90         } else {
     91           this.mergeCurRight_ = r;
     92           this.mergedColorId = Math.max(this.mergedColorId, colorId);
     93         }
     94       } else {
     95         if (this.merging_)
     96           this.flush();
     97         this.ctx_.fillStyle = this.pallette_[colorId];
     98         this.ctx_.fillRect(x, this.y_, w, this.h_);
     99       }
    100     },
    101 
    102     /**
    103      * Commits any pending fillRect operations to the underlying graphics
    104      * context.
    105      */
    106     flush: function() {
    107       if (this.merging_) {
    108         this.ctx_.fillStyle = this.pallette_[this.mergedColorId];
    109         this.ctx_.fillRect(this.mergeStartX_, this.y_,
    110                            this.mergeCurRight_ - this.mergeStartX_, this.h_);
    111         this.merging_ = false;
    112       }
    113     }
    114   };
    115 
    116   return {
    117     FastRectRenderer: FastRectRenderer
    118   };
    119 
    120 });
    121