Crop image at image_loader.
To avoid wide image becomes blurry thumbnail, crop image at image_loader.
Currently it is used only for thumbnails, we only support to crop an image into square.
BUG=480679
TEST=ImageLoaderJsTest.ImageLoaderTest
Review URL: https://codereview.chromium.org/1137993007
Cr-Commit-Position: refs/heads/master@{#329609}
diff --git a/chrome/browser/chromeos/file_manager/image_loader_jstest.cc b/chrome/browser/chromeos/file_manager/image_loader_jstest.cc
index 24453f73..17fbc2c8 100644
--- a/chrome/browser/chromeos/file_manager/image_loader_jstest.cc
+++ b/chrome/browser/chromeos/file_manager/image_loader_jstest.cc
@@ -16,6 +16,9 @@
}
IN_PROC_BROWSER_TEST_F(ImageLoaderJsTest, CacheTest) {
- RunTest(base::FilePath(FILE_PATH_LITERAL(
- "cache_unittest.html")));
+ RunTest(base::FilePath(FILE_PATH_LITERAL("cache_unittest.html")));
+}
+
+IN_PROC_BROWSER_TEST_F(ImageLoaderJsTest, ImageLoaderTest) {
+ RunTest(base::FilePath(FILE_PATH_LITERAL("image_loader_unittest.html")));
}
diff --git a/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js b/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
index 5b91925..2c8aa6d 100644
--- a/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
+++ b/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
@@ -264,11 +264,27 @@
this.metadata_.filesystem &&
this.metadata_.filesystem.modificationTime &&
this.metadata_.filesystem.modificationTime.getTime();
- var thumbnailUrl =
- fillMode === ThumbnailLoader.FillMode.OVER_FILL &&
- this.croppedThumbnailUrl_ ?
- this.croppedThumbnailUrl_ :
- this.thumbnailUrl_;
+ var thumbnailUrl = this.thumbnailUrl_;
+ var options = {
+ maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH,
+ maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT,
+ cache: true,
+ priority: this.priority_,
+ timestamp: modificationTime
+ };
+
+ if (fillMode === ThumbnailLoader.FillMode.OVER_FILL) {
+ // Use cropped thumbnail url if available.
+ thumbnailUrl = this.croppedThumbnailUrl_ ?
+ this.croppedThumbnailUrl_ : this.thumbnailUrl_;
+
+ // Set crop option to image loader. Since image of croppedThumbnailUrl_ is
+ // 360x360 with current implemenation, it's no problem to crop it.
+ options['width'] = 360;
+ options['height'] = 360;
+ options['crop'] = true;
+ }
+
ImageLoaderClient.getInstance().load(
thumbnailUrl,
function(result) {
@@ -277,13 +293,7 @@
else
reject(result);
},
- {
- maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH,
- maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT,
- cache: true,
- priority: this.priority_,
- timestamp: modificationTime
- });
+ options);
}.bind(this)).then(function(result) {
if (!this.transform_)
return result;
diff --git a/ui/file_manager/image_loader/compiled_resources.gyp b/ui/file_manager/image_loader/compiled_resources.gyp
index f2173e7a..7a28a3ca 100644
--- a/ui/file_manager/image_loader/compiled_resources.gyp
+++ b/ui/file_manager/image_loader/compiled_resources.gyp
@@ -7,7 +7,8 @@
'target_name': 'background',
'variables': {
'depends': [
- "../file_manager/common/js/file_type.js",
+ '../../webui/resources/js/assert.js',
+ '../file_manager/common/js/file_type.js',
'../file_manager/common/js/metrics_base.js',
'../file_manager/common/js/metrics.js',
'../file_manager/common/js/metrics_events.js',
diff --git a/ui/file_manager/image_loader/image_loader.js b/ui/file_manager/image_loader/image_loader.js
index c94e13d..7665f07 100644
--- a/ui/file_manager/image_loader/image_loader.js
+++ b/ui/file_manager/image_loader/image_loader.js
@@ -204,35 +204,121 @@
};
/**
- * Performs resizing of the source image into the target canvas.
+ * Performs resizing and cropping of the source image into the target canvas.
*
* @param {HTMLCanvasElement|Image} source Source image or canvas.
* @param {HTMLCanvasElement} target Target canvas.
* @param {Object} options Resizing options as a hash array.
*/
-ImageLoader.resize = function(source, target, options) {
- var targetDimensions = ImageLoader.resizeDimensions(
- source.width, source.height, options);
-
+ImageLoader.resizeAndCrop = function(source, target, options) {
// Default orientation is 0deg.
- var orientation = options.orientation || new ImageOrientation(1, 0, 0, 1);
- var size = orientation.getSizeAfterCancelling(
- targetDimensions.width, targetDimensions.height);
- target.width = size.width;
- target.height = size.height;
+ var orientation =
+ ImageOrientation.fromDriveOrientation(options.orientation || 0);
- var targetContext = target.getContext('2d');
+ // Calculates copy parameters.
+ var copyParameters = ImageLoader.calculateCopyParameters(source, options);
+ target.width = copyParameters.canvas.width;
+ target.height = copyParameters.canvas.height;
+
+ // Apply.
+ var targetContext =
+ /** @type {CanvasRenderingContext2D} */ (target.getContext('2d'));
targetContext.save();
orientation.cancelImageOrientation(
- targetContext, targetDimensions.width, targetDimensions.height);
+ targetContext, copyParameters.target.width, copyParameters.target.height);
targetContext.drawImage(
source,
- 0, 0, source.width, source.height,
- 0, 0, targetDimensions.width, targetDimensions.height);
+ copyParameters.source.x,
+ copyParameters.source.y,
+ copyParameters.source.width,
+ copyParameters.source.height,
+ copyParameters.target.x,
+ copyParameters.target.y,
+ copyParameters.target.width,
+ copyParameters.target.height);
targetContext.restore();
};
/**
+ * @typedef {{
+ * source: {x:number, y:number, width:number, height:number},
+ * target: {x:number, y:number, width:number, height:number},
+ * canvas: {width:number, height:number}
+ * }}
+ */
+ImageLoader.CopyParameters;
+
+/**
+ * Calculates copy parameters.
+ *
+ * @param {HTMLCanvasElement|Image} source Source image or canvas.
+ * @param {Object} options Resizing options as a hash array.
+ * @return {!ImageLoader.CopyParameters} Calculated copy parameters.
+ */
+ImageLoader.calculateCopyParameters = function(source, options) {
+ if (options.crop) {
+ // When an image is cropped, target should be a fixed size square.
+ assert(options.width);
+ assert(options.height);
+ assert(options.width === options.height);
+
+ // The length of shorter edge becomes dimension of cropped area in the
+ // source.
+ var cropSourceDimension = Math.min(source.width, source.height);
+
+ return {
+ source: {
+ x: Math.floor((source.width / 2) - (cropSourceDimension / 2)),
+ y: Math.floor((source.height / 2) - (cropSourceDimension / 2)),
+ width: cropSourceDimension,
+ height: cropSourceDimension
+ },
+ target: {
+ x: 0,
+ y: 0,
+ width: options.width,
+ height: options.height
+ },
+ canvas: {
+ width: options.width,
+ height: options.height
+ }
+ };
+ }
+
+ // Target dimension is calculated in the rotated(transformed) coordinate.
+ var targetCanvasDimensions = ImageLoader.resizeDimensions(
+ source.width, source.height, options);
+
+ var targetDimensions = targetCanvasDimensions;
+ if (options.orientation && options.orientation % 2) {
+ targetDimensions = {
+ width: targetCanvasDimensions.height,
+ height: targetCanvasDimensions.width
+ };
+ }
+
+ return {
+ source: {
+ x: 0,
+ y: 0,
+ width: source.width,
+ height: source.height
+ },
+ target: {
+ x: 0,
+ y: 0,
+ width: targetDimensions.width,
+ height: targetDimensions.height
+ },
+ canvas: {
+ width: targetCanvasDimensions.width,
+ height: targetCanvasDimensions.height
+ }
+ };
+};
+
+/**
* Matrix converts AdobeRGB color space into sRGB color space.
* @const {!Array<number>}
*/
diff --git a/ui/file_manager/image_loader/image_loader_unittest.html b/ui/file_manager/image_loader/image_loader_unittest.html
new file mode 100644
index 0000000..0ad3431
--- /dev/null
+++ b/ui/file_manager/image_loader/image_loader_unittest.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<!-- Copyright 2015 The Chromium Authors. All rights reserved.
+ -- Use of this source code is governed by a BSD-style license that can be
+ -- found in the LICENSE file.
+ -->
+
+<script src="../../webui/resources/js/assert.js"></script>
+<script src="../file_manager/foreground/js/metadata/image_orientation.js"></script>
+
+<script src="image_loader.js"></script>
+<script src="image_loader_unittest.js"></script>
diff --git a/ui/file_manager/image_loader/image_loader_unittest.js b/ui/file_manager/image_loader/image_loader_unittest.js
new file mode 100644
index 0000000..09173646
--- /dev/null
+++ b/ui/file_manager/image_loader/image_loader_unittest.js
@@ -0,0 +1,165 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * Test case:
+ * - Source image: 200x50
+ * - Target: max size is 100x100
+ */
+function testNormalImage() {
+ var source = new Image();
+ source.width = 200;
+ source.height = 50;
+ var options = {
+ maxWidth: 100,
+ maxHeight: 100
+ };
+ var result = ImageLoader.calculateCopyParameters(source, options);
+ assertEquals(0, result.source.x);
+ assertEquals(0, result.source.y);
+ assertEquals(200, result.source.width);
+ assertEquals(50, result.source.height);
+ assertEquals(0, result.target.x);
+ assertEquals(0, result.target.y);
+ assertEquals(100, result.target.width);
+ assertEquals(25, result.target.height);
+ assertEquals(100, result.canvas.width);
+ assertEquals(25, result.canvas.height);
+};
+
+/**
+ * Test case:
+ * - Source image: 50x200 90 deg clock-wise rotated image.
+ * - Target: max size is 100x100
+ */
+function testRotatedImage() {
+ var source = new Image();
+ source.width = 50;
+ source.height = 200;
+ var options = {
+ maxWidth: 100,
+ maxHeight: 100,
+ orientation: 1
+ };
+ var result = ImageLoader.calculateCopyParameters(source, options);
+ assertEquals(0, result.source.x);
+ assertEquals(0, result.source.y);
+ assertEquals(50, result.source.width);
+ assertEquals(200, result.source.height);
+ assertEquals(0, result.target.x);
+ assertEquals(0, result.target.y);
+ assertEquals(25, result.target.width);
+ assertEquals(100, result.target.height);
+ assertEquals(100, result.canvas.width);
+ assertEquals(25, result.canvas.height);
+}
+
+/**
+ * Test case:
+ * - Source image: 800x100
+ * - Target: 50x50 cropped image.
+ */
+function testCroppedImage() {
+ var source = new Image();
+ source.width = 800;
+ source.height = 100;
+ var options = {
+ width: 50,
+ height: 50,
+ crop: true
+ };
+ var result = ImageLoader.calculateCopyParameters(source, options);
+ assertEquals(350, result.source.x);
+ assertEquals(0, result.source.y);
+ assertEquals(100, result.source.width);
+ assertEquals(100, result.source.height);
+ assertEquals(0, result.target.x);
+ assertEquals(0, result.target.y);
+ assertEquals(50, result.target.width);
+ assertEquals(50, result.target.height);
+ assertEquals(50, result.canvas.width);
+ assertEquals(50, result.canvas.height);
+}
+
+/**
+ * Test case:
+ * - Source image: 200x25
+ * - Target: 50x50 cropped image.
+ */
+function testCroppedImageWithResize() {
+ var source = new Image();
+ source.width = 200;
+ source.height = 25;
+ var options = {
+ width: 50,
+ height: 50,
+ crop: true
+ };
+ var result = ImageLoader.calculateCopyParameters(source, options);
+ assertEquals(87, result.source.x);
+ assertEquals(0, result.source.y);
+ assertEquals(25, result.source.width);
+ assertEquals(25, result.source.height);
+ assertEquals(0, result.target.x);
+ assertEquals(0, result.target.y);
+ assertEquals(50, result.target.width);
+ assertEquals(50, result.target.height);
+ assertEquals(50, result.canvas.width);
+ assertEquals(50, result.canvas.height);
+}
+
+/**
+ * Test case:
+ * - Source image: 20x10
+ * - Target: 50x50 cropped image.
+ */
+function testCroppedTinyImage() {
+ var source = new Image();
+ source.width = 20;
+ source.height = 10;
+ var options = {
+ width: 50,
+ height: 50,
+ crop: true
+ };
+ var result = ImageLoader.calculateCopyParameters(source, options);
+ assertEquals(5, result.source.x);
+ assertEquals(0, result.source.y);
+ assertEquals(10, result.source.width);
+ assertEquals(10, result.source.height);
+ assertEquals(0, result.target.x);
+ assertEquals(0, result.target.y);
+ assertEquals(50, result.target.width);
+ assertEquals(50, result.target.height);
+ assertEquals(50, result.canvas.width);
+ assertEquals(50, result.canvas.height);
+}
+
+/**
+ * Test case:
+ * - Source image: 100x400 90 degree clock-wise rotated.
+ * - Target: 50x50 cropped image
+ */
+function testCroppedRotatedImage() {
+ var source = new Image();
+ source.width = 100;
+ source.height = 400;
+ var options = {
+ width: 50,
+ height: 50,
+ crop: true,
+ orientation: 1
+ };
+ var result = ImageLoader.calculateCopyParameters(source, options);
+ assertEquals(0, result.source.x);
+ assertEquals(150, result.source.y);
+ assertEquals(100, result.source.width);
+ assertEquals(100, result.source.height);
+ assertEquals(0, result.target.x);
+ assertEquals(0, result.target.y);
+ assertEquals(50, result.target.width);
+ assertEquals(50, result.target.height);
+ assertEquals(50, result.canvas.width);
+ assertEquals(50, result.canvas.height);
+}
diff --git a/ui/file_manager/image_loader/manifest.json b/ui/file_manager/image_loader/manifest.json
index 5905da85..b6c8a4ef 100644
--- a/ui/file_manager/image_loader/manifest.json
+++ b/ui/file_manager/image_loader/manifest.json
@@ -19,6 +19,7 @@
"content_security_policy": "default-src 'none'; script-src 'self' chrome://resources chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj; style-src 'self'; frame-src 'self'; img-src 'self' data:; media-src 'self'; connect-src 'self' https://www.googledrive.com",
"background": {
"scripts": [
+ "chrome://resources/js/assert.js",
"chrome://resources/js/analytics.js",
"cache.js",
"chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/common/js/file_type.js",
diff --git a/ui/file_manager/image_loader/request.js b/ui/file_manager/image_loader/request.js
index 495d8e8..57f5360 100644
--- a/ui/file_manager/image_loader/request.js
+++ b/ui/file_manager/image_loader/request.js
@@ -487,7 +487,7 @@
ImageLoader.shouldProcess(this.image_.width,
this.image_.height,
this.request_)) {
- ImageLoader.resize(this.image_, this.canvas_, this.request_);
+ ImageLoader.resizeAndCrop(this.image_, this.canvas_, this.request_);
ImageLoader.convertColorSpace(
this.canvas_, this.request_.colorSpace || ColorSpace.SRGB);
this.sendImage_(true); // Image changed.