blueimp-load-image
Version:
JavaScript Load Image is a library to load images provided as File or Blob objects or via URL. It returns an optionally scaled, cropped or rotated HTML img or canvas element. It also provides methods to parse image metadata to extract IPTC and Exif tags a
328 lines (317 loc) • 9.22 kB
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
}
})