• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
28cr.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