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 base.require('base.range'); 8 base.require('base.events'); 9 10 base.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