• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7base.require('base.range');
8base.require('base.events');
9
10base.exportTo('ui', function() {
11  // FIXME(pdr): Replace this padding with just what's necessary for
12  //             drawing borders / highlights.
13  //             https://code.google.com/p/trace-viewer/issues/detail?id=228
14  var DEFAULT_PAD_PERCENTAGE = 0.75;
15
16  function QuadViewViewport(worldRect,
17                            opt_quad_stack_scale,
18                            opt_padding,
19                            opt_devicePixelRatio) {
20    base.EventTarget.call(this);
21    if (!worldRect)
22      throw new Error('Cannot initialize a viewport with an empty worldRect');
23
24    // Physical pixels / device-independent pixels;
25    // 1 is normal; higher for eg Retina
26    this.devicePixelRatio =
27        opt_devicePixelRatio || window.devicePixelRatio || 1;
28
29    this.layoutRect_ = undefined;
30    this.setWorldRect_(worldRect, opt_padding);
31
32    var scale;
33    if (opt_quad_stack_scale) {
34      scale = opt_quad_stack_scale;
35    } else {
36      scale = 0.125;
37      if (this.devicePixelRatio > 1)
38        scale = scale * this.devicePixelRatio;
39    }
40    this.worldPixelsPerDevicePixel_ = scale;
41
42    this.updateScale_();
43  }
44
45  QuadViewViewport.prototype = {
46
47    __proto__: base.EventTarget.prototype,
48
49    // The pixels in the original, traced browser are
50    // represented in a canvas 'world', scaled by a
51    // this 'scale' value.
52    set scale(scale) {
53      this.worldPixelsPerDevicePixel_ = scale;
54      this.updateScale_();
55      this.didChange_();
56    },
57
58    get scale() {
59      return this.worldPixelsPerDevicePixel_;
60    },
61
62    get worldRect() {
63      return this.worldRect_;
64    },
65
66    get unpaddedWorldRect() {
67      return this.unpaddedWorldRect_;
68    },
69
70    updateBoxSize: function(canvas) {
71      var resizedCanvas = false;
72      if (canvas.width !== this.worldWidthInDevicePixels_) {
73        canvas.width = this.worldWidthInDevicePixels_ * ui.RASTER_SCALE;
74        canvas.style.width = this.layoutRect_.width + 'px';
75        resizedCanvas = true;
76      }
77      if (canvas.height !== this.worldHeightInDevicePixels_) {
78        canvas.height = this.worldHeightInDevicePixels_ * ui.RASTER_SCALE;
79        canvas.style.height = this.layoutRect_.height + 'px';
80        resizedCanvas = true;
81      }
82      return resizedCanvas;
83    },
84
85    layoutPixelsToWorldPixels: function(v) {
86      var tmp = this.layoutPixelsToDevicePixels(v);
87      return this.devicePixelsToWorldPixels(tmp);
88    },
89
90    layoutPixelsToDevicePixels: function(v) {
91      var res = vec2.create();
92      return vec2.scale(res, v, this.devicePixelRatio);
93    },
94
95    devicePixelsToWorldPixels: function(v) {
96      var res = vec2.create();
97      vec2.transformMat2d(res, v, this.transformDevicePixelsToWorld_);
98      return res;
99    },
100
101    getWorldToDevicePixelsTransform: function() {
102      return this.transformWorldToDevicePixels_;
103    },
104
105    getDeviceLineWidthAssumingTransformIsApplied: function(
106        desiredDeviceLineWidth) {
107      return desiredDeviceLineWidth / this.worldPixelsPerDevicePixel_;
108    },
109
110    applyTransformToContext: function(ctx) {
111      var transform = this.transformWorldToDevicePixels_;
112      ctx.transform(transform[0], transform[1], transform[2],
113                    transform[3], transform[4], transform[5]);
114    },
115
116    forceRedrawAll: function() {
117      this.didChange_();
118    },
119
120    //-------------------------------------------
121
122    updateScale_: function() {
123      this.worldWidthInDevicePixels_ =
124          this.worldRect_.width * this.worldPixelsPerDevicePixel_;
125      this.worldHeightInDevicePixels_ =
126          this.worldRect_.height * this.worldPixelsPerDevicePixel_;
127
128      this.updateLayoutRect_();
129
130      this.transformWorldToDevicePixels_ = mat2d.create();
131      this.transformDevicePixelsToWorld_ = mat2d.create();
132      this.updateTransform_();
133    },
134
135    /** Adjust the scaled world box for Retina-like displays */
136    updateLayoutRect_: function() {
137      var devicePixelsPerLayoutPixel =
138          this.worldPixelsPerDevicePixel_ / this.devicePixelRatio;
139      this.layoutRect_ = this.worldRect.scale(devicePixelsPerLayoutPixel);
140    },
141
142    setWorldRect_: function(worldRect, opt_padding) {
143      var worldPad;
144      var padding;
145      if (opt_padding !== undefined) {
146        padding = opt_padding;
147      } else {
148        padding = DEFAULT_PAD_PERCENTAGE;
149      }
150      worldPad = Math.min(worldRect.width,
151                          worldRect.height) * padding;
152
153      this.unpaddedWorldRect_ = worldRect;
154      this.worldRect_ = worldRect.clone().enlarge(worldPad);
155    },
156
157    updateTransform_: function() {
158      if (!this.transformWorldToDevicePixels_)
159        return;
160
161      mat2d.identity(this.transformWorldToDevicePixels_);
162      mat2d.translateXY(
163          this.transformWorldToDevicePixels_,
164          -this.worldRect_.x, -this.worldRect_.y);
165      mat2d.scaleXY(this.transformWorldToDevicePixels_,
166          this.worldPixelsPerDevicePixel_,
167          this.worldPixelsPerDevicePixel_);
168
169      mat2d.invert(this.transformDevicePixelsToWorld_,
170                   this.transformWorldToDevicePixels_);
171    },
172
173    didChange_: function() {
174      base.dispatchSimpleEvent(this, 'change', false, false);
175    }
176  };
177
178  return {
179    QuadViewViewport: QuadViewViewport
180  };
181});
182