Skip to content

Commit

Permalink
Merge pull request #112 from raccoonback/specify-max-canvas-size
Browse files Browse the repository at this point in the history
[bug] specify the maximum size of Canvas supported by each browser
  • Loading branch information
Donaldcwl authored Aug 10, 2021
2 parents 39665bd + 06376e5 commit 5be3de6
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 8 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ or
const options = {
maxSizeMB: number, // (default: Number.POSITIVE_INFINITY)
maxWidthOrHeight: number, // compressedFile will scale down by ratio to a point that width or height is smaller than maxWidthOrHeight (default: undefined)
// but, automatically reduce the size to smaller than the maximum Canvas size supported by each browser.
// Please check the Cabeat part for details.
onProgress: Function, // optional, a function takes one progress argument (percentage from 0 to 100)
useWebWorker: boolean, // optional, use multi-thread web worker, fallback to run in main-thread (default: true)

Expand All @@ -64,6 +66,20 @@ const options = {

imageCompression(file: File, options): Promise<File>
```

#### Caveat ####
Each browser limits [the maximum size](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#maximum_canvas_size) of a Canvas object. <br/>
So, we resize the image to less than the maximum size that each browser restricts. <br/>
(However, the `proportion/ratio` of the image remains.)

| Browser | Maximum height | Maximum width | Maximum area |
|---|---|---|---|
| Chrome | 32,767 pixels | 32,767 pixels | 268,435,456 pixels (i.e., 16,384 x 16,384) |
| Firefox | 32,767 pixels | 32,767 pixels | 472,907,776 pixels (i.e., 22,528 x 20,992) |
| Safari | 32,767 pixels | 32,767 pixels | 268,435,456 pixels (i.e., 16,384 x 16,384) |
| IE | 8,192 pixels | 8,192 pixels | ? |
| Etc | ? | ? | ? |

### Helper function ###
- for advanced users only, most users won't need to use the helper functions
```javascript
Expand Down
8 changes: 8 additions & 0 deletions lib/config/max-canvas-size.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#maximum_canvas_size
export default {
"chrome": 16384,
"firefox": 22528,
"safari": 16384,
"internet explorer": 8192,
"etc": 8192
}
61 changes: 60 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import UPNG from './UPNG'
import Bowser from "bowser";
import MaxCanvasSize from './config/max-canvas-size'

const isBrowser = typeof window !== 'undefined' // change browser environment to support SSR

Expand Down Expand Up @@ -102,14 +104,54 @@ export function loadImage (src) {
})
}

/**
* approximateBelowCanvasMaximumSizeOfBrowser
*
* it uses binary search to converge below the browser's maximum Canvas size.
*
* ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#maximum_canvas_size
*
* @param {number} initWidth
* @param {number} initHeight
* @returns {object}
*/
function approximateBelowMaximumCanvasSizeOfBrowser(initWidth, initHeight) {
const browserName = getBrowserName()
const maximumCanvasSize = MaxCanvasSize[browserName]

let width = initWidth;
let height = initHeight
let size = width * height
const ratio = width > height ? height / width : width / height

while(size > maximumCanvasSize * maximumCanvasSize) {
const halfSizeWidth = (maximumCanvasSize + width) / 2;
const halfSizeHeight = (maximumCanvasSize + height) / 2;
if(halfSizeWidth < halfSizeHeight) {
height = halfSizeHeight
width = halfSizeHeight * ratio
} else {
height = halfSizeWidth * ratio
width = halfSizeWidth
}

size = width * height
}

return {
width, height
}
}

/**
* drawImageInCanvas
*
* @param {HTMLImageElement} img
* @returns {HTMLCanvasElement | OffscreenCanvas}
*/
export function drawImageInCanvas (img) {
const [canvas, ctx] = getNewCanvasAndCtx(img.width, img.height)
const {width, height} = approximateBelowMaximumCanvasSizeOfBrowser(img.width, img.height)
const [canvas, ctx] = getNewCanvasAndCtx(width, height)
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
return canvas
}
Expand Down Expand Up @@ -350,3 +392,20 @@ if (isBrowser) {
Math.floor(value) === value
}
}

/**
* getBrowserName
*
* Extracts the browser name from the useragent.
*
* @returns {string}
*/
export function getBrowserName() {
const browserName = Bowser.parse(globalThis.navigator.userAgent).browser.name || ''
const lowerCasedBrowserName = browserName.toLowerCase()
if(['chrome', 'safari', 'firefox'].includes(lowerCasedBrowserName)) {
return lowerCasedBrowserName
}

return 'etc'
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"dist"
],
"dependencies": {
"bowser": "^2.11.0",
"uzip": "0.20201231.0"
},
"devDependencies": {
Expand Down
14 changes: 9 additions & 5 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import path from 'path'
const pkg = require('./package.json')
const notExternal = [
// 'pako',
'uzip'
'uzip',
'bowser',
]
const external = Object.keys(pkg.dependencies).filter(value => !notExternal.includes(value))

Expand All @@ -19,7 +20,7 @@ let plugins = [
babel(),
terser({
keep_fnames: true,
mangle: { reserved: ['CustomFile', 'CustomFileReader', 'UPNG', 'UZIP'] }
mangle: { reserved: ['CustomFile', 'CustomFileReader', 'UPNG', 'UZIP', 'bowser'] }
}),
license({
sourcemap: true,
Expand All @@ -46,7 +47,8 @@ export default {
sourcemap: true,
globals: {
// pako: 'pako',
uzip: 'UZIP'
uzip: 'UZIP',
bowser: 'bowser'
}
},
{
Expand All @@ -55,8 +57,10 @@ export default {
sourcemap: true,
globals: {
// pako: 'pako',
uzip: 'UZIP'
uzip: 'UZIP',
bowser: 'bowser'
}
}
},

]
}
4 changes: 2 additions & 2 deletions test/setup_jsdom.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ if (typeof window.Worker === 'undefined') {
}

global.window = window
const KEYS = ['document', 'Blob', 'File', 'URL', 'Worker', 'FileReader', 'atob', 'Uint8Array', 'Image', 'HTMLCanvasElement', 'HTMLImageElement']
const KEYS = ['document', 'navigator', 'Blob', 'File', 'URL', 'Worker', 'FileReader', 'atob', 'Uint8Array', 'Image', 'HTMLCanvasElement', 'HTMLImageElement']
KEYS.forEach(key => global[key] = window[key])

if (typeof window.URL.createObjectURL === 'undefined') {
Object.defineProperty(window.URL, 'createObjectURL', { value: () => {} })
}
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1219,6 +1219,11 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==

bowser@^2.11.0:
version "2.11.0"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f"
integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==

brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
Expand Down

0 comments on commit 5be3de6

Please sign in to comment.