Skip to content

Commit 5be3de6

Browse files
authored
Merge pull request #112 from raccoonback/specify-max-canvas-size
[bug] specify the maximum size of Canvas supported by each browser
2 parents 39665bd + 06376e5 commit 5be3de6

File tree

7 files changed

+101
-8
lines changed

7 files changed

+101
-8
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ or
5252
const options = {
5353
maxSizeMB: number, // (default: Number.POSITIVE_INFINITY)
5454
maxWidthOrHeight: number, // compressedFile will scale down by ratio to a point that width or height is smaller than maxWidthOrHeight (default: undefined)
55+
// but, automatically reduce the size to smaller than the maximum Canvas size supported by each browser.
56+
// Please check the Cabeat part for details.
5557
onProgress: Function, // optional, a function takes one progress argument (percentage from 0 to 100)
5658
useWebWorker: boolean, // optional, use multi-thread web worker, fallback to run in main-thread (default: true)
5759

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

6567
imageCompression(file: File, options): Promise<File>
6668
```
69+
70+
#### Caveat ####
71+
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/>
72+
So, we resize the image to less than the maximum size that each browser restricts. <br/>
73+
(However, the `proportion/ratio` of the image remains.)
74+
75+
| Browser | Maximum height | Maximum width | Maximum area |
76+
|---|---|---|---|
77+
| Chrome | 32,767 pixels | 32,767 pixels | 268,435,456 pixels (i.e., 16,384 x 16,384) |
78+
| Firefox | 32,767 pixels | 32,767 pixels | 472,907,776 pixels (i.e., 22,528 x 20,992) |
79+
| Safari | 32,767 pixels | 32,767 pixels | 268,435,456 pixels (i.e., 16,384 x 16,384) |
80+
| IE | 8,192 pixels | 8,192 pixels | ? |
81+
| Etc | ? | ? | ? |
82+
6783
### Helper function ###
6884
- for advanced users only, most users won't need to use the helper functions
6985
```javascript

lib/config/max-canvas-size.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#maximum_canvas_size
2+
export default {
3+
"chrome": 16384,
4+
"firefox": 22528,
5+
"safari": 16384,
6+
"internet explorer": 8192,
7+
"etc": 8192
8+
}

lib/utils.js

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import UPNG from './UPNG'
2+
import Bowser from "bowser";
3+
import MaxCanvasSize from './config/max-canvas-size'
24

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

@@ -102,14 +104,54 @@ export function loadImage (src) {
102104
})
103105
}
104106

107+
/**
108+
* approximateBelowCanvasMaximumSizeOfBrowser
109+
*
110+
* it uses binary search to converge below the browser's maximum Canvas size.
111+
*
112+
* ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#maximum_canvas_size
113+
*
114+
* @param {number} initWidth
115+
* @param {number} initHeight
116+
* @returns {object}
117+
*/
118+
function approximateBelowMaximumCanvasSizeOfBrowser(initWidth, initHeight) {
119+
const browserName = getBrowserName()
120+
const maximumCanvasSize = MaxCanvasSize[browserName]
121+
122+
let width = initWidth;
123+
let height = initHeight
124+
let size = width * height
125+
const ratio = width > height ? height / width : width / height
126+
127+
while(size > maximumCanvasSize * maximumCanvasSize) {
128+
const halfSizeWidth = (maximumCanvasSize + width) / 2;
129+
const halfSizeHeight = (maximumCanvasSize + height) / 2;
130+
if(halfSizeWidth < halfSizeHeight) {
131+
height = halfSizeHeight
132+
width = halfSizeHeight * ratio
133+
} else {
134+
height = halfSizeWidth * ratio
135+
width = halfSizeWidth
136+
}
137+
138+
size = width * height
139+
}
140+
141+
return {
142+
width, height
143+
}
144+
}
145+
105146
/**
106147
* drawImageInCanvas
107148
*
108149
* @param {HTMLImageElement} img
109150
* @returns {HTMLCanvasElement | OffscreenCanvas}
110151
*/
111152
export function drawImageInCanvas (img) {
112-
const [canvas, ctx] = getNewCanvasAndCtx(img.width, img.height)
153+
const {width, height} = approximateBelowMaximumCanvasSizeOfBrowser(img.width, img.height)
154+
const [canvas, ctx] = getNewCanvasAndCtx(width, height)
113155
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
114156
return canvas
115157
}
@@ -350,3 +392,20 @@ if (isBrowser) {
350392
Math.floor(value) === value
351393
}
352394
}
395+
396+
/**
397+
* getBrowserName
398+
*
399+
* Extracts the browser name from the useragent.
400+
*
401+
* @returns {string}
402+
*/
403+
export function getBrowserName() {
404+
const browserName = Bowser.parse(globalThis.navigator.userAgent).browser.name || ''
405+
const lowerCasedBrowserName = browserName.toLowerCase()
406+
if(['chrome', 'safari', 'firefox'].includes(lowerCasedBrowserName)) {
407+
return lowerCasedBrowserName
408+
}
409+
410+
return 'etc'
411+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"dist"
3939
],
4040
"dependencies": {
41+
"bowser": "^2.11.0",
4142
"uzip": "0.20201231.0"
4243
},
4344
"devDependencies": {

rollup.config.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import path from 'path'
1010
const pkg = require('./package.json')
1111
const notExternal = [
1212
// 'pako',
13-
'uzip'
13+
'uzip',
14+
'bowser',
1415
]
1516
const external = Object.keys(pkg.dependencies).filter(value => !notExternal.includes(value))
1617

@@ -19,7 +20,7 @@ let plugins = [
1920
babel(),
2021
terser({
2122
keep_fnames: true,
22-
mangle: { reserved: ['CustomFile', 'CustomFileReader', 'UPNG', 'UZIP'] }
23+
mangle: { reserved: ['CustomFile', 'CustomFileReader', 'UPNG', 'UZIP', 'bowser'] }
2324
}),
2425
license({
2526
sourcemap: true,
@@ -46,7 +47,8 @@ export default {
4647
sourcemap: true,
4748
globals: {
4849
// pako: 'pako',
49-
uzip: 'UZIP'
50+
uzip: 'UZIP',
51+
bowser: 'bowser'
5052
}
5153
},
5254
{
@@ -55,8 +57,10 @@ export default {
5557
sourcemap: true,
5658
globals: {
5759
// pako: 'pako',
58-
uzip: 'UZIP'
60+
uzip: 'UZIP',
61+
bowser: 'bowser'
5962
}
60-
}
63+
},
64+
6165
]
6266
}

test/setup_jsdom.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ if (typeof window.Worker === 'undefined') {
88
}
99

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

1414
if (typeof window.URL.createObjectURL === 'undefined') {
1515
Object.defineProperty(window.URL, 'createObjectURL', { value: () => {} })
16-
}
16+
}

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,11 @@ binary-extensions@^2.0.0:
12191219
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
12201220
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
12211221

1222+
bowser@^2.11.0:
1223+
version "2.11.0"
1224+
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f"
1225+
integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==
1226+
12221227
brace-expansion@^1.1.7:
12231228
version "1.1.11"
12241229
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"

0 commit comments

Comments
 (0)