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 |