load-image-scale.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /*
  2. * JavaScript Load Image Scaling
  3. * https://github.com/blueimp/JavaScript-Load-Image
  4. *
  5. * Copyright 2011, Sebastian Tschan
  6. * https://blueimp.net
  7. *
  8. * Licensed under the MIT license:
  9. * https://opensource.org/licenses/MIT
  10. */
  11. /* global define, module, require */
  12. ;(function(factory) {
  13. 'use strict'
  14. if (typeof define === 'function' && define.amd) {
  15. // Register as an anonymous AMD module:
  16. define(['./load-image'], factory)
  17. } else if (typeof module === 'object' && module.exports) {
  18. factory(require('./load-image'))
  19. } else {
  20. // Browser globals:
  21. factory(window.loadImage)
  22. }
  23. })(function(loadImage) {
  24. 'use strict'
  25. var originalTransform = loadImage.transform
  26. loadImage.transform = function(img, options, callback, file, data) {
  27. originalTransform.call(
  28. loadImage,
  29. loadImage.scale(img, options, data),
  30. options,
  31. callback,
  32. file,
  33. data
  34. )
  35. }
  36. // Transform image coordinates, allows to override e.g.
  37. // the canvas orientation based on the orientation option,
  38. // gets canvas, options passed as arguments:
  39. loadImage.transformCoordinates = function() {}
  40. // Returns transformed options, allows to override e.g.
  41. // maxWidth, maxHeight and crop options based on the aspectRatio.
  42. // gets img, options passed as arguments:
  43. loadImage.getTransformedOptions = function(img, options) {
  44. var aspectRatio = options.aspectRatio
  45. var newOptions
  46. var i
  47. var width
  48. var height
  49. if (!aspectRatio) {
  50. return options
  51. }
  52. newOptions = {}
  53. for (i in options) {
  54. if (Object.prototype.hasOwnProperty.call(options, i)) {
  55. newOptions[i] = options[i]
  56. }
  57. }
  58. newOptions.crop = true
  59. width = img.naturalWidth || img.width
  60. height = img.naturalHeight || img.height
  61. if (width / height > aspectRatio) {
  62. newOptions.maxWidth = height * aspectRatio
  63. newOptions.maxHeight = height
  64. } else {
  65. newOptions.maxWidth = width
  66. newOptions.maxHeight = width / aspectRatio
  67. }
  68. return newOptions
  69. }
  70. // Canvas render method, allows to implement a different rendering algorithm:
  71. loadImage.renderImageToCanvas = function(
  72. canvas,
  73. img,
  74. sourceX,
  75. sourceY,
  76. sourceWidth,
  77. sourceHeight,
  78. destX,
  79. destY,
  80. destWidth,
  81. destHeight
  82. ) {
  83. canvas
  84. .getContext('2d')
  85. .drawImage(
  86. img,
  87. sourceX,
  88. sourceY,
  89. sourceWidth,
  90. sourceHeight,
  91. destX,
  92. destY,
  93. destWidth,
  94. destHeight
  95. )
  96. return canvas
  97. }
  98. // Determines if the target image should be a canvas element:
  99. loadImage.hasCanvasOption = function(options) {
  100. return options.canvas || options.crop || !!options.aspectRatio
  101. }
  102. // Scales and/or crops the given image (img or canvas HTML element)
  103. // using the given options.
  104. // Returns a canvas object if the browser supports canvas
  105. // and the hasCanvasOption method returns true or a canvas
  106. // object is passed as image, else the scaled image:
  107. loadImage.scale = function(img, options, data) {
  108. // eslint-disable-next-line no-param-reassign
  109. options = options || {}
  110. var canvas = document.createElement('canvas')
  111. var useCanvas =
  112. img.getContext ||
  113. (loadImage.hasCanvasOption(options) && canvas.getContext)
  114. var width = img.naturalWidth || img.width
  115. var height = img.naturalHeight || img.height
  116. var destWidth = width
  117. var destHeight = height
  118. var maxWidth
  119. var maxHeight
  120. var minWidth
  121. var minHeight
  122. var sourceWidth
  123. var sourceHeight
  124. var sourceX
  125. var sourceY
  126. var pixelRatio
  127. var downsamplingRatio
  128. var tmp
  129. /**
  130. * Scales up image dimensions
  131. */
  132. function scaleUp() {
  133. var scale = Math.max(
  134. (minWidth || destWidth) / destWidth,
  135. (minHeight || destHeight) / destHeight
  136. )
  137. if (scale > 1) {
  138. destWidth *= scale
  139. destHeight *= scale
  140. }
  141. }
  142. /**
  143. * Scales down image dimensions
  144. */
  145. function scaleDown() {
  146. var scale = Math.min(
  147. (maxWidth || destWidth) / destWidth,
  148. (maxHeight || destHeight) / destHeight
  149. )
  150. if (scale < 1) {
  151. destWidth *= scale
  152. destHeight *= scale
  153. }
  154. }
  155. if (useCanvas) {
  156. // eslint-disable-next-line no-param-reassign
  157. options = loadImage.getTransformedOptions(img, options, data)
  158. sourceX = options.left || 0
  159. sourceY = options.top || 0
  160. if (options.sourceWidth) {
  161. sourceWidth = options.sourceWidth
  162. if (options.right !== undefined && options.left === undefined) {
  163. sourceX = width - sourceWidth - options.right
  164. }
  165. } else {
  166. sourceWidth = width - sourceX - (options.right || 0)
  167. }
  168. if (options.sourceHeight) {
  169. sourceHeight = options.sourceHeight
  170. if (options.bottom !== undefined && options.top === undefined) {
  171. sourceY = height - sourceHeight - options.bottom
  172. }
  173. } else {
  174. sourceHeight = height - sourceY - (options.bottom || 0)
  175. }
  176. destWidth = sourceWidth
  177. destHeight = sourceHeight
  178. }
  179. maxWidth = options.maxWidth
  180. maxHeight = options.maxHeight
  181. minWidth = options.minWidth
  182. minHeight = options.minHeight
  183. if (useCanvas && maxWidth && maxHeight && options.crop) {
  184. destWidth = maxWidth
  185. destHeight = maxHeight
  186. tmp = sourceWidth / sourceHeight - maxWidth / maxHeight
  187. if (tmp < 0) {
  188. sourceHeight = (maxHeight * sourceWidth) / maxWidth
  189. if (options.top === undefined && options.bottom === undefined) {
  190. sourceY = (height - sourceHeight) / 2
  191. }
  192. } else if (tmp > 0) {
  193. sourceWidth = (maxWidth * sourceHeight) / maxHeight
  194. if (options.left === undefined && options.right === undefined) {
  195. sourceX = (width - sourceWidth) / 2
  196. }
  197. }
  198. } else {
  199. if (options.contain || options.cover) {
  200. minWidth = maxWidth = maxWidth || minWidth
  201. minHeight = maxHeight = maxHeight || minHeight
  202. }
  203. if (options.cover) {
  204. scaleDown()
  205. scaleUp()
  206. } else {
  207. scaleUp()
  208. scaleDown()
  209. }
  210. }
  211. if (useCanvas) {
  212. pixelRatio = options.pixelRatio
  213. if (pixelRatio > 1) {
  214. canvas.style.width = destWidth + 'px'
  215. canvas.style.height = destHeight + 'px'
  216. destWidth *= pixelRatio
  217. destHeight *= pixelRatio
  218. canvas.getContext('2d').scale(pixelRatio, pixelRatio)
  219. }
  220. downsamplingRatio = options.downsamplingRatio
  221. if (
  222. downsamplingRatio > 0 &&
  223. downsamplingRatio < 1 &&
  224. destWidth < sourceWidth &&
  225. destHeight < sourceHeight
  226. ) {
  227. while (sourceWidth * downsamplingRatio > destWidth) {
  228. canvas.width = sourceWidth * downsamplingRatio
  229. canvas.height = sourceHeight * downsamplingRatio
  230. loadImage.renderImageToCanvas(
  231. canvas,
  232. img,
  233. sourceX,
  234. sourceY,
  235. sourceWidth,
  236. sourceHeight,
  237. 0,
  238. 0,
  239. canvas.width,
  240. canvas.height
  241. )
  242. sourceX = 0
  243. sourceY = 0
  244. sourceWidth = canvas.width
  245. sourceHeight = canvas.height
  246. // eslint-disable-next-line no-param-reassign
  247. img = document.createElement('canvas')
  248. img.width = sourceWidth
  249. img.height = sourceHeight
  250. loadImage.renderImageToCanvas(
  251. img,
  252. canvas,
  253. 0,
  254. 0,
  255. sourceWidth,
  256. sourceHeight,
  257. 0,
  258. 0,
  259. sourceWidth,
  260. sourceHeight
  261. )
  262. }
  263. }
  264. canvas.width = destWidth
  265. canvas.height = destHeight
  266. loadImage.transformCoordinates(canvas, options)
  267. return loadImage.renderImageToCanvas(
  268. canvas,
  269. img,
  270. sourceX,
  271. sourceY,
  272. sourceWidth,
  273. sourceHeight,
  274. 0,
  275. 0,
  276. destWidth,
  277. destHeight
  278. )
  279. }
  280. img.width = destWidth
  281. img.height = destHeight
  282. return img
  283. }
  284. })