| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * The ImageBuffer object holds an offscreen canvas object and | 6 * The ImageBuffer object holds an offscreen canvas object and |
| 7 * draws its content on the screen canvas applying scale and offset. | 7 * draws its content on the screen canvas applying scale and offset. |
| 8 * Supports pluggable overlays that modify the image appearance and behavior. | 8 * Supports pluggable overlays that modify the image appearance and behavior. |
| 9 * @constructor | 9 * @constructor |
| 10 */ | 10 */ |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 */ | 138 */ |
| 139 ImageBuffer.prototype.onClick = function (x, y) { | 139 ImageBuffer.prototype.onClick = function (x, y) { |
| 140 for (var i = this.overlays_.length - 1; i >= 0; i--) { | 140 for (var i = this.overlays_.length - 1; i >= 0; i--) { |
| 141 if (this.overlays_[i].onClick(x, y)) return true; | 141 if (this.overlays_[i].onClick(x, y)) return true; |
| 142 } | 142 } |
| 143 return false; | 143 return false; |
| 144 }; | 144 }; |
| 145 | 145 |
| 146 /** | 146 /** |
| 147 * Searches for a drag handler in the descending Z-order. | 147 * Searches for a drag handler in the descending Z-order. |
| 148 * @return {Function} A closure to be called on mouse drag. | 148 * @return {function(number,number)} A function to be called on mouse drag. |
| 149 */ | 149 */ |
| 150 ImageBuffer.prototype.getDragHandler = function (x, y) { | 150 ImageBuffer.prototype.getDragHandler = function (x, y) { |
| 151 for (var i = this.overlays_.length - 1; i >= 0; i--) { | 151 for (var i = this.overlays_.length - 1; i >= 0; i--) { |
| 152 var handler = this.overlays_[i].getDragHandler(x, y); | 152 var handler = this.overlays_[i].getDragHandler(x, y); |
| 153 if (handler) return handler; | 153 if (handler) return handler; |
| 154 } | 154 } |
| 155 return null; | 155 return null; |
| 156 }; | 156 }; |
| 157 | 157 |
| 158 /** | 158 /** |
| (...skipping 25 matching lines...) Expand all Loading... |
| 184 // Draw below everything including the content. | 184 // Draw below everything including the content. |
| 185 ImageBuffer.Margin.prototype.getZIndex = function() { return -2 }; | 185 ImageBuffer.Margin.prototype.getZIndex = function() { return -2 }; |
| 186 | 186 |
| 187 ImageBuffer.Margin.prototype.draw = function(context) { | 187 ImageBuffer.Margin.prototype.draw = function(context) { |
| 188 context.save(); | 188 context.save(); |
| 189 context.fillStyle = '#F0F0F0'; | 189 context.fillStyle = '#F0F0F0'; |
| 190 context.strokeStyle = '#000000'; | 190 context.strokeStyle = '#000000'; |
| 191 Rect.fillBetween(context, | 191 Rect.fillBetween(context, |
| 192 this.viewport_.getImageBoundsOnScreen(), | 192 this.viewport_.getImageBoundsOnScreen(), |
| 193 this.viewport_.getScreenBounds()); | 193 this.viewport_.getScreenBounds()); |
| 194 Rect.stroke(context, this.viewport_.getImageBoundsOnScreen()); | 194 |
| 195 Rect.outline(context, this.viewport_.getImageBoundsOnScreen()); |
| 195 context.restore(); | 196 context.restore(); |
| 196 }; | 197 }; |
| 197 | 198 |
| 198 /** | 199 /** |
| 199 * The overlay containing the image. | 200 * The overlay containing the image. |
| 200 */ | 201 */ |
| 201 ImageBuffer.Content = function(viewport, document) { | 202 ImageBuffer.Content = function(viewport, document) { |
| 202 this.viewport_ = viewport; | 203 this.viewport_ = viewport; |
| 203 this.document_ = document; | 204 this.document_ = document; |
| 204 | 205 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 245 }; | 246 }; |
| 246 | 247 |
| 247 ImageBuffer.Content.prototype.getCanvas = function() { return this.canvas_ }; | 248 ImageBuffer.Content.prototype.getCanvas = function() { return this.canvas_ }; |
| 248 | 249 |
| 249 /** | 250 /** |
| 250 * Replaces the off-screen canvas. | 251 * Replaces the off-screen canvas. |
| 251 * To be used when the editor modifies the image dimensions. | 252 * To be used when the editor modifies the image dimensions. |
| 252 * If the logical width/height are supplied they override the canvas dimensions | 253 * If the logical width/height are supplied they override the canvas dimensions |
| 253 * and the canvas contents is scaled when displayed. | 254 * and the canvas contents is scaled when displayed. |
| 254 * @param {HTMLCanvasElement} canvas | 255 * @param {HTMLCanvasElement} canvas |
| 255 * @param {Number} opt_width Logical width (=canvas.width by default) | 256 * @param {number} opt_width Logical width (=canvas.width by default) |
| 256 * @param {Number} opt_height Logical height (=canvas.height by default) | 257 * @param {number} opt_height Logical height (=canvas.height by default) |
| 257 */ | 258 */ |
| 258 ImageBuffer.Content.prototype.setCanvas = function( | 259 ImageBuffer.Content.prototype.setCanvas = function( |
| 259 canvas, opt_width, opt_height) { | 260 canvas, opt_width, opt_height) { |
| 260 this.canvas_ = canvas; | 261 this.canvas_ = canvas; |
| 261 this.viewport_.setImageSize(opt_width || canvas.width, | 262 this.viewport_.setImageSize(opt_width || canvas.width, |
| 262 opt_height || canvas.height); | 263 opt_height || canvas.height); |
| 263 | 264 |
| 264 this.invalidateCaches(); | 265 this.invalidateCaches(); |
| 265 }; | 266 }; |
| 266 | 267 |
| 267 /** | 268 /** |
| 268 * @return {HTMLCanvasElement} A new blank canvas of the required size. | 269 * @return {HTMLCanvasElement} A new blank canvas of the required size. |
| 269 */ | 270 */ |
| 270 ImageBuffer.Content.prototype.createBlankCanvas = function (width, height) { | 271 ImageBuffer.Content.prototype.createBlankCanvas = function (width, height) { |
| 271 var canvas = this.document_.createElement('canvas'); | 272 var canvas = this.document_.createElement('canvas'); |
| 272 canvas.width = width; | 273 canvas.width = width; |
| 273 canvas.height = height; | 274 canvas.height = height; |
| 274 return canvas; | 275 return canvas; |
| 275 }; | 276 }; |
| 276 | 277 |
| 277 /** | 278 /** |
| 278 * @param {Number} opt_width Width of the copy, original width by default. | 279 * @param {number} opt_width Width of the copy, original width by default. |
| 279 * @param {Number} opt_height Height of the copy, original height by default. | 280 * @param {number} opt_height Height of the copy, original height by default. |
| 280 * @return {HTMLCanvasElement} A new canvas with a copy of the content. | 281 * @return {HTMLCanvasElement} A new canvas with a copy of the content. |
| 281 */ | 282 */ |
| 282 ImageBuffer.Content.prototype.copyCanvas = function (opt_width, opt_height) { | 283 ImageBuffer.Content.prototype.copyCanvas = function (opt_width, opt_height) { |
| 283 var canvas = this.createBlankCanvas(opt_width || this.canvas_.width, | 284 var canvas = this.createBlankCanvas(opt_width || this.canvas_.width, |
| 284 opt_height || this.canvas_.height); | 285 opt_height || this.canvas_.height); |
| 285 Rect.drawImage(canvas.getContext('2d'), this.canvas_); | 286 Rect.drawImage(canvas.getContext('2d'), this.canvas_); |
| 286 return canvas; | 287 return canvas; |
| 287 }; | 288 }; |
| 288 | 289 |
| 289 /** | 290 /** |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 335 ImageBuffer.Overview.RIGHT = 7; | 336 ImageBuffer.Overview.RIGHT = 7; |
| 336 ImageBuffer.Overview.BOTTOM = 50; | 337 ImageBuffer.Overview.BOTTOM = 50; |
| 337 | 338 |
| 338 ImageBuffer.Overview.prototype.update = function() { | 339 ImageBuffer.Overview.prototype.update = function() { |
| 339 var imageBounds = this.viewport_.getImageBounds(); | 340 var imageBounds = this.viewport_.getImageBounds(); |
| 340 | 341 |
| 341 if (this.contentGeneration_ != this.content_.getCacheGeneration()) { | 342 if (this.contentGeneration_ != this.content_.getCacheGeneration()) { |
| 342 this.contentGeneration_ = this.content_.getCacheGeneration(); | 343 this.contentGeneration_ = this.content_.getCacheGeneration(); |
| 343 | 344 |
| 344 var aspect = imageBounds.width / imageBounds.height; | 345 var aspect = imageBounds.width / imageBounds.height; |
| 345 if (aspect > 1) { | |
| 346 this.bounds_ = new Rect(ImageBuffer.Overview.MAX_SIZE, | |
| 347 ImageBuffer.Overview.MAX_SIZE / aspect); | |
| 348 } else { | |
| 349 this.bounds_ = new Rect(ImageBuffer.Overview.MAX_SIZE * aspect, | |
| 350 ImageBuffer.Overview.MAX_SIZE); | |
| 351 } | |
| 352 | 346 |
| 353 this.canvas_ = | 347 this.canvas_ = this.content_.copyCanvas( |
| 354 this.content_.copyCanvas(this.bounds_.width, this.bounds_.height); | 348 ImageBuffer.Overview.MAX_SIZE * Math.min(aspect, 1), |
| 349 ImageBuffer.Overview.MAX_SIZE / Math.max(aspect, 1)); |
| 355 } | 350 } |
| 356 | 351 |
| 352 this.bounds_ = null; |
| 357 this.clipped_ = null; | 353 this.clipped_ = null; |
| 358 | 354 |
| 359 if (this.viewport_.isClipped()) { | 355 if (this.viewport_.isClipped()) { |
| 360 var screenBounds = this.viewport_.getScreenBounds(); | 356 var screenBounds = this.viewport_.getScreenBounds(); |
| 361 | 357 |
| 362 this.bounds_ = this.bounds_.moveTo( | 358 this.bounds_ = new Rect( |
| 363 screenBounds.width - ImageBuffer.Overview.RIGHT - this.bounds_.width, | 359 screenBounds.width - ImageBuffer.Overview.RIGHT - this.canvas_.width, |
| 364 screenBounds.height - ImageBuffer.Overview.BOTTOM - | 360 screenBounds.height - ImageBuffer.Overview.BOTTOM - this.canvas_.height, |
| 365 this.bounds_.height); | 361 this.canvas_.width, |
| 362 this.canvas_.height); |
| 366 | 363 |
| 367 this.scale_ = this.bounds_.width / imageBounds.width; | 364 this.scale_ = this.bounds_.width / imageBounds.width; |
| 368 | 365 |
| 369 this.clipped_ = this.viewport_.getImageClipped(). | 366 this.clipped_ = this.viewport_.getImageClipped(). |
| 370 scale(this.scale_). | 367 scale(this.scale_). |
| 371 shift(this.bounds_.left, this.bounds_.top); | 368 shift(this.bounds_.left, this.bounds_.top); |
| 372 } | 369 } |
| 373 }; | 370 }; |
| 374 | 371 |
| 375 ImageBuffer.Overview.prototype.draw = function(context) { | 372 ImageBuffer.Overview.prototype.draw = function(context) { |
| 376 this.update(); | 373 this.update(); |
| 377 | 374 |
| 378 if (!this.clipped_) return; | 375 if (!this.clipped_) return; |
| 379 | 376 |
| 380 // Draw the thumbnail. | 377 // Draw the thumbnail. |
| 381 Rect.drawImage(context, this.canvas_, this.bounds_); | 378 Rect.drawImage(context, this.canvas_, this.bounds_); |
| 382 | 379 |
| 383 // Draw the thumbnail border. | |
| 384 context.strokeStyle = '#000000'; | |
| 385 Rect.stroke(context, this.bounds_); | |
| 386 | |
| 387 // Draw the shadow over the off-screen part of the thumbnail. | 380 // Draw the shadow over the off-screen part of the thumbnail. |
| 388 context.globalAlpha = 0.3; | 381 context.globalAlpha = 0.3; |
| 389 context.fillStyle = '#000000'; | 382 context.fillStyle = '#000000'; |
| 390 Rect.fillBetween(context, this.clipped_, this.bounds_); | 383 Rect.fillBetween(context, this.clipped_, this.bounds_); |
| 391 | 384 |
| 392 // Outline the on-screen part of the thumbnail. | 385 // Outline the on-screen part of the thumbnail. |
| 393 context.strokeStyle = '#FFFFFF'; | 386 context.strokeStyle = '#FFFFFF'; |
| 394 Rect.stroke(context, this.clipped_); | 387 Rect.outline(context, this.clipped_); |
| 388 |
| 389 context.globalAlpha = 1; |
| 390 // Draw the thumbnail border. |
| 391 context.strokeStyle = '#000000'; |
| 392 Rect.outline(context, this.bounds_); |
| 395 }; | 393 }; |
| 396 | 394 |
| 397 ImageBuffer.Overview.prototype.getCursorStyle = function(x, y) { | 395 ImageBuffer.Overview.prototype.getCursorStyle = function(x, y) { |
| 398 if (!this.bounds_ || !this.bounds_.inside(x, y)) return null; | 396 if (!this.bounds_ || !this.bounds_.inside(x, y)) return null; |
| 399 | 397 |
| 400 // Indicate that the on-screen part is draggable. | 398 // Indicate that the on-screen part is draggable. |
| 401 if (this.clipped_.inside(x, y)) return 'move'; | 399 if (this.clipped_ && this.clipped_.inside(x, y)) return 'move'; |
| 402 | 400 |
| 403 // Indicate that the rest of the thumbnail is clickable. | 401 // Indicate that the rest of the thumbnail is clickable. |
| 404 return 'crosshair'; | 402 return 'crosshair'; |
| 405 }; | 403 }; |
| 406 | 404 |
| 407 ImageBuffer.Overview.prototype.onClick = function(x, y) { | 405 ImageBuffer.Overview.prototype.onClick = function(x, y) { |
| 408 if (this.getCursorStyle(x, y) != 'crosshair') return false; | 406 if (this.getCursorStyle(x, y) != 'crosshair') return false; |
| 409 this.viewport_.setCenter( | 407 this.viewport_.setCenter( |
| 410 (x - this.bounds_.left) / this.scale_, | 408 (x - this.bounds_.left) / this.scale_, |
| 411 (y - this.bounds_.top) / this.scale_); | 409 (y - this.bounds_.top) / this.scale_); |
| 412 this.viewport_.repaint(); | 410 this.viewport_.repaint(); |
| 413 return true; | 411 return true; |
| 414 }; | 412 }; |
| 415 | 413 |
| 416 ImageBuffer.Overview.prototype.getDragHandler = function(x, y) { | 414 ImageBuffer.Overview.prototype.getDragHandler = function(x, y) { |
| 417 var cursor = this.getCursorStyle(x, y); | 415 var cursor = this.getCursorStyle(x, y); |
| 418 | 416 |
| 419 if (cursor == 'move') { | 417 if (cursor == 'move') { |
| 420 var self = this; | 418 var self = this; |
| 421 function scale() { return -self.scale_;} | 419 function scale() { return -self.scale_;} |
| 422 function hit(x, y) { return self.bounds_ && self.bounds_.inside(x, y); } | 420 function hit(x, y) { return self.bounds_ && self.bounds_.inside(x, y); } |
| 423 return this.viewport_.createOffsetSetter(x, y, scale, hit); | 421 return this.viewport_.createOffsetSetter(x, y, scale, hit); |
| 424 } else if (cursor == 'crosshair') { | 422 } else if (cursor == 'crosshair') { |
| 425 // Force non-draggable behavior. | 423 // Force non-draggable behavior. |
| 426 return function() {}; | 424 return function() {}; |
| 427 } else { | 425 } else { |
| 428 return null; | 426 return null; |
| 429 } | 427 } |
| 430 }; | 428 }; |
| OLD | NEW |