| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 * Loads and resizes an image. | 6 * Loads and resizes an image. |
| 7 * @constructor | 7 * @constructor |
| 8 */ | 8 */ |
| 9 function ImageLoader() { | 9 function ImageLoader() { |
| 10 /** | 10 /** |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 120 }; | 120 }; |
| 121 | 121 |
| 122 /** | 122 /** |
| 123 * Returns the singleton instance. | 123 * Returns the singleton instance. |
| 124 * @return {ImageLoader} ImageLoader object. | 124 * @return {ImageLoader} ImageLoader object. |
| 125 */ | 125 */ |
| 126 ImageLoader.getInstance = function() { | 126 ImageLoader.getInstance = function() { |
| 127 if (!ImageLoader.instance_) | 127 if (!ImageLoader.instance_) |
| 128 ImageLoader.instance_ = new ImageLoader(); | 128 ImageLoader.instance_ = new ImageLoader(); |
| 129 return ImageLoader.instance_; | 129 return ImageLoader.instance_; |
| 130 }; | 130 }; |
| 131 | |
| 132 /** | |
| 133 * Checks if the options contain any image processing. | |
| 134 * | |
| 135 * @param {number} width Source width. | |
| 136 * @param {number} height Source height. | |
| 137 * @param {Object} options Resizing options as a hash array. | |
| 138 * @return {boolean} True if yes, false if not. | |
| 139 */ | |
| 140 ImageLoader.shouldProcess = function(width, height, options) { | |
| 141 var targetDimensions = ImageLoader.resizeDimensions(width, height, options); | |
| 142 | |
| 143 // Dimensions has to be adjusted. | |
| 144 if (targetDimensions.width != width || targetDimensions.height != height) | |
| 145 return true; | |
| 146 | |
| 147 // Orientation has to be adjusted. | |
| 148 if (!options.orientation.isIdentity()) | |
| 149 return true; | |
| 150 | |
| 151 // Non-standard color space has to be converted. | |
| 152 if (options.colorSpace && options.colorSpace !== ColorSpace.SRGB) | |
| 153 return true; | |
| 154 | |
| 155 // No changes required. | |
| 156 return false; | |
| 157 }; | |
| 158 | |
| 159 /** | |
| 160 * Calculates dimensions taking into account resize options, such as: | |
| 161 * - scale: for scaling, | |
| 162 * - maxWidth, maxHeight: for maximum dimensions, | |
| 163 * - width, height: for exact requested size. | |
| 164 * Returns the target size as hash array with width, height properties. | |
| 165 * | |
| 166 * @param {number} width Source width. | |
| 167 * @param {number} height Source height. | |
| 168 * @param {Object} options Resizing options as a hash array. | |
| 169 * @return {Object} Dimensions, eg. {width: 100, height: 50}. | |
| 170 */ | |
| 171 ImageLoader.resizeDimensions = function(width, height, options) { | |
| 172 var scale = options.scale || 1; | |
| 173 var targetDimensions = options.orientation.getSizeAfterCancelling( | |
| 174 width * scale, height * scale); | |
| 175 var targetWidth = targetDimensions.width; | |
| 176 var targetHeight = targetDimensions.height; | |
| 177 | |
| 178 if (options.maxWidth && targetWidth > options.maxWidth) { | |
| 179 var scale = options.maxWidth / targetWidth; | |
| 180 targetWidth *= scale; | |
| 181 targetHeight *= scale; | |
| 182 } | |
| 183 | |
| 184 if (options.maxHeight && targetHeight > options.maxHeight) { | |
| 185 var scale = options.maxHeight / targetHeight; | |
| 186 targetWidth *= scale; | |
| 187 targetHeight *= scale; | |
| 188 } | |
| 189 | |
| 190 if (options.width) | |
| 191 targetWidth = options.width; | |
| 192 | |
| 193 if (options.height) | |
| 194 targetHeight = options.height; | |
| 195 | |
| 196 targetWidth = Math.round(targetWidth); | |
| 197 targetHeight = Math.round(targetHeight); | |
| 198 | |
| 199 return {width: targetWidth, height: targetHeight}; | |
| 200 }; | |
| 201 | |
| 202 /** | |
| 203 * Performs resizing and cropping of the source image into the target canvas. | |
| 204 * | |
| 205 * @param {HTMLCanvasElement|Image} source Source image or canvas. | |
| 206 * @param {HTMLCanvasElement} target Target canvas. | |
| 207 * @param {Object} options Resizing options as a hash array. | |
| 208 */ | |
| 209 ImageLoader.resizeAndCrop = function(source, target, options) { | |
| 210 // Calculates copy parameters. | |
| 211 var copyParameters = ImageLoader.calculateCopyParameters(source, options); | |
| 212 target.width = copyParameters.canvas.width; | |
| 213 target.height = copyParameters.canvas.height; | |
| 214 | |
| 215 // Apply. | |
| 216 var targetContext = | |
| 217 /** @type {CanvasRenderingContext2D} */ (target.getContext('2d')); | |
| 218 targetContext.save(); | |
| 219 options.orientation.cancelImageOrientation( | |
| 220 targetContext, copyParameters.target.width, copyParameters.target.height); | |
| 221 targetContext.drawImage( | |
| 222 source, | |
| 223 copyParameters.source.x, | |
| 224 copyParameters.source.y, | |
| 225 copyParameters.source.width, | |
| 226 copyParameters.source.height, | |
| 227 copyParameters.target.x, | |
| 228 copyParameters.target.y, | |
| 229 copyParameters.target.width, | |
| 230 copyParameters.target.height); | |
| 231 targetContext.restore(); | |
| 232 }; | |
| 233 | |
| 234 /** | |
| 235 * @typedef {{ | |
| 236 * source: {x:number, y:number, width:number, height:number}, | |
| 237 * target: {x:number, y:number, width:number, height:number}, | |
| 238 * canvas: {width:number, height:number} | |
| 239 * }} | |
| 240 */ | |
| 241 ImageLoader.CopyParameters; | |
| 242 | |
| 243 /** | |
| 244 * Calculates copy parameters. | |
| 245 * | |
| 246 * @param {HTMLCanvasElement|Image} source Source image or canvas. | |
| 247 * @param {Object} options Resizing options as a hash array. | |
| 248 * @return {!ImageLoader.CopyParameters} Calculated copy parameters. | |
| 249 */ | |
| 250 ImageLoader.calculateCopyParameters = function(source, options) { | |
| 251 if (options.crop) { | |
| 252 // When an image is cropped, target should be a fixed size square. | |
| 253 assert(options.width); | |
| 254 assert(options.height); | |
| 255 assert(options.width === options.height); | |
| 256 | |
| 257 // The length of shorter edge becomes dimension of cropped area in the | |
| 258 // source. | |
| 259 var cropSourceDimension = Math.min(source.width, source.height); | |
| 260 | |
| 261 return { | |
| 262 source: { | |
| 263 x: Math.floor((source.width / 2) - (cropSourceDimension / 2)), | |
| 264 y: Math.floor((source.height / 2) - (cropSourceDimension / 2)), | |
| 265 width: cropSourceDimension, | |
| 266 height: cropSourceDimension | |
| 267 }, | |
| 268 target: { | |
| 269 x: 0, | |
| 270 y: 0, | |
| 271 width: options.width, | |
| 272 height: options.height | |
| 273 }, | |
| 274 canvas: { | |
| 275 width: options.width, | |
| 276 height: options.height | |
| 277 } | |
| 278 }; | |
| 279 } | |
| 280 | |
| 281 // Target dimension is calculated in the rotated(transformed) coordinate. | |
| 282 var targetCanvasDimensions = ImageLoader.resizeDimensions( | |
| 283 source.width, source.height, options); | |
| 284 | |
| 285 var targetDimensions = options.orientation.getSizeAfterCancelling( | |
| 286 targetCanvasDimensions.width, targetCanvasDimensions.height); | |
| 287 | |
| 288 return { | |
| 289 source: { | |
| 290 x: 0, | |
| 291 y: 0, | |
| 292 width: source.width, | |
| 293 height: source.height | |
| 294 }, | |
| 295 target: { | |
| 296 x: 0, | |
| 297 y: 0, | |
| 298 width: targetDimensions.width, | |
| 299 height: targetDimensions.height | |
| 300 }, | |
| 301 canvas: { | |
| 302 width: targetCanvasDimensions.width, | |
| 303 height: targetCanvasDimensions.height | |
| 304 } | |
| 305 }; | |
| 306 }; | |
| 307 | |
| 308 /** | |
| 309 * Matrix converts AdobeRGB color space into sRGB color space. | |
| 310 * @const {!Array<number>} | |
| 311 */ | |
| 312 ImageLoader.MATRIX_FROM_ADOBE_TO_STANDARD = [ | |
| 313 1.39836, -0.39836, 0.00000, | |
| 314 0.00000, 1.00000, 0.00000, | |
| 315 0.00000, -0.04293, 1.04293 | |
| 316 ]; | |
| 317 | |
| 318 /** | |
| 319 * Converts the canvas of color space into sRGB. | |
| 320 * @param {HTMLCanvasElement} target Target canvas. | |
| 321 * @param {ColorSpace} colorSpace Current color space. | |
| 322 */ | |
| 323 ImageLoader.convertColorSpace = function(target, colorSpace) { | |
| 324 if (colorSpace === ColorSpace.SRGB) | |
| 325 return; | |
| 326 if (colorSpace === ColorSpace.ADOBE_RGB) { | |
| 327 var matrix = ImageLoader.MATRIX_FROM_ADOBE_TO_STANDARD; | |
| 328 var context = target.getContext('2d'); | |
| 329 var imageData = context.getImageData(0, 0, target.width, target.height); | |
| 330 var data = imageData.data; | |
| 331 for (var i = 0; i < data.length; i += 4) { | |
| 332 // Scale to [0, 1]. | |
| 333 var adobeR = data[i] / 255; | |
| 334 var adobeG = data[i + 1] / 255; | |
| 335 var adobeB = data[i + 2] / 255; | |
| 336 | |
| 337 // Revert gannma transformation. | |
| 338 adobeR = adobeR <= 0.0556 ? adobeR / 32 : Math.pow(adobeR, 2.2); | |
| 339 adobeG = adobeG <= 0.0556 ? adobeG / 32 : Math.pow(adobeG, 2.2); | |
| 340 adobeB = adobeB <= 0.0556 ? adobeB / 32 : Math.pow(adobeB, 2.2); | |
| 341 | |
| 342 // Convert color space. | |
| 343 var sR = matrix[0] * adobeR + matrix[1] * adobeG + matrix[2] * adobeB; | |
| 344 var sG = matrix[3] * adobeR + matrix[4] * adobeG + matrix[5] * adobeB; | |
| 345 var sB = matrix[6] * adobeR + matrix[7] * adobeG + matrix[8] * adobeB; | |
| 346 | |
| 347 // Gannma transformation. | |
| 348 sR = sR <= 0.0031308 ? 12.92 * sR : 1.055 * Math.pow(sR, 1 / 2.4) - 0.055; | |
| 349 sG = sG <= 0.0031308 ? 12.92 * sG : 1.055 * Math.pow(sG, 1 / 2.4) - 0.055; | |
| 350 sB = sB <= 0.0031308 ? 12.92 * sB : 1.055 * Math.pow(sB, 1 / 2.4) - 0.055; | |
| 351 | |
| 352 // Scale to [0, 255]. | |
| 353 data[i] = Math.max(0, Math.min(255, sR * 255)); | |
| 354 data[i + 1] = Math.max(0, Math.min(255, sG * 255)); | |
| 355 data[i + 2] = Math.max(0, Math.min(255, sB * 255)); | |
| 356 } | |
| 357 context.putImageData(imageData, 0, 0); | |
| 358 } | |
| 359 }; | |
| OLD | NEW |