Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 * Viewport class controls the way the image is displayed (scale, offset etc). | |
| 7 */ | |
| 8 function Viewport(repaintCallback) { | |
| 9 this.repaintCallback_ = repaintCallback; | |
| 10 | |
| 11 this.imageBounds_ = new Rect(); | |
| 12 this.screenBounds_ = new Rect(); | |
| 13 | |
| 14 this.scale_ = 1; | |
| 15 this.offsetX_ = 0; | |
| 16 this.offsetY_ = 0; | |
| 17 | |
| 18 this.generation_ = 0; | |
| 19 | |
| 20 this.update(); | |
| 21 } | |
| 22 | |
| 23 /** | |
| 24 * Viewport modification. | |
| 25 */ | |
| 26 | |
|
SeRya
2011/08/08 13:26:44
New line should not be here. The argument should b
Vladislav Kaznacheev
2011/08/08 13:47:51
Initialized in the constructor.
As for the comme
| |
| 27 Viewport.prototype.setScaleControl = function(scaleControl) { | |
| 28 this.scaleControl_ = scaleControl; | |
| 29 }; | |
| 30 | |
| 31 Viewport.prototype.setImageSize = function(width, height) { | |
| 32 this.imageBounds_ = new Rect(width, height); | |
| 33 if (this.scaleControl_) this.scaleControl_.displayImageSize(width, height); | |
| 34 this.invalidateCaches(); | |
| 35 }; | |
| 36 | |
| 37 Viewport.prototype.setScreenSize = function(width, height) { | |
| 38 this.screenBounds_ = new Rect(width, height); | |
| 39 if (this.scaleControl_) | |
| 40 this.scaleControl_.setMinScale(this.getFittingScale()); | |
| 41 this.invalidateCaches(); | |
| 42 }; | |
| 43 | |
| 44 Viewport.prototype.getScale = function () { return this.scale_ }; | |
|
SeRya
2011/08/08 13:26:44
Space before ().
Vladislav Kaznacheev
2011/08/08 13:47:51
Done.
| |
| 45 | |
| 46 Viewport.prototype.setScale = function (scale, notify) { | |
|
SeRya
2011/08/08 13:26:44
Space before ()
Vladislav Kaznacheev
2011/08/08 13:47:51
Done.
| |
| 47 if (this.scale_ == scale) return; | |
| 48 this.scale_ = scale; | |
| 49 if (notify && this.scaleControl_) this.scaleControl_.displayScale(scale); | |
| 50 this.invalidateCaches(); | |
| 51 }; | |
| 52 | |
| 53 Viewport.prototype.getFittingScale = function() { | |
| 54 var scaleX = this.screenBounds_.width / this.imageBounds_.width; | |
| 55 var scaleY = this.screenBounds_.height / this.imageBounds_.height; | |
| 56 return Math.min(scaleX, scaleY) * 0.85; | |
| 57 }; | |
| 58 | |
| 59 Viewport.prototype.fitImage = function() { | |
| 60 var scale = this.getFittingScale(); | |
| 61 if (this.scaleControl_) this.scaleControl_.setMinScale(scale); | |
| 62 this.setScale(scale, true); | |
| 63 }; | |
| 64 | |
| 65 Viewport.prototype.getOffsetX = function () { return this.offsetX_ }; | |
| 66 | |
| 67 Viewport.prototype.getOffsetY = function () { return this.offsetY_ }; | |
| 68 | |
| 69 Viewport.prototype.setOffset = function(x, y, ignoreClipping) { | |
| 70 if (!ignoreClipping) { | |
| 71 x = this.clampOffsetX_(x); | |
| 72 y = this.clampOffsetY_(y); | |
| 73 } | |
| 74 if (this.offsetX_ == x && this.offsetY_ == y) return; | |
| 75 this.offsetX_ = x; | |
| 76 this.offsetY_ = y; | |
| 77 this.invalidateCaches(); | |
| 78 }; | |
| 79 | |
| 80 Viewport.prototype.setCenter = function(x, y, ignoreClipping) { | |
| 81 this.setOffset( | |
| 82 this.imageBounds_.width / 2 - x, | |
| 83 this.imageBounds_.height / 2 - y, | |
| 84 ignoreClipping); | |
| 85 }; | |
| 86 | |
| 87 /** | |
| 88 * Return a closure that can be called to pan the image. | |
| 89 * Useful for implementing non-trivial variants of panning (overview etc). | |
| 90 * @param {Number} originalX The x coordinate on the screen canvas that | |
| 91 * corresponds to zero change to offsetX. | |
| 92 * @param {Number} originalY The y coordinate on the screen canvas that | |
| 93 * corresponds to zero change to offsetY. | |
| 94 * @param {Function} scaleFunc returns the current image to screen scale. | |
| 95 * @param {Function} hitFunc returns true if (x,y) is in the valid region. | |
| 96 */ | |
| 97 Viewport.prototype.createOffsetSetter = function ( | |
| 98 originalX, originalY, scaleFunc, hitFunc) { | |
| 99 var originalOffsetX = this.offsetX_; | |
| 100 var originalOffsetY = this.offsetY_; | |
| 101 if (!hitFunc) hitFunc = function() { return true }; | |
| 102 if (!scaleFunc) scaleFunc = this.getScale.bind(this); | |
| 103 | |
| 104 var self = this; | |
| 105 return function(x, y) { | |
| 106 if (hitFunc(x, y)) { | |
| 107 var scale = scaleFunc(); | |
| 108 self.setOffset( | |
| 109 originalOffsetX + (x - originalX) / scale, | |
| 110 originalOffsetY + (y - originalY) / scale); | |
| 111 self.repaint(); | |
| 112 } | |
| 113 }; | |
| 114 }; | |
| 115 | |
| 116 /** | |
| 117 * Access to the current viewport state. | |
|
SeRya
2011/08/08 13:26:44
It's not a JS Doc comment and dont need to comply
Vladislav Kaznacheev
2011/08/08 13:47:51
Done.
| |
| 118 */ | |
| 119 | |
| 120 /** | |
| 121 * @return {Rect} The image bounds in image coordinates. | |
| 122 */ | |
| 123 Viewport.prototype.getImageBounds = function() { return this.imageBounds_ }; | |
| 124 | |
| 125 /** | |
| 126 * @return {Rect} The screen bounds in screen coordinates. | |
| 127 */ | |
| 128 Viewport.prototype.getScreenBounds = function() { return this.screenBounds_ }; | |
| 129 | |
| 130 /** | |
| 131 * @return {Rect} The visible part of the image, in image coordinates. | |
| 132 */ | |
| 133 Viewport.prototype.getImageClipped = function() { return this.imageClipped_ }; | |
| 134 | |
| 135 /** | |
| 136 * @return {Rect} The visible part of the image, in screen coordinates. | |
| 137 */ | |
| 138 Viewport.prototype.getScreenClipped = function() { return this.screenClipped_ }; | |
| 139 | |
| 140 /** | |
| 141 * A counter that is incremented with each viewport state change. | |
| 142 * Clients that cache anything that depends on the viewport state should keep | |
| 143 * track of this counter. | |
| 144 */ | |
| 145 Viewport.prototype.getCacheGeneration = function() { return this.generation_ }; | |
| 146 | |
| 147 /** | |
| 148 * Called on evert view port state change (even if repaint has not been called). | |
| 149 */ | |
| 150 Viewport.prototype.invalidateCaches = function() { this.generation_++ }; | |
| 151 | |
| 152 /** | |
| 153 * @return {Rect} The image bounds in screen coordinates. | |
| 154 */ | |
| 155 Viewport.prototype.getImageBoundsOnScreen = function() { | |
| 156 return this.imageOnScreen_; | |
| 157 }; | |
| 158 | |
| 159 /* | |
|
SeRya
2011/08/08 13:26:44
/** ?
Vladislav Kaznacheev
2011/08/08 13:47:51
Also a section comment.
On 2011/08/08 13:26:44, Se
| |
| 160 * Conversion between the screen and image coordinate spaces. | |
| 161 */ | |
| 162 Viewport.prototype.screenToImageSize = function(size) { | |
| 163 return size / this.getScale(); | |
| 164 }; | |
| 165 | |
| 166 Viewport.prototype.screenToImageX = function(x) { | |
| 167 return Math.round((x - this.imageOnScreen_.left) / this.getScale()); | |
| 168 }; | |
| 169 | |
| 170 Viewport.prototype.screenToImageY = function(y) { | |
| 171 return Math.round((y - this.imageOnScreen_.top) / this.getScale()); | |
| 172 }; | |
| 173 | |
| 174 Viewport.prototype.screenToImageRect = function(rect) { | |
| 175 return new Rect( | |
| 176 this.screenToImageX(rect.left), | |
| 177 this.screenToImageY(rect.top), | |
| 178 this.screenToImageSize(rect.width), | |
| 179 this.screenToImageSize(rect.height)); | |
| 180 }; | |
| 181 | |
| 182 Viewport.prototype.imageToScreenSize = function(size) { | |
| 183 return size * this.getScale(); | |
| 184 }; | |
| 185 | |
| 186 Viewport.prototype.imageToScreenX = function(x) { | |
| 187 return Math.round(this.imageOnScreen_.left + x * this.getScale()); | |
| 188 }; | |
| 189 | |
| 190 Viewport.prototype.imageToScreenY = function(y) { | |
| 191 return Math.round(this.imageOnScreen_.top + y * this.getScale()); | |
| 192 }; | |
| 193 | |
| 194 Viewport.prototype.imageToScreenRect = function(rect) { | |
| 195 return new Rect( | |
| 196 this.imageToScreenX(rect.left), | |
| 197 this.imageToScreenY(rect.top), | |
| 198 this.imageToScreenSize(rect.width), | |
| 199 this.imageToScreenSize(rect.height)); | |
| 200 }; | |
| 201 | |
| 202 /** | |
| 203 * @return {Boolean} True if some part of the image is clipped by the screen. | |
| 204 */ | |
| 205 Viewport.prototype.isClipped = function () { | |
| 206 return this.getMarginX_() < 0 || this.getMarginY_() < 0; | |
| 207 }; | |
| 208 | |
| 209 /** | |
| 210 * Horizontal margin. Negative if the image is clipped horizontally. | |
| 211 */ | |
| 212 Viewport.prototype.getMarginX_ = function() { | |
| 213 return Math.floor( | |
| 214 (this.screenBounds_.width - this.imageBounds_.width * this.scale_) / 2); | |
| 215 }; | |
| 216 | |
| 217 /** | |
| 218 * Vertical margin. Negative if the image is clipped vertically. | |
| 219 */ | |
| 220 Viewport.prototype.getMarginY_ = function() { | |
| 221 return Math.floor( | |
| 222 (this.screenBounds_.height - this.imageBounds_.height * this.scale_) / 2); | |
| 223 }; | |
| 224 | |
| 225 Viewport.prototype.clampOffsetX_ = function(x) { | |
| 226 var limit = Math.max(0, -this.getMarginX_() / this.getScale()); | |
| 227 return ImageUtil.clamp(-limit, x, limit); | |
| 228 }; | |
| 229 | |
| 230 Viewport.prototype.clampOffsetY_ = function(y) { | |
| 231 var limit = Math.max(0, -this.getMarginY_() / this.getScale()); | |
| 232 return ImageUtil.clamp(-limit, y, limit); | |
| 233 }; | |
| 234 | |
| 235 /** | |
| 236 * Recalculate the viewport parameters. | |
| 237 */ | |
| 238 Viewport.prototype.update = function() { | |
| 239 var scale = this.getScale(); | |
| 240 | |
| 241 // Image bounds in screen coordinates. | |
| 242 this.imageOnScreen_ = new Rect( | |
| 243 this.getMarginX_(), | |
| 244 this.getMarginY_(), | |
| 245 Math.floor(this.imageBounds_.width * scale), | |
| 246 Math.floor(this.imageBounds_.height * scale)); | |
| 247 | |
| 248 // A visible part of the image in image coordinates. | |
| 249 this.imageClipped_ = new Rect(this.imageBounds_); | |
| 250 | |
| 251 // A visible part of the image in screen coordinates. | |
| 252 this.screenClipped_ = new Rect(this.screenBounds_); | |
| 253 | |
| 254 // Adjust for the offset. | |
| 255 if (this.imageOnScreen_.left < 0) { | |
| 256 this.imageOnScreen_.left += this.clampOffsetX_(this.offsetX_) * scale; | |
| 257 this.imageClipped_.left = -this.imageOnScreen_.left / scale; | |
| 258 this.imageClipped_.width = this.screenBounds_.width / scale; | |
| 259 } else { | |
| 260 this.screenClipped_.left = this.imageOnScreen_.left; | |
| 261 this.screenClipped_.width = this.imageOnScreen_.width; | |
| 262 } | |
| 263 | |
| 264 if (this.imageOnScreen_.top < 0) { | |
| 265 this.imageOnScreen_.top += this.clampOffsetY_(this.offsetY_) * scale; | |
| 266 this.imageClipped_.top = -this.imageOnScreen_.top / scale; | |
| 267 this.imageClipped_.height = this.screenBounds_.height / scale; | |
| 268 } else { | |
| 269 this.screenClipped_.top = this.imageOnScreen_.top; | |
| 270 this.screenClipped_.height = this.imageOnScreen_.height; | |
| 271 } | |
| 272 }; | |
| 273 | |
| 274 Viewport.prototype.repaint = function () { | |
| 275 if (this.repaintCallback_) this.repaintCallback_(); | |
| 276 }; | |
| OLD | NEW |