@applitools/screenshoter 3.2.9 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongo/dry-run/package-lock.json +11 -11
- package/.bongo/dry-run.tgz +0 -0
- package/CHANGELOG.md +6 -0
- package/index.js +2 -1
- package/logs/screenshot_2021_12_16_19_06_06_332Z_full_app_failed_1639681566332.png +0 -0
- package/logs/screenshot_2021_12_16_19_15_59_935Z_full_app_failed_1639682159935.png +0 -0
- package/logs/screenshot_2021_12_16_19_33_20_679Z_full_app_failed_1639683200679.png +0 -0
- package/logs/screenshot_2021_12_16_19_37_22_120Z_ios_viewport_failed.png +0 -0
- package/logs/screenshot_2021_12_16_19_38_09_461Z_ios_full_page_failed.png +0 -0
- package/logs/screenshot_2021_12_16_19_59_45_182Z_ios_viewport_failed.png +0 -0
- package/package.json +5 -5
- package/src/find-image-pattern.js +10 -38
- package/src/image.js +104 -64
- package/src/take-screenshot.js +136 -160
- package/src/take-simple-screenshot.js +25 -0
- package/src/take-stitched-screenshot.js +32 -52
- package/src/take-viewport-screenshot.js +179 -16
- package/test/e2e/android.spec.js +42 -11
- package/test/e2e/external.spec.js +81 -10
- package/test/e2e/ios.spec.js +48 -10
- package/test/e2e/web-ios.spec.js +3 -5
- package/test/e2e/web.spec.js +20 -15
- package/test/fixtures/android/app-fully-non-scrollable.png +0 -0
- package/test/fixtures/android/app-fully-recycler.png +0 -0
- package/test/fixtures/android/app-fully-scroll-statusbar.png +0 -0
- package/test/fixtures/android/app-fully-scroll.png +0 -0
- package/test/fixtures/android/app-statusbar.png +0 -0
- package/test/fixtures/android/x-app-fully-collapsing.png +0 -0
- package/test/fixtures/android/x-app-fully-recycler.png +0 -0
- package/test/fixtures/android/x-element-fully.png +0 -0
- package/test/fixtures/image/{house.combined-higher-wider.png → house.framed-higher-wider.png} +0 -0
- package/test/fixtures/image/{house.combined-higher.png → house.framed-higher.png} +0 -0
- package/test/fixtures/image/house.framed-shorter-thinner.png +0 -0
- package/test/fixtures/image/{house.combined-wider.png → house.framed-wider.png} +0 -0
- package/test/fixtures/ios/app-fully-collapsing.png +0 -0
- package/test/fixtures/ios/app-fully-collection.png +0 -0
- package/test/fixtures/ios/app-fully-overlapped-statusbar.png +0 -0
- package/test/fixtures/ios/app-fully-overlapped.png +0 -0
- package/test/fixtures/ios/app-fully-scroll-statusbar.png +0 -0
- package/test/fixtures/ios/app-fully-scroll.png +0 -0
- package/test/fixtures/ios/app-fully-superview.png +0 -0
- package/test/fixtures/ios/app-fully-table.png +0 -0
- package/test/fixtures/ios/app-statusbar.png +0 -0
- package/test/fixtures/ios/app.png +0 -0
- package/test/fixtures/ios/element-fully.png +0 -0
- package/test/fixtures/ios/element.png +0 -0
- package/test/fixtures/ios/region.png +0 -0
- package/test/fixtures/pattern/iPad_5th_landscape.png +0 -0
- package/test/fixtures/pattern/iPad_5th_portrait.png +0 -0
- package/test/fixtures/pattern/iPad_9th_landscape.png +0 -0
- package/test/fixtures/pattern/iPad_9th_portrait.png +0 -0
- package/test/fixtures/pattern/iPhone_11_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_11_portrait.png +0 -0
- package/test/fixtures/pattern/iPhone_13_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_13_portrait.png +0 -0
- package/test/fixtures/pattern/iPhone_SE_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_SE_portrait.png +0 -0
- package/test/fixtures/pattern/iPhone_XS_portrait_noviewport.png +0 -0
- package/test/fixtures/web-ios/page-fully.png +0 -0
- package/test/fixtures/web-ios/page.png +0 -0
- package/test/it/find-pattern.spec.js +16 -11
- package/test/it/image.spec.js +42 -15
- package/logs/screenshot_2021_11_14_12_35_00_342Z_full_frame_failed.png +0 -0
- package/logs/screenshot_2021_11_14_12_38_00_715Z_frame_failed.png +0 -0
- package/logs/screenshot_2021_11_14_12_38_03_866Z_frame_failed.png +0 -0
- package/logs/screenshot_2021_11_14_13_02_56_464Z_full_app_failed_1636894976464.png +0 -0
- package/logs/screenshot_2021_11_14_13_04_27_904Z_full_app_failed_1636895067904.png +0 -0
- package/logs/screenshot_2021_11_14_13_06_13_662Z_full_app_failed_1636895173662.png +0 -0
- package/logs/screenshot_2021_11_14_13_06_23_745Z_full_app_failed_1636895183745.png +0 -0
- package/logs/screenshot_2021_11_14_13_18_31_571Z_full_app_failed_1636895911571.png +0 -0
- package/logs/screenshot_2021_11_14_13_25_54_557Z_viewport_failed_1636896354557.png +0 -0
- package/logs/screenshot_2021_11_14_13_29_32_326Z_viewport_failed_1636896572326.png +0 -0
- package/logs/screenshot_2021_11_14_13_34_22_483Z_viewport_failed_1636896862483.png +0 -0
- package/logs/screenshot_2021_11_14_13_37_25_734Z_viewport_failed_1636897045734.png +0 -0
- package/logs/screenshot_2021_11_14_13_42_25_024Z_viewport_failed_1636897345024.png +0 -0
- package/logs/screenshot_2021_11_14_13_57_24_366Z_full_app_failed_1636898244366.png +0 -0
- package/logs/screenshot_2021_11_14_14_20_42_951Z_full_app_failed_1636899642951.png +0 -0
- package/logs/screenshot_2021_11_14_14_31_07_853Z_full_app_failed_1636900267853.png +0 -0
- package/logs/screenshot_2021_11_14_14_32_07_195Z_full_app_failed_1636900327195.png +0 -0
- package/logs/screenshot_2021_11_14_14_42_16_716Z_full_app_failed_1636900936716.png +0 -0
- package/logs/screenshot_2021_11_14_14_47_37_646Z_full_app_failed_1636901257646.png +0 -0
- package/logs/screenshot_2021_11_14_14_54_18_522Z_full_app_failed_1636901658522.png +0 -0
- package/logs/screenshot_2021_11_14_14_55_36_756Z_full_app_failed_1636901736756.png +0 -0
- package/logs/screenshot_2021_11_14_15_00_26_000Z_full_app_failed_1636902026000.png +0 -0
- package/logs/screenshot_2021_11_14_15_04_13_598Z_full_app_failed_1636902253598.png +0 -0
- package/logs/screenshot_2021_11_14_15_07_37_914Z_full_app_failed_1636902457914.png +0 -0
- package/logs/screenshot_2021_11_14_15_12_20_039Z_full_app_failed_1636902740039.png +0 -0
- package/logs/screenshot_2021_11_14_15_15_44_401Z_full_app_failed_1636902944401.png +0 -0
- package/logs/screenshot_2021_11_14_15_26_23_318Z_viewport_failed_1636903583318.png +0 -0
- package/src/calculate-screenshot-regions.js +0 -31
- package/src/screenshoter.js +0 -159
- package/test/fixtures/pattern/iPad_Air_portrait.png +0 -0
- package/test/fixtures/pattern/iPhone_5S_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_XR_perfecto_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_XS_Max_perfecto_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_XS_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_XS_portrait.png +0 -0
- package/test/fixtures/pattern/iPhone_X_perfecto_portrait.png +0 -0
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
11
|
"node_modules/@applitools/screenshoter": {
|
|
12
|
-
"version": "3.2.
|
|
12
|
+
"version": "3.2.9",
|
|
13
13
|
"resolved": "file:../dry-run.tgz",
|
|
14
|
-
"integrity": "sha512-
|
|
14
|
+
"integrity": "sha512-a9s+yYo2bQ61Nyt/Enqrjgi7BjHV97c5LrEREO2iFisgdq+SzNHaVOav63UIQ9a5qvMAAetyZgU2NgVtPNNWow==",
|
|
15
15
|
"license": "SEE LICENSE IN LICENSE",
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@applitools/snippets": "2.1.
|
|
17
|
+
"@applitools/snippets": "2.1.8",
|
|
18
18
|
"@applitools/utils": "1.2.4",
|
|
19
19
|
"png-async": "0.9.4"
|
|
20
20
|
},
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"node_modules/@applitools/snippets": {
|
|
26
|
-
"version": "2.1.
|
|
27
|
-
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.
|
|
28
|
-
"integrity": "sha512-
|
|
26
|
+
"version": "2.1.8",
|
|
27
|
+
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.8.tgz",
|
|
28
|
+
"integrity": "sha512-7CGFsbL9vAd6MBiGLVKHpKYywPXN499/fJMzEAVCLuGCY81CIc/PchqPFWrZZKZOY/IV21RJmE3MvqZAeXTIXA==",
|
|
29
29
|
"engines": {
|
|
30
30
|
"node": ">=8.9.0"
|
|
31
31
|
}
|
|
@@ -47,17 +47,17 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@applitools/screenshoter": {
|
|
49
49
|
"version": "file:../dry-run.tgz",
|
|
50
|
-
"integrity": "sha512-
|
|
50
|
+
"integrity": "sha512-a9s+yYo2bQ61Nyt/Enqrjgi7BjHV97c5LrEREO2iFisgdq+SzNHaVOav63UIQ9a5qvMAAetyZgU2NgVtPNNWow==",
|
|
51
51
|
"requires": {
|
|
52
|
-
"@applitools/snippets": "2.1.
|
|
52
|
+
"@applitools/snippets": "2.1.8",
|
|
53
53
|
"@applitools/utils": "1.2.4",
|
|
54
54
|
"png-async": "0.9.4"
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
"@applitools/snippets": {
|
|
58
|
-
"version": "2.1.
|
|
59
|
-
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.
|
|
60
|
-
"integrity": "sha512-
|
|
58
|
+
"version": "2.1.8",
|
|
59
|
+
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.8.tgz",
|
|
60
|
+
"integrity": "sha512-7CGFsbL9vAd6MBiGLVKHpKYywPXN499/fJMzEAVCLuGCY81CIc/PchqPFWrZZKZOY/IV21RJmE3MvqZAeXTIXA=="
|
|
61
61
|
},
|
|
62
62
|
"@applitools/utils": {
|
|
63
63
|
"version": "1.2.4",
|
package/.bongo/dry-run.tgz
CHANGED
|
Binary file
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
## Unreleased
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
## 3.3.0 - 2021/12/16
|
|
8
|
+
|
|
9
|
+
- fix ios web screenshots on pages without viewport meta tag
|
|
10
|
+
- improve native apps support
|
|
11
|
+
- updated to @applitools/snippets@2.1.8 (from 2.1.7)
|
|
12
|
+
|
|
7
13
|
## 3.2.9 - 2021/11/14
|
|
8
14
|
|
|
9
15
|
- add support of scrollable elements that change its size during scrolling
|
package/index.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
module.exports = require('./src/
|
|
1
|
+
module.exports = require('./src/take-screenshot')
|
|
2
|
+
exports.takeScreenshot = require('./src/take-screenshot')
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applitools/screenshoter",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Applitools universal screenshoter for web and native applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"applitools",
|
|
@@ -49,15 +49,15 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@applitools/snippets": "2.1.
|
|
52
|
+
"@applitools/snippets": "2.1.8",
|
|
53
53
|
"@applitools/utils": "1.2.4",
|
|
54
54
|
"png-async": "0.9.4"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@applitools/driver": "1.
|
|
57
|
+
"@applitools/driver": "1.4.1",
|
|
58
58
|
"@applitools/sdk-release-kit": "0.13.4",
|
|
59
|
-
"@applitools/spec-driver-webdriverio": "1.2.
|
|
60
|
-
"@applitools/test-utils": "1.0.
|
|
59
|
+
"@applitools/spec-driver-webdriverio": "1.2.2",
|
|
60
|
+
"@applitools/test-utils": "1.0.10",
|
|
61
61
|
"chromedriver": "^95.0.0",
|
|
62
62
|
"eslint": "^7.9.0",
|
|
63
63
|
"eslint-plugin-mocha-no-only": "^1.1.1",
|
|
@@ -1,60 +1,32 @@
|
|
|
1
1
|
function findImagePattern(image, pattern) {
|
|
2
2
|
for (let pixel = 0; pixel < image.width * image.height; ++pixel) {
|
|
3
3
|
if (isPattern(image, pixel, pattern)) {
|
|
4
|
-
return {
|
|
5
|
-
x: (pixel % image.width) - pattern.offset,
|
|
6
|
-
y: Math.floor(pixel / image.width) - pattern.offset,
|
|
7
|
-
}
|
|
4
|
+
return {x: (pixel % image.width) - pattern.offset, y: Math.floor(pixel / image.width) - pattern.offset}
|
|
8
5
|
}
|
|
9
6
|
}
|
|
10
7
|
return null
|
|
11
8
|
}
|
|
12
9
|
|
|
13
10
|
function isPattern(image, index, pattern) {
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const sideLength = pattern.size - round * 2
|
|
20
|
-
const stepsNumber = sideLength * channels - channels
|
|
21
|
-
const threshold = Math.min((roundNumber - round) * 10 + 10, 100)
|
|
22
|
-
for (let step = 0; step < stepsNumber; ++step) {
|
|
23
|
-
let pixelIndex = pixelOffset + round + round * image.width
|
|
24
|
-
|
|
25
|
-
if (step < sideLength) {
|
|
26
|
-
pixelIndex += step
|
|
27
|
-
} else if (step < sideLength * 2 - 1) {
|
|
28
|
-
pixelIndex += sideLength - 1 + ((step % sideLength) + 1) * image.width
|
|
29
|
-
} else if (step < sideLength * 3 - 2) {
|
|
30
|
-
pixelIndex += (sideLength - 1) * image.width + (sideLength - (step % sideLength) - 1)
|
|
31
|
-
} else {
|
|
32
|
-
pixelIndex += (step % sideLength) * image.width
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const pixelColor = pixelColorAt(image, pixelIndex, threshold)
|
|
36
|
-
if (pixelColor !== chunkColor) {
|
|
37
|
-
return false
|
|
38
|
-
}
|
|
39
|
-
}
|
|
11
|
+
const itemLength = pattern.size * pattern.pixelRatio
|
|
12
|
+
for (const [itemIndex, itemColor] of pattern.mask.entries()) {
|
|
13
|
+
for (let partOffset = itemIndex * itemLength; partOffset < (itemIndex + 1) * itemLength; ++partOffset) {
|
|
14
|
+
const pixelColor = pixelColorAt(image, index + partOffset)
|
|
15
|
+
if (pixelColor !== itemColor) return false
|
|
40
16
|
}
|
|
41
17
|
}
|
|
42
18
|
return true
|
|
43
19
|
}
|
|
44
20
|
|
|
45
|
-
function pixelColorAt(image, index
|
|
21
|
+
function pixelColorAt(image, index) {
|
|
46
22
|
const channels = 4
|
|
47
23
|
const r = image.data[index * channels]
|
|
48
24
|
const g = image.data[index * channels + 1]
|
|
49
25
|
const b = image.data[index * channels + 2]
|
|
50
|
-
const rgb = [r, g, b]
|
|
51
26
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
else if (rgb.every(sub => sub <= threshold)) return 0
|
|
56
|
-
// OTHER
|
|
57
|
-
else return -1
|
|
27
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
|
28
|
+
|
|
29
|
+
return luminance < 128 ? /* black */ 1 : /* white */ 0
|
|
58
30
|
}
|
|
59
31
|
|
|
60
32
|
module.exports = findImagePattern
|
package/src/image.js
CHANGED
|
@@ -6,7 +6,7 @@ const utils = require('@applitools/utils')
|
|
|
6
6
|
|
|
7
7
|
function makeImage(data) {
|
|
8
8
|
let image, size
|
|
9
|
-
let transforms = {rotate: 0, scale: 1, crop: null}
|
|
9
|
+
let transforms = {rotate: 0, scale: 1, crop: null, modifiers: []}
|
|
10
10
|
|
|
11
11
|
if (utils.types.isBase64(data)) {
|
|
12
12
|
const buffer = Buffer.from(data, 'base64')
|
|
@@ -20,21 +20,19 @@ function makeImage(data) {
|
|
|
20
20
|
image = fromBuffer(data)
|
|
21
21
|
size = extractPngSize(data)
|
|
22
22
|
} else if (data.isImage) {
|
|
23
|
-
image = data.toRaw()
|
|
24
|
-
size = data.size
|
|
25
23
|
transforms = data.transforms
|
|
24
|
+
image = data.toRaw()
|
|
25
|
+
size = utils.geometry.scale(data.size, 1 / transforms.scale)
|
|
26
26
|
} else if (utils.types.has(data, ['width', 'height'])) {
|
|
27
27
|
image = fromSize(data)
|
|
28
28
|
if (data.data) image.data = data.data
|
|
29
29
|
size = {width: data.width, height: data.height}
|
|
30
|
+
} else if (data.auto) {
|
|
31
|
+
size = {width: -1, height: -1}
|
|
30
32
|
} else {
|
|
31
33
|
throw new Error('Unable to create an image abstraction from unknown data')
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
if (!transforms.crop) {
|
|
35
|
-
transforms.crop = utils.geometry.region({x: 0, y: 0}, size)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
36
|
return {
|
|
39
37
|
get isImage() {
|
|
40
38
|
return true
|
|
@@ -46,10 +44,10 @@ function makeImage(data) {
|
|
|
46
44
|
return {...transforms}
|
|
47
45
|
},
|
|
48
46
|
get width() {
|
|
49
|
-
return size.width
|
|
47
|
+
return this.size.width
|
|
50
48
|
},
|
|
51
49
|
get height() {
|
|
52
|
-
return size.height
|
|
50
|
+
return this.size.height
|
|
53
51
|
},
|
|
54
52
|
scale(ratio) {
|
|
55
53
|
transforms.scale *= ratio
|
|
@@ -67,7 +65,9 @@ function makeImage(data) {
|
|
|
67
65
|
region = utils.geometry.scale(region, 1 / transforms.scale)
|
|
68
66
|
}
|
|
69
67
|
region = utils.geometry.rotate(region, transforms.rotate)
|
|
70
|
-
transforms.crop =
|
|
68
|
+
transforms.crop = transforms.crop
|
|
69
|
+
? utils.geometry.intersect(transforms.crop, utils.geometry.offset(region, transforms.crop))
|
|
70
|
+
: utils.geometry.intersect({x: 0, y: 0, ...size}, region)
|
|
71
71
|
|
|
72
72
|
size = utils.geometry.round(utils.geometry.size(transforms.crop))
|
|
73
73
|
|
|
@@ -77,18 +77,43 @@ function makeImage(data) {
|
|
|
77
77
|
transforms.rotate = (transforms.rotate + degree) % 360
|
|
78
78
|
return this
|
|
79
79
|
},
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
80
|
+
copy(srcImage, offset) {
|
|
81
|
+
const scale = srcImage.transforms.scale
|
|
82
|
+
if (!image) {
|
|
83
|
+
size = {
|
|
84
|
+
width: Math.max(Math.floor((offset.x + srcImage.width) / scale), size.width),
|
|
85
|
+
height: Math.max(Math.floor((offset.y + srcImage.height) / scale), size.height),
|
|
86
|
+
}
|
|
87
|
+
transforms.scale = Math.min(scale, transforms.scale)
|
|
88
|
+
}
|
|
89
|
+
transforms.modifiers.push({
|
|
90
|
+
type: 'copy',
|
|
91
|
+
image: srcImage.scale(scale === transforms.scale ? 1 / scale : scale / transforms.scale).toObject(),
|
|
92
|
+
offset: utils.geometry.scale(offset, 1 / transforms.scale),
|
|
93
|
+
})
|
|
94
|
+
|
|
83
95
|
return this
|
|
84
96
|
},
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
frame(topImage, bottomImage, region) {
|
|
98
|
+
const scale = topImage.transforms.scale
|
|
99
|
+
const prevSize = size
|
|
100
|
+
region = utils.geometry.scale(region, 1 / scale)
|
|
101
|
+
size = {
|
|
102
|
+
width: Math.floor(topImage.width / scale + Math.max(size.width - region.width, 0)),
|
|
103
|
+
height: Math.floor(topImage.height / scale + Math.max(size.height - region.height, 0)),
|
|
104
|
+
}
|
|
105
|
+
transforms.modifiers.push({
|
|
106
|
+
type: 'frame',
|
|
107
|
+
top: topImage.scale(scale === transforms.scale ? 1 / scale : scale / transforms.scale).toObject(),
|
|
108
|
+
bottom: bottomImage.scale(scale === transforms.scale ? 1 / scale : scale / transforms.scale).toObject(),
|
|
109
|
+
region,
|
|
110
|
+
})
|
|
111
|
+
transforms.added = {width: size.width - prevSize.width, height: size.height - prevSize.height}
|
|
90
112
|
return this
|
|
91
113
|
},
|
|
114
|
+
async toRaw() {
|
|
115
|
+
return image
|
|
116
|
+
},
|
|
92
117
|
async toBuffer() {
|
|
93
118
|
const image = await this.toObject()
|
|
94
119
|
return image.data
|
|
@@ -99,19 +124,17 @@ function makeImage(data) {
|
|
|
99
124
|
async toFile(path) {
|
|
100
125
|
return toFile(await image, path)
|
|
101
126
|
},
|
|
102
|
-
async toRaw() {
|
|
103
|
-
return image
|
|
104
|
-
},
|
|
105
127
|
async toObject() {
|
|
106
|
-
image = await transform(await image, transforms)
|
|
107
|
-
transforms = {
|
|
128
|
+
image = await transform(image ? await image : size, transforms)
|
|
129
|
+
transforms = {crop: null, scale: 1, rotate: 0, modifiers: []}
|
|
108
130
|
return image
|
|
109
131
|
},
|
|
110
132
|
async debug(debug) {
|
|
111
133
|
if (!debug || !debug.path) return
|
|
112
134
|
const timestamp = new Date().toISOString().replace(/[-T:.]/g, '_')
|
|
113
135
|
const filename = ['screenshot', timestamp, debug.name, debug.suffix].filter(part => part).join('_') + '.png'
|
|
114
|
-
|
|
136
|
+
const transformedImage = await transform(image ? await image : size, transforms)
|
|
137
|
+
return toFile(transformedImage, path.join(debug.path, filename)).catch(() => null)
|
|
115
138
|
},
|
|
116
139
|
}
|
|
117
140
|
}
|
|
@@ -164,10 +187,27 @@ async function toFile(image, path) {
|
|
|
164
187
|
}
|
|
165
188
|
|
|
166
189
|
async function transform(image, transforms) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
190
|
+
if (!image.data) {
|
|
191
|
+
const size = transforms.added
|
|
192
|
+
? {width: image.width - transforms.added.width, height: image.height - transforms.added.height}
|
|
193
|
+
: image
|
|
194
|
+
image = new png.Image(size)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
image = await transforms.modifiers.reduce(async (image, modifier) => {
|
|
198
|
+
if (modifier.type === 'copy') {
|
|
199
|
+
return copy(await image, await modifier.image, modifier.offset)
|
|
200
|
+
} else if (modifier.type === 'frame') {
|
|
201
|
+
return frame(await modifier.top, await modifier.bottom, await image, modifier.region)
|
|
202
|
+
} else {
|
|
203
|
+
return image
|
|
204
|
+
}
|
|
205
|
+
}, image)
|
|
206
|
+
|
|
207
|
+
image = transforms.rotate > 0 ? await rotate(image, transforms.rotate) : image
|
|
208
|
+
image = transforms.crop ? await extract(image, transforms.crop) : image
|
|
209
|
+
image = transforms.scale !== 1 ? await scale(image, transforms.scale) : image
|
|
210
|
+
return image
|
|
171
211
|
}
|
|
172
212
|
|
|
173
213
|
async function scale(image, scaleRatio) {
|
|
@@ -288,98 +328,98 @@ async function copy(dstImage, srcImage, offset) {
|
|
|
288
328
|
return dstImage
|
|
289
329
|
}
|
|
290
330
|
|
|
291
|
-
async function
|
|
331
|
+
async function frame(topImage, bottomImage, srcImage, region) {
|
|
292
332
|
region = utils.geometry.intersect(
|
|
293
|
-
{x: 0, y: 0, width:
|
|
333
|
+
{x: 0, y: 0, width: topImage.width, height: topImage.height},
|
|
294
334
|
utils.geometry.round(region),
|
|
295
335
|
)
|
|
296
336
|
|
|
297
|
-
if (region.x === 0 && region.y === 0 && region.width >=
|
|
337
|
+
if (region.x === 0 && region.y === 0 && region.width >= topImage.width && region.height >= topImage.height) {
|
|
298
338
|
return srcImage
|
|
299
339
|
}
|
|
300
340
|
|
|
301
|
-
if (region.width
|
|
302
|
-
await copy(
|
|
303
|
-
return
|
|
341
|
+
if (region.width >= srcImage.width && region.height >= srcImage.height) {
|
|
342
|
+
await copy(topImage, srcImage, {x: region.x, y: region.y})
|
|
343
|
+
return topImage
|
|
304
344
|
}
|
|
305
345
|
|
|
306
346
|
const dstImage = new png.Image({
|
|
307
|
-
width:
|
|
308
|
-
height:
|
|
347
|
+
width: topImage.width + Math.max(srcImage.width - region.width, 0),
|
|
348
|
+
height: topImage.height + Math.max(srcImage.height - region.height, 0),
|
|
309
349
|
})
|
|
310
350
|
|
|
311
351
|
if (region.width === srcImage.width) {
|
|
312
|
-
const
|
|
352
|
+
const topExtImage = await extract(topImage, {
|
|
313
353
|
x: 0,
|
|
314
354
|
y: 0,
|
|
315
|
-
width:
|
|
355
|
+
width: topImage.width,
|
|
316
356
|
height: region.y + region.height,
|
|
317
357
|
})
|
|
318
|
-
await copy(dstImage,
|
|
358
|
+
await copy(dstImage, topExtImage, {x: 0, y: 0})
|
|
319
359
|
} else if (region.height === srcImage.height) {
|
|
320
|
-
const
|
|
360
|
+
const leftExtImage = await extract(topImage, {
|
|
321
361
|
x: 0,
|
|
322
362
|
y: 0,
|
|
323
363
|
width: region.x + region.width,
|
|
324
|
-
height:
|
|
364
|
+
height: topImage.height,
|
|
325
365
|
})
|
|
326
|
-
await copy(dstImage,
|
|
366
|
+
await copy(dstImage, leftExtImage, {x: 0, y: 0})
|
|
327
367
|
} else {
|
|
328
|
-
const
|
|
368
|
+
const topLeftExtImage = await extract(topImage, {
|
|
329
369
|
x: 0,
|
|
330
370
|
y: 0,
|
|
331
371
|
width: region.x + region.width,
|
|
332
372
|
height: region.y + region.height,
|
|
333
373
|
})
|
|
334
|
-
await copy(dstImage,
|
|
374
|
+
await copy(dstImage, topLeftExtImage, {x: 0, y: 0})
|
|
335
375
|
|
|
336
|
-
const rightExtImage = await extract(
|
|
376
|
+
const rightExtImage = await extract(topImage, {
|
|
337
377
|
x: region.x + region.width,
|
|
338
378
|
y: 0,
|
|
339
|
-
width:
|
|
379
|
+
width: topImage.width - (region.x + region.width),
|
|
340
380
|
height: region.y,
|
|
341
381
|
})
|
|
342
382
|
await copy(dstImage, rightExtImage, {x: region.x + region.width, y: 0})
|
|
343
383
|
|
|
344
|
-
const bottomExtImage = await extract(
|
|
384
|
+
const bottomExtImage = await extract(topImage, {
|
|
345
385
|
x: 0,
|
|
346
386
|
y: region.y + region.height,
|
|
347
387
|
width: region.x,
|
|
348
|
-
height:
|
|
388
|
+
height: topImage.height - (region.y + region.height),
|
|
349
389
|
})
|
|
350
390
|
await copy(dstImage, bottomExtImage, {x: 0, y: region.y + region.height})
|
|
351
391
|
}
|
|
352
392
|
|
|
353
|
-
if (
|
|
393
|
+
if (bottomImage.height > region.y + region.height || bottomImage.width > region.x + region.width) {
|
|
354
394
|
// first image might be higher
|
|
355
|
-
const yDiff =
|
|
395
|
+
const yDiff = topImage.height - bottomImage.height
|
|
356
396
|
if (region.width === srcImage.width) {
|
|
357
|
-
const
|
|
397
|
+
const bottomExtImage = await extract(bottomImage, {
|
|
358
398
|
x: 0,
|
|
359
399
|
y: region.y - yDiff + region.height,
|
|
360
|
-
width:
|
|
361
|
-
height:
|
|
400
|
+
width: bottomImage.width,
|
|
401
|
+
height: bottomImage.height - (region.y - yDiff + region.height),
|
|
362
402
|
})
|
|
363
|
-
await copy(dstImage,
|
|
403
|
+
await copy(dstImage, bottomExtImage, {x: 0, y: region.y + Math.max(srcImage.height, region.height)})
|
|
364
404
|
} else if (region.height === srcImage.height) {
|
|
365
|
-
const
|
|
405
|
+
const rightExtImage = await extract(bottomImage, {
|
|
366
406
|
x: region.x + region.width,
|
|
367
407
|
y: 0,
|
|
368
|
-
width:
|
|
369
|
-
height:
|
|
408
|
+
width: bottomImage.width - (region.x + region.width),
|
|
409
|
+
height: bottomImage.height,
|
|
370
410
|
})
|
|
371
|
-
await copy(dstImage,
|
|
411
|
+
await copy(dstImage, rightExtImage, {x: region.x + Math.max(srcImage.width, region.width), y: 0})
|
|
372
412
|
} else {
|
|
373
|
-
const
|
|
413
|
+
const bottomRightExtImage = await extract(bottomImage, {
|
|
374
414
|
x: region.x,
|
|
375
415
|
y: region.y - yDiff,
|
|
376
|
-
width:
|
|
377
|
-
height:
|
|
416
|
+
width: bottomImage.width - region.x,
|
|
417
|
+
height: bottomImage.height - (region.y - yDiff),
|
|
378
418
|
})
|
|
379
419
|
|
|
380
|
-
await copy(dstImage,
|
|
381
|
-
x: region.x + srcImage.width - region.width,
|
|
382
|
-
y: region.y + srcImage.height - region.height,
|
|
420
|
+
await copy(dstImage, bottomRightExtImage, {
|
|
421
|
+
x: region.x + Math.max(srcImage.width - region.width, 0),
|
|
422
|
+
y: region.y + Math.max(srcImage.height - region.height, 0),
|
|
383
423
|
})
|
|
384
424
|
}
|
|
385
425
|
}
|