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