@applitools/screenshoter 3.2.8 → 3.3.2
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 +49 -6
- package/.bongo/dry-run/package.json +5 -0
- package/.bongo/dry-run.tgz +0 -0
- package/CHANGELOG.md +19 -0
- package/index.js +2 -1
- package/package.json +7 -4
- package/src/find-image-pattern.js +11 -38
- package/src/image.js +107 -64
- package/src/take-screenshot.js +136 -160
- package/src/take-simple-screenshot.js +25 -0
- package/src/take-stitched-screenshot.js +34 -40
- package/src/take-viewport-screenshot.js +179 -16
- package/test/e2e/android.spec.js +120 -13
- package/test/e2e/external.spec.js +155 -0
- package/test/e2e/ios.spec.js +142 -12
- package/test/e2e/web-ios.spec.js +19 -6
- package/test/e2e/web.spec.js +33 -21
- 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/external/agl.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/ios/webview-fully.png +0 -0
- package/test/fixtures/ios/webview.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/frame-fully.png +0 -0
- package/test/fixtures/web/frame.png +0 -0
- package/test/fixtures/web/inner-element-fully.png +0 -0
- package/test/fixtures/web/inner-element.png +0 -0
- package/test/fixtures/web/inner-region-fully.png +0 -0
- package/test/fixtures/web/inner-region.png +0 -0
- package/test/fixtures/web/page-fully.png +0 -0
- package/test/fixtures/web/page.png +0 -0
- package/test/fixtures/web/region-fully.png +0 -0
- package/test/fixtures/web/region.png +0 -0
- package/test/fixtures/web-ios/page-fully.png +0 -0
- package/test/it/find-pattern.spec.js +16 -11
- package/test/it/image.spec.js +42 -15
- package/docker-compose.yaml +0 -29
- package/src/calculate-screenshot-regions.js +0 -31
- package/src/screenshoter.js +0 -158
- 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
- package/test/util/spec-driver.js +0 -288
|
@@ -1,20 +1,63 @@
|
|
|
1
1
|
{
|
|
2
|
+
"name": "dry-run",
|
|
3
|
+
"lockfileVersion": 2,
|
|
2
4
|
"requires": true,
|
|
3
|
-
"
|
|
5
|
+
"packages": {
|
|
6
|
+
"": {
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@applitools/screenshoter": "file:../dry-run.tgz"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"node_modules/@applitools/screenshoter": {
|
|
12
|
+
"version": "3.3.1",
|
|
13
|
+
"resolved": "file:../dry-run.tgz",
|
|
14
|
+
"integrity": "sha512-Nn4m2tV2g1+ECbNzbJ4NJzvpx02lYAZ6MM4HXuT16ShCZPXi2fCuvnJEZX3D5j/EkZjEB6yFZWXrKT70m2Djew==",
|
|
15
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@applitools/snippets": "2.1.10",
|
|
18
|
+
"@applitools/utils": "1.2.4",
|
|
19
|
+
"png-async": "0.9.4"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">= 8.9.0"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"node_modules/@applitools/snippets": {
|
|
26
|
+
"version": "2.1.10",
|
|
27
|
+
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.10.tgz",
|
|
28
|
+
"integrity": "sha512-LGjtd8IZOwbhETqKRDX9CX85+BskkFUkuMuLvriSDEEQiSm0MpFbbcJVMjYaVL2qobaJbY+3WvK0g7+gkZuuqA==",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=8.9.0"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"node_modules/@applitools/utils": {
|
|
34
|
+
"version": "1.2.4",
|
|
35
|
+
"resolved": "https://registry.npmjs.org/@applitools/utils/-/utils-1.2.4.tgz",
|
|
36
|
+
"integrity": "sha512-w7ma6FFGyqhdP6LEcuHFWOcH7EzBjnoAX3UfbFWcTHA3QXnXPX37Y2ENYRodfwkorP1cUKyUHwNXJB/BMIj/hg==",
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">= 8.9.0"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"node_modules/png-async": {
|
|
42
|
+
"version": "0.9.4",
|
|
43
|
+
"resolved": "https://registry.npmjs.org/png-async/-/png-async-0.9.4.tgz",
|
|
44
|
+
"integrity": "sha512-B//AXX9TkneKfgtOpT1mdUnnhk2BImGD+a98vImsMU8uo1dBeHyW/kM2erWZ/CsYteTPU/xKG+t6T62heHkC3A=="
|
|
45
|
+
}
|
|
46
|
+
},
|
|
4
47
|
"dependencies": {
|
|
5
48
|
"@applitools/screenshoter": {
|
|
6
49
|
"version": "file:../dry-run.tgz",
|
|
7
|
-
"integrity": "sha512-
|
|
50
|
+
"integrity": "sha512-Nn4m2tV2g1+ECbNzbJ4NJzvpx02lYAZ6MM4HXuT16ShCZPXi2fCuvnJEZX3D5j/EkZjEB6yFZWXrKT70m2Djew==",
|
|
8
51
|
"requires": {
|
|
9
|
-
"@applitools/snippets": "2.1.
|
|
52
|
+
"@applitools/snippets": "2.1.10",
|
|
10
53
|
"@applitools/utils": "1.2.4",
|
|
11
54
|
"png-async": "0.9.4"
|
|
12
55
|
}
|
|
13
56
|
},
|
|
14
57
|
"@applitools/snippets": {
|
|
15
|
-
"version": "2.1.
|
|
16
|
-
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.
|
|
17
|
-
"integrity": "sha512-
|
|
58
|
+
"version": "2.1.10",
|
|
59
|
+
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.10.tgz",
|
|
60
|
+
"integrity": "sha512-LGjtd8IZOwbhETqKRDX9CX85+BskkFUkuMuLvriSDEEQiSm0MpFbbcJVMjYaVL2qobaJbY+3WvK0g7+gkZuuqA=="
|
|
18
61
|
},
|
|
19
62
|
"@applitools/utils": {
|
|
20
63
|
"version": "1.2.4",
|
package/.bongo/dry-run.tgz
CHANGED
|
Binary file
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@
|
|
|
4
4
|
## Unreleased
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
## 3.3.2 - 2021/12/20
|
|
8
|
+
|
|
9
|
+
- add basic support of webview screenshots on ios
|
|
10
|
+
- updated to @applitools/snippets@2.1.10 (from 2.1.8)
|
|
11
|
+
|
|
12
|
+
## 3.3.1 - 2021/12/17
|
|
13
|
+
|
|
14
|
+
- no changes
|
|
15
|
+
|
|
16
|
+
## 3.3.0 - 2021/12/16
|
|
17
|
+
|
|
18
|
+
- fix ios web screenshots on pages without viewport meta tag
|
|
19
|
+
- improve native apps support
|
|
20
|
+
- updated to @applitools/snippets@2.1.8 (from 2.1.7)
|
|
21
|
+
|
|
22
|
+
## 3.2.9 - 2021/11/14
|
|
23
|
+
|
|
24
|
+
- add support of scrollable elements that change its size during scrolling
|
|
25
|
+
|
|
7
26
|
## 3.2.8 - 2021/10/30
|
|
8
27
|
|
|
9
28
|
- updated to @applitools/utils@1.2.4 (from 1.2.3)
|
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')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applitools/screenshoter",
|
|
3
|
-
"version": "3.2
|
|
3
|
+
"version": "3.3.2",
|
|
4
4
|
"description": "Applitools universal screenshoter for web and native applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"applitools",
|
|
@@ -49,13 +49,16 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@applitools/snippets": "2.1.
|
|
52
|
+
"@applitools/snippets": "2.1.10",
|
|
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.5",
|
|
58
58
|
"@applitools/sdk-release-kit": "0.13.4",
|
|
59
|
+
"@applitools/spec-driver-webdriverio": "1.2.2",
|
|
60
|
+
"@applitools/test-utils": "1.0.10",
|
|
61
|
+
"chromedriver": "^95.0.0",
|
|
59
62
|
"eslint": "^7.9.0",
|
|
60
63
|
"eslint-plugin-mocha-no-only": "^1.1.1",
|
|
61
64
|
"eslint-plugin-node": "^11.1.0",
|
|
@@ -64,7 +67,7 @@
|
|
|
64
67
|
"mocha": "^8.2.1",
|
|
65
68
|
"pixelmatch": "^5.2.1",
|
|
66
69
|
"prettier": "1.19.0",
|
|
67
|
-
"webdriverio": "^
|
|
70
|
+
"webdriverio": "^7.16.7"
|
|
68
71
|
},
|
|
69
72
|
"engines": {
|
|
70
73
|
"node": ">= 8.9.0"
|
|
@@ -1,60 +1,33 @@
|
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
y: Math.floor(pixel / image.width) - pattern.offset,
|
|
7
|
-
}
|
|
4
|
+
const patterOffset = pattern.offset * pattern.pixelRatio
|
|
5
|
+
return {x: (pixel % image.width) - patterOffset, y: Math.floor(pixel / image.width) - patterOffset}
|
|
8
6
|
}
|
|
9
7
|
}
|
|
10
8
|
return null
|
|
11
9
|
}
|
|
12
10
|
|
|
13
11
|
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
|
-
}
|
|
12
|
+
const itemLength = pattern.size * pattern.pixelRatio
|
|
13
|
+
for (const [itemIndex, itemColor] of pattern.mask.entries()) {
|
|
14
|
+
for (let partOffset = itemIndex * itemLength; partOffset < (itemIndex + 1) * itemLength; ++partOffset) {
|
|
15
|
+
const pixelColor = pixelColorAt(image, index + partOffset)
|
|
16
|
+
if (pixelColor !== itemColor) return false
|
|
40
17
|
}
|
|
41
18
|
}
|
|
42
19
|
return true
|
|
43
20
|
}
|
|
44
21
|
|
|
45
|
-
function pixelColorAt(image, index
|
|
22
|
+
function pixelColorAt(image, index) {
|
|
46
23
|
const channels = 4
|
|
47
24
|
const r = image.data[index * channels]
|
|
48
25
|
const g = image.data[index * channels + 1]
|
|
49
26
|
const b = image.data[index * channels + 2]
|
|
50
|
-
const rgb = [r, g, b]
|
|
51
27
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
else if (rgb.every(sub => sub <= threshold)) return 0
|
|
56
|
-
// OTHER
|
|
57
|
-
else return -1
|
|
28
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
|
29
|
+
|
|
30
|
+
return luminance < 128 ? /* black */ 1 : /* white */ 0
|
|
58
31
|
}
|
|
59
32
|
|
|
60
33
|
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,95 +328,98 @@ async function copy(dstImage, srcImage, offset) {
|
|
|
288
328
|
return dstImage
|
|
289
329
|
}
|
|
290
330
|
|
|
291
|
-
async function
|
|
292
|
-
region = utils.geometry.intersect(
|
|
331
|
+
async function frame(topImage, bottomImage, srcImage, region) {
|
|
332
|
+
region = utils.geometry.intersect(
|
|
333
|
+
{x: 0, y: 0, width: topImage.width, height: topImage.height},
|
|
334
|
+
utils.geometry.round(region),
|
|
335
|
+
)
|
|
293
336
|
|
|
294
|
-
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) {
|
|
295
338
|
return srcImage
|
|
296
339
|
}
|
|
297
340
|
|
|
298
|
-
if (region.width
|
|
299
|
-
await copy(
|
|
300
|
-
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
|
|
301
344
|
}
|
|
302
345
|
|
|
303
346
|
const dstImage = new png.Image({
|
|
304
|
-
width:
|
|
305
|
-
height:
|
|
347
|
+
width: topImage.width + Math.max(srcImage.width - region.width, 0),
|
|
348
|
+
height: topImage.height + Math.max(srcImage.height - region.height, 0),
|
|
306
349
|
})
|
|
307
350
|
|
|
308
351
|
if (region.width === srcImage.width) {
|
|
309
|
-
const
|
|
352
|
+
const topExtImage = await extract(topImage, {
|
|
310
353
|
x: 0,
|
|
311
354
|
y: 0,
|
|
312
|
-
width:
|
|
355
|
+
width: topImage.width,
|
|
313
356
|
height: region.y + region.height,
|
|
314
357
|
})
|
|
315
|
-
await copy(dstImage,
|
|
358
|
+
await copy(dstImage, topExtImage, {x: 0, y: 0})
|
|
316
359
|
} else if (region.height === srcImage.height) {
|
|
317
|
-
const
|
|
360
|
+
const leftExtImage = await extract(topImage, {
|
|
318
361
|
x: 0,
|
|
319
362
|
y: 0,
|
|
320
363
|
width: region.x + region.width,
|
|
321
|
-
height:
|
|
364
|
+
height: topImage.height,
|
|
322
365
|
})
|
|
323
|
-
await copy(dstImage,
|
|
366
|
+
await copy(dstImage, leftExtImage, {x: 0, y: 0})
|
|
324
367
|
} else {
|
|
325
|
-
const
|
|
368
|
+
const topLeftExtImage = await extract(topImage, {
|
|
326
369
|
x: 0,
|
|
327
370
|
y: 0,
|
|
328
371
|
width: region.x + region.width,
|
|
329
372
|
height: region.y + region.height,
|
|
330
373
|
})
|
|
331
|
-
await copy(dstImage,
|
|
374
|
+
await copy(dstImage, topLeftExtImage, {x: 0, y: 0})
|
|
332
375
|
|
|
333
|
-
const rightExtImage = await extract(
|
|
376
|
+
const rightExtImage = await extract(topImage, {
|
|
334
377
|
x: region.x + region.width,
|
|
335
378
|
y: 0,
|
|
336
|
-
width:
|
|
379
|
+
width: topImage.width - (region.x + region.width),
|
|
337
380
|
height: region.y,
|
|
338
381
|
})
|
|
339
382
|
await copy(dstImage, rightExtImage, {x: region.x + region.width, y: 0})
|
|
340
383
|
|
|
341
|
-
const bottomExtImage = await extract(
|
|
384
|
+
const bottomExtImage = await extract(topImage, {
|
|
342
385
|
x: 0,
|
|
343
386
|
y: region.y + region.height,
|
|
344
387
|
width: region.x,
|
|
345
|
-
height:
|
|
388
|
+
height: topImage.height - (region.y + region.height),
|
|
346
389
|
})
|
|
347
390
|
await copy(dstImage, bottomExtImage, {x: 0, y: region.y + region.height})
|
|
348
391
|
}
|
|
349
392
|
|
|
350
|
-
if (
|
|
393
|
+
if (bottomImage.height > region.y + region.height || bottomImage.width > region.x + region.width) {
|
|
351
394
|
// first image might be higher
|
|
352
|
-
const yDiff =
|
|
395
|
+
const yDiff = topImage.height - bottomImage.height
|
|
353
396
|
if (region.width === srcImage.width) {
|
|
354
|
-
const
|
|
397
|
+
const bottomExtImage = await extract(bottomImage, {
|
|
355
398
|
x: 0,
|
|
356
399
|
y: region.y - yDiff + region.height,
|
|
357
|
-
width:
|
|
358
|
-
height:
|
|
400
|
+
width: bottomImage.width,
|
|
401
|
+
height: bottomImage.height - (region.y - yDiff + region.height),
|
|
359
402
|
})
|
|
360
|
-
await copy(dstImage,
|
|
403
|
+
await copy(dstImage, bottomExtImage, {x: 0, y: region.y + Math.max(srcImage.height, region.height)})
|
|
361
404
|
} else if (region.height === srcImage.height) {
|
|
362
|
-
const
|
|
405
|
+
const rightExtImage = await extract(bottomImage, {
|
|
363
406
|
x: region.x + region.width,
|
|
364
407
|
y: 0,
|
|
365
|
-
width:
|
|
366
|
-
height:
|
|
408
|
+
width: bottomImage.width - (region.x + region.width),
|
|
409
|
+
height: bottomImage.height,
|
|
367
410
|
})
|
|
368
|
-
await copy(dstImage,
|
|
411
|
+
await copy(dstImage, rightExtImage, {x: region.x + Math.max(srcImage.width, region.width), y: 0})
|
|
369
412
|
} else {
|
|
370
|
-
const
|
|
413
|
+
const bottomRightExtImage = await extract(bottomImage, {
|
|
371
414
|
x: region.x,
|
|
372
415
|
y: region.y - yDiff,
|
|
373
|
-
width:
|
|
374
|
-
height:
|
|
416
|
+
width: bottomImage.width - region.x,
|
|
417
|
+
height: bottomImage.height - (region.y - yDiff),
|
|
375
418
|
})
|
|
376
419
|
|
|
377
|
-
await copy(dstImage,
|
|
378
|
-
x: region.x + srcImage.width - region.width,
|
|
379
|
-
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),
|
|
380
423
|
})
|
|
381
424
|
}
|
|
382
425
|
}
|