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