328 lines
9.0 KiB
JavaScript
328 lines
9.0 KiB
JavaScript
/*
|
|
* JavaScript Load Image Scaling
|
|
* https://github.com/blueimp/JavaScript-Load-Image
|
|
*
|
|
* Copyright 2011, Sebastian Tschan
|
|
* https://blueimp.net
|
|
*
|
|
* Licensed under the MIT license:
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
/* global define, module, require */
|
|
|
|
;(function (factory) {
|
|
'use strict'
|
|
if (typeof define === 'function' && define.amd) {
|
|
// Register as an anonymous AMD module:
|
|
define(['./load-image'], factory)
|
|
} else if (typeof module === 'object' && module.exports) {
|
|
factory(require('./load-image'))
|
|
} else {
|
|
// Browser globals:
|
|
factory(window.loadImage)
|
|
}
|
|
})(function (loadImage) {
|
|
'use strict'
|
|
|
|
var originalTransform = loadImage.transform
|
|
|
|
loadImage.createCanvas = function (width, height, offscreen) {
|
|
if (offscreen && loadImage.global.OffscreenCanvas) {
|
|
return new OffscreenCanvas(width, height)
|
|
}
|
|
var canvas = document.createElement('canvas')
|
|
canvas.width = width
|
|
canvas.height = height
|
|
return canvas
|
|
}
|
|
|
|
loadImage.transform = function (img, options, callback, file, data) {
|
|
originalTransform.call(
|
|
loadImage,
|
|
loadImage.scale(img, options, data),
|
|
options,
|
|
callback,
|
|
file,
|
|
data
|
|
)
|
|
}
|
|
|
|
// Transform image coordinates, allows to override e.g.
|
|
// the canvas orientation based on the orientation option,
|
|
// gets canvas, options and data passed as arguments:
|
|
loadImage.transformCoordinates = function () {}
|
|
|
|
// Returns transformed options, allows to override e.g.
|
|
// maxWidth, maxHeight and crop options based on the aspectRatio.
|
|
// gets img, options, data passed as arguments:
|
|
loadImage.getTransformedOptions = function (img, options) {
|
|
var aspectRatio = options.aspectRatio
|
|
var newOptions
|
|
var i
|
|
var width
|
|
var height
|
|
if (!aspectRatio) {
|
|
return options
|
|
}
|
|
newOptions = {}
|
|
for (i in options) {
|
|
if (Object.prototype.hasOwnProperty.call(options, i)) {
|
|
newOptions[i] = options[i]
|
|
}
|
|
}
|
|
newOptions.crop = true
|
|
width = img.naturalWidth || img.width
|
|
height = img.naturalHeight || img.height
|
|
if (width / height > aspectRatio) {
|
|
newOptions.maxWidth = height * aspectRatio
|
|
newOptions.maxHeight = height
|
|
} else {
|
|
newOptions.maxWidth = width
|
|
newOptions.maxHeight = width / aspectRatio
|
|
}
|
|
return newOptions
|
|
}
|
|
|
|
// Canvas render method, allows to implement a different rendering algorithm:
|
|
loadImage.drawImage = function (
|
|
img,
|
|
canvas,
|
|
sourceX,
|
|
sourceY,
|
|
sourceWidth,
|
|
sourceHeight,
|
|
destWidth,
|
|
destHeight,
|
|
options
|
|
) {
|
|
var ctx = canvas.getContext('2d')
|
|
if (options.imageSmoothingEnabled === false) {
|
|
ctx.msImageSmoothingEnabled = false
|
|
ctx.imageSmoothingEnabled = false
|
|
} else if (options.imageSmoothingQuality) {
|
|
ctx.imageSmoothingQuality = options.imageSmoothingQuality
|
|
}
|
|
ctx.drawImage(
|
|
img,
|
|
sourceX,
|
|
sourceY,
|
|
sourceWidth,
|
|
sourceHeight,
|
|
0,
|
|
0,
|
|
destWidth,
|
|
destHeight
|
|
)
|
|
return ctx
|
|
}
|
|
|
|
// Determines if the target image should be a canvas element:
|
|
loadImage.requiresCanvas = function (options) {
|
|
return options.canvas || options.crop || !!options.aspectRatio
|
|
}
|
|
|
|
// Scales and/or crops the given image (img or canvas HTML element)
|
|
// using the given options:
|
|
loadImage.scale = function (img, options, data) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
options = options || {}
|
|
// eslint-disable-next-line no-param-reassign
|
|
data = data || {}
|
|
var useCanvas =
|
|
img.getContext ||
|
|
(loadImage.requiresCanvas(options) &&
|
|
!!loadImage.global.HTMLCanvasElement)
|
|
var width = img.naturalWidth || img.width
|
|
var height = img.naturalHeight || img.height
|
|
var destWidth = width
|
|
var destHeight = height
|
|
var maxWidth
|
|
var maxHeight
|
|
var minWidth
|
|
var minHeight
|
|
var sourceWidth
|
|
var sourceHeight
|
|
var sourceX
|
|
var sourceY
|
|
var pixelRatio
|
|
var downsamplingRatio
|
|
var tmp
|
|
var canvas
|
|
/**
|
|
* Scales up image dimensions
|
|
*/
|
|
function scaleUp() {
|
|
var scale = Math.max(
|
|
(minWidth || destWidth) / destWidth,
|
|
(minHeight || destHeight) / destHeight
|
|
)
|
|
if (scale > 1) {
|
|
destWidth *= scale
|
|
destHeight *= scale
|
|
}
|
|
}
|
|
/**
|
|
* Scales down image dimensions
|
|
*/
|
|
function scaleDown() {
|
|
var scale = Math.min(
|
|
(maxWidth || destWidth) / destWidth,
|
|
(maxHeight || destHeight) / destHeight
|
|
)
|
|
if (scale < 1) {
|
|
destWidth *= scale
|
|
destHeight *= scale
|
|
}
|
|
}
|
|
if (useCanvas) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
options = loadImage.getTransformedOptions(img, options, data)
|
|
sourceX = options.left || 0
|
|
sourceY = options.top || 0
|
|
if (options.sourceWidth) {
|
|
sourceWidth = options.sourceWidth
|
|
if (options.right !== undefined && options.left === undefined) {
|
|
sourceX = width - sourceWidth - options.right
|
|
}
|
|
} else {
|
|
sourceWidth = width - sourceX - (options.right || 0)
|
|
}
|
|
if (options.sourceHeight) {
|
|
sourceHeight = options.sourceHeight
|
|
if (options.bottom !== undefined && options.top === undefined) {
|
|
sourceY = height - sourceHeight - options.bottom
|
|
}
|
|
} else {
|
|
sourceHeight = height - sourceY - (options.bottom || 0)
|
|
}
|
|
destWidth = sourceWidth
|
|
destHeight = sourceHeight
|
|
}
|
|
maxWidth = options.maxWidth
|
|
maxHeight = options.maxHeight
|
|
minWidth = options.minWidth
|
|
minHeight = options.minHeight
|
|
if (useCanvas && maxWidth && maxHeight && options.crop) {
|
|
destWidth = maxWidth
|
|
destHeight = maxHeight
|
|
tmp = sourceWidth / sourceHeight - maxWidth / maxHeight
|
|
if (tmp < 0) {
|
|
sourceHeight = (maxHeight * sourceWidth) / maxWidth
|
|
if (options.top === undefined && options.bottom === undefined) {
|
|
sourceY = (height - sourceHeight) / 2
|
|
}
|
|
} else if (tmp > 0) {
|
|
sourceWidth = (maxWidth * sourceHeight) / maxHeight
|
|
if (options.left === undefined && options.right === undefined) {
|
|
sourceX = (width - sourceWidth) / 2
|
|
}
|
|
}
|
|
} else {
|
|
if (options.contain || options.cover) {
|
|
minWidth = maxWidth = maxWidth || minWidth
|
|
minHeight = maxHeight = maxHeight || minHeight
|
|
}
|
|
if (options.cover) {
|
|
scaleDown()
|
|
scaleUp()
|
|
} else {
|
|
scaleUp()
|
|
scaleDown()
|
|
}
|
|
}
|
|
if (useCanvas) {
|
|
pixelRatio = options.pixelRatio
|
|
if (
|
|
pixelRatio > 1 &&
|
|
// Check if the image has not yet had the device pixel ratio applied:
|
|
!(
|
|
img.style.width &&
|
|
Math.floor(parseFloat(img.style.width, 10)) ===
|
|
Math.floor(width / pixelRatio)
|
|
)
|
|
) {
|
|
destWidth *= pixelRatio
|
|
destHeight *= pixelRatio
|
|
}
|
|
// Check if workaround for Chromium orientation crop bug is required:
|
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=1074354
|
|
if (
|
|
loadImage.orientationCropBug &&
|
|
!img.getContext &&
|
|
(sourceX || sourceY || sourceWidth !== width || sourceHeight !== height)
|
|
) {
|
|
// Write the complete source image to an intermediate canvas first:
|
|
tmp = img
|
|
// eslint-disable-next-line no-param-reassign
|
|
img = loadImage.createCanvas(width, height, true)
|
|
loadImage.drawImage(
|
|
tmp,
|
|
img,
|
|
0,
|
|
0,
|
|
width,
|
|
height,
|
|
width,
|
|
height,
|
|
options
|
|
)
|
|
}
|
|
downsamplingRatio = options.downsamplingRatio
|
|
if (
|
|
downsamplingRatio > 0 &&
|
|
downsamplingRatio < 1 &&
|
|
destWidth < sourceWidth &&
|
|
destHeight < sourceHeight
|
|
) {
|
|
while (sourceWidth * downsamplingRatio > destWidth) {
|
|
canvas = loadImage.createCanvas(
|
|
sourceWidth * downsamplingRatio,
|
|
sourceHeight * downsamplingRatio,
|
|
true
|
|
)
|
|
loadImage.drawImage(
|
|
img,
|
|
canvas,
|
|
sourceX,
|
|
sourceY,
|
|
sourceWidth,
|
|
sourceHeight,
|
|
canvas.width,
|
|
canvas.height,
|
|
options
|
|
)
|
|
sourceX = 0
|
|
sourceY = 0
|
|
sourceWidth = canvas.width
|
|
sourceHeight = canvas.height
|
|
// eslint-disable-next-line no-param-reassign
|
|
img = canvas
|
|
}
|
|
}
|
|
canvas = loadImage.createCanvas(destWidth, destHeight)
|
|
loadImage.transformCoordinates(canvas, options, data)
|
|
if (pixelRatio > 1) {
|
|
canvas.style.width = canvas.width / pixelRatio + 'px'
|
|
}
|
|
loadImage
|
|
.drawImage(
|
|
img,
|
|
canvas,
|
|
sourceX,
|
|
sourceY,
|
|
sourceWidth,
|
|
sourceHeight,
|
|
destWidth,
|
|
destHeight,
|
|
options
|
|
)
|
|
.setTransform(1, 0, 0, 1, 0, 0) // reset to the identity matrix
|
|
return canvas
|
|
}
|
|
img.width = destWidth
|
|
img.height = destHeight
|
|
return img
|
|
}
|
|
})
|