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