@godscene/shared 1.7.11
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/README.md +9 -0
- package/dist/es/baseDB.mjs +109 -0
- package/dist/es/cli/cli-args.mjs +95 -0
- package/dist/es/cli/cli-error.mjs +24 -0
- package/dist/es/cli/cli-runner.mjs +122 -0
- package/dist/es/cli/index.mjs +4 -0
- package/dist/es/common.mjs +37 -0
- package/dist/es/constants/example-code.mjs +227 -0
- package/dist/es/constants/index.mjs +124 -0
- package/dist/es/env/basic.mjs +6 -0
- package/dist/es/env/constants.mjs +110 -0
- package/dist/es/env/global-config-manager.mjs +94 -0
- package/dist/es/env/helper.mjs +43 -0
- package/dist/es/env/index.mjs +5 -0
- package/dist/es/env/init-debug.mjs +18 -0
- package/dist/es/env/model-config-manager.mjs +79 -0
- package/dist/es/env/parse-model-config.mjs +165 -0
- package/dist/es/env/types.mjs +232 -0
- package/dist/es/env/utils.mjs +18 -0
- package/dist/es/extractor/constants.mjs +2 -0
- package/dist/es/extractor/cs_postmessage.mjs +61 -0
- package/dist/es/extractor/customLocator.mjs +641 -0
- package/dist/es/extractor/debug.mjs +6 -0
- package/dist/es/extractor/dom-util.mjs +96 -0
- package/dist/es/extractor/index.mjs +5 -0
- package/dist/es/extractor/locator.mjs +250 -0
- package/dist/es/extractor/tree.mjs +78 -0
- package/dist/es/extractor/util.mjs +245 -0
- package/dist/es/extractor/web-extractor.mjs +393 -0
- package/dist/es/img/box-select.mjs +824 -0
- package/dist/es/img/canvas-fallback.mjs +238 -0
- package/dist/es/img/get-photon.mjs +45 -0
- package/dist/es/img/get-sharp.mjs +11 -0
- package/dist/es/img/index.mjs +4 -0
- package/dist/es/img/info.mjs +35 -0
- package/dist/es/img/transform.mjs +275 -0
- package/dist/es/index.mjs +2 -0
- package/dist/es/key-alias-utils.mjs +19 -0
- package/dist/es/logger.mjs +64 -0
- package/dist/es/mcp/base-server.mjs +282 -0
- package/dist/es/mcp/base-tools.mjs +159 -0
- package/dist/es/mcp/chrome-path.mjs +35 -0
- package/dist/es/mcp/cli-report-session.mjs +78 -0
- package/dist/es/mcp/error-formatter.mjs +19 -0
- package/dist/es/mcp/index.mjs +9 -0
- package/dist/es/mcp/init-arg-utils.mjs +38 -0
- package/dist/es/mcp/inject-report-html-plugin.mjs +53 -0
- package/dist/es/mcp/launcher-helper.mjs +52 -0
- package/dist/es/mcp/tool-generator.mjs +419 -0
- package/dist/es/mcp/types.mjs +3 -0
- package/dist/es/node/fs.mjs +44 -0
- package/dist/es/node/index.mjs +2 -0
- package/dist/es/node/port.mjs +24 -0
- package/dist/es/polyfills/async-hooks.mjs +2 -0
- package/dist/es/polyfills/index.mjs +1 -0
- package/dist/es/types/index.mjs +3 -0
- package/dist/es/us-keyboard-layout.mjs +1414 -0
- package/dist/es/us-keyboard-layout.mjs.LICENSE.txt +5 -0
- package/dist/es/utils.mjs +72 -0
- package/dist/es/zod-schema-utils.mjs +54 -0
- package/dist/lib/baseDB.js +149 -0
- package/dist/lib/cli/cli-args.js +138 -0
- package/dist/lib/cli/cli-error.js +61 -0
- package/dist/lib/cli/cli-runner.js +181 -0
- package/dist/lib/cli/index.js +53 -0
- package/dist/lib/common.js +93 -0
- package/dist/lib/constants/example-code.js +264 -0
- package/dist/lib/constants/index.js +221 -0
- package/dist/lib/env/basic.js +40 -0
- package/dist/lib/env/constants.js +153 -0
- package/dist/lib/env/global-config-manager.js +128 -0
- package/dist/lib/env/helper.js +80 -0
- package/dist/lib/env/index.js +90 -0
- package/dist/lib/env/init-debug.js +52 -0
- package/dist/lib/env/model-config-manager.js +113 -0
- package/dist/lib/env/parse-model-config.js +211 -0
- package/dist/lib/env/types.js +572 -0
- package/dist/lib/env/utils.js +61 -0
- package/dist/lib/extractor/constants.js +42 -0
- package/dist/lib/extractor/cs_postmessage.js +98 -0
- package/dist/lib/extractor/customLocator.js +693 -0
- package/dist/lib/extractor/debug.js +12 -0
- package/dist/lib/extractor/dom-util.js +157 -0
- package/dist/lib/extractor/index.js +87 -0
- package/dist/lib/extractor/locator.js +296 -0
- package/dist/lib/extractor/tree.js +124 -0
- package/dist/lib/extractor/util.js +336 -0
- package/dist/lib/extractor/web-extractor.js +442 -0
- package/dist/lib/img/box-select.js +875 -0
- package/dist/lib/img/canvas-fallback.js +305 -0
- package/dist/lib/img/get-photon.js +82 -0
- package/dist/lib/img/get-sharp.js +45 -0
- package/dist/lib/img/index.js +95 -0
- package/dist/lib/img/info.js +92 -0
- package/dist/lib/img/transform.js +364 -0
- package/dist/lib/index.js +36 -0
- package/dist/lib/key-alias-utils.js +62 -0
- package/dist/lib/logger.js +114 -0
- package/dist/lib/mcp/base-server.js +332 -0
- package/dist/lib/mcp/base-tools.js +193 -0
- package/dist/lib/mcp/chrome-path.js +72 -0
- package/dist/lib/mcp/cli-report-session.js +121 -0
- package/dist/lib/mcp/error-formatter.js +53 -0
- package/dist/lib/mcp/index.js +114 -0
- package/dist/lib/mcp/init-arg-utils.js +78 -0
- package/dist/lib/mcp/inject-report-html-plugin.js +98 -0
- package/dist/lib/mcp/launcher-helper.js +86 -0
- package/dist/lib/mcp/tool-generator.js +456 -0
- package/dist/lib/mcp/types.js +40 -0
- package/dist/lib/node/fs.js +97 -0
- package/dist/lib/node/index.js +65 -0
- package/dist/lib/node/port.js +61 -0
- package/dist/lib/polyfills/async-hooks.js +36 -0
- package/dist/lib/polyfills/index.js +58 -0
- package/dist/lib/types/index.js +37 -0
- package/dist/lib/us-keyboard-layout.js +1457 -0
- package/dist/lib/us-keyboard-layout.js.LICENSE.txt +5 -0
- package/dist/lib/utils.js +148 -0
- package/dist/lib/zod-schema-utils.js +97 -0
- package/dist/types/baseDB.d.ts +25 -0
- package/dist/types/cli/cli-args.d.ts +8 -0
- package/dist/types/cli/cli-error.d.ts +5 -0
- package/dist/types/cli/cli-runner.d.ts +19 -0
- package/dist/types/cli/index.d.ts +4 -0
- package/dist/types/common.d.ts +12 -0
- package/dist/types/constants/example-code.d.ts +2 -0
- package/dist/types/constants/index.d.ts +61 -0
- package/dist/types/env/basic.d.ts +6 -0
- package/dist/types/env/constants.d.ts +50 -0
- package/dist/types/env/global-config-manager.d.ts +32 -0
- package/dist/types/env/helper.d.ts +4 -0
- package/dist/types/env/index.d.ts +4 -0
- package/dist/types/env/init-debug.d.ts +1 -0
- package/dist/types/env/model-config-manager.d.ts +25 -0
- package/dist/types/env/parse-model-config.d.ts +31 -0
- package/dist/types/env/types.d.ts +339 -0
- package/dist/types/env/utils.d.ts +7 -0
- package/dist/types/extractor/constants.d.ts +1 -0
- package/dist/types/extractor/cs_postmessage.d.ts +2 -0
- package/dist/types/extractor/customLocator.d.ts +69 -0
- package/dist/types/extractor/debug.d.ts +1 -0
- package/dist/types/extractor/dom-util.d.ts +57 -0
- package/dist/types/extractor/index.d.ts +33 -0
- package/dist/types/extractor/locator.d.ts +9 -0
- package/dist/types/extractor/tree.d.ts +6 -0
- package/dist/types/extractor/util.d.ts +47 -0
- package/dist/types/extractor/web-extractor.d.ts +24 -0
- package/dist/types/img/box-select.d.ts +26 -0
- package/dist/types/img/canvas-fallback.d.ts +105 -0
- package/dist/types/img/get-photon.d.ts +19 -0
- package/dist/types/img/get-sharp.d.ts +3 -0
- package/dist/types/img/index.d.ts +3 -0
- package/dist/types/img/info.d.ts +34 -0
- package/dist/types/img/transform.d.ts +98 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/key-alias-utils.d.ts +9 -0
- package/dist/types/logger.d.ts +5 -0
- package/dist/types/mcp/base-server.d.ts +93 -0
- package/dist/types/mcp/base-tools.d.ts +148 -0
- package/dist/types/mcp/chrome-path.d.ts +2 -0
- package/dist/types/mcp/cli-report-session.d.ts +12 -0
- package/dist/types/mcp/error-formatter.d.ts +12 -0
- package/dist/types/mcp/index.d.ts +9 -0
- package/dist/types/mcp/init-arg-utils.d.ts +13 -0
- package/dist/types/mcp/inject-report-html-plugin.d.ts +18 -0
- package/dist/types/mcp/launcher-helper.d.ts +94 -0
- package/dist/types/mcp/tool-generator.d.ts +10 -0
- package/dist/types/mcp/types.d.ts +113 -0
- package/dist/types/node/fs.d.ts +15 -0
- package/dist/types/node/index.d.ts +2 -0
- package/dist/types/node/port.d.ts +8 -0
- package/dist/types/polyfills/async-hooks.d.ts +6 -0
- package/dist/types/polyfills/index.d.ts +4 -0
- package/dist/types/types/index.d.ts +36 -0
- package/dist/types/us-keyboard-layout.d.ts +32 -0
- package/dist/types/utils.d.ts +34 -0
- package/dist/types/zod-schema-utils.d.ts +23 -0
- package/package.json +125 -0
- package/src/baseDB.ts +158 -0
- package/src/cli/cli-args.ts +173 -0
- package/src/cli/cli-error.ts +24 -0
- package/src/cli/cli-runner.ts +230 -0
- package/src/cli/index.ts +4 -0
- package/src/common.ts +67 -0
- package/src/constants/example-code.ts +227 -0
- package/src/constants/index.ts +139 -0
- package/src/env/basic.ts +12 -0
- package/src/env/constants.ts +303 -0
- package/src/env/global-config-manager.ts +191 -0
- package/src/env/helper.ts +58 -0
- package/src/env/index.ts +4 -0
- package/src/env/init-debug.ts +34 -0
- package/src/env/model-config-manager.ts +149 -0
- package/src/env/parse-model-config.ts +357 -0
- package/src/env/types.ts +583 -0
- package/src/env/utils.ts +39 -0
- package/src/extractor/constants.ts +5 -0
- package/src/extractor/cs_postmessage.ts +136 -0
- package/src/extractor/customLocator.ts +1245 -0
- package/src/extractor/debug.ts +10 -0
- package/src/extractor/dom-util.ts +231 -0
- package/src/extractor/index.ts +50 -0
- package/src/extractor/locator.ts +469 -0
- package/src/extractor/tree.ts +179 -0
- package/src/extractor/util.ts +482 -0
- package/src/extractor/web-extractor.ts +617 -0
- package/src/img/box-select.ts +588 -0
- package/src/img/canvas-fallback.ts +393 -0
- package/src/img/get-photon.ts +108 -0
- package/src/img/get-sharp.ts +18 -0
- package/src/img/index.ts +27 -0
- package/src/img/info.ts +102 -0
- package/src/img/transform.ts +553 -0
- package/src/index.ts +1 -0
- package/src/key-alias-utils.ts +23 -0
- package/src/logger.ts +96 -0
- package/src/mcp/base-server.ts +500 -0
- package/src/mcp/base-tools.ts +391 -0
- package/src/mcp/chrome-path.ts +48 -0
- package/src/mcp/cli-report-session.ts +130 -0
- package/src/mcp/error-formatter.ts +52 -0
- package/src/mcp/index.ts +9 -0
- package/src/mcp/init-arg-utils.ts +105 -0
- package/src/mcp/inject-report-html-plugin.ts +119 -0
- package/src/mcp/launcher-helper.ts +200 -0
- package/src/mcp/tool-generator.ts +658 -0
- package/src/mcp/types.ts +131 -0
- package/src/node/fs.ts +84 -0
- package/src/node/index.ts +2 -0
- package/src/node/port.ts +37 -0
- package/src/polyfills/async-hooks.ts +6 -0
- package/src/polyfills/index.ts +4 -0
- package/src/types/index.ts +54 -0
- package/src/us-keyboard-layout.ts +723 -0
- package/src/utils.ts +149 -0
- package/src/zod-schema-utils.ts +133 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import type { PhotonImage as PhotonImageType } from '@silvia-odwyer/photon-node';
|
|
3
|
+
import { NodeType } from '../constants';
|
|
4
|
+
import type { BaseElement, Rect } from '../types';
|
|
5
|
+
import getPhoton from './get-photon';
|
|
6
|
+
import { photonFromBase64, photonToBase64 } from './transform';
|
|
7
|
+
|
|
8
|
+
// Simple 5x7 bitmap font for digits 0-9
|
|
9
|
+
const DIGIT_FONT: Record<string, number[][]> = {
|
|
10
|
+
'0': [
|
|
11
|
+
[0, 1, 1, 1, 0],
|
|
12
|
+
[1, 0, 0, 0, 1],
|
|
13
|
+
[1, 0, 0, 0, 1],
|
|
14
|
+
[1, 0, 0, 0, 1],
|
|
15
|
+
[1, 0, 0, 0, 1],
|
|
16
|
+
[1, 0, 0, 0, 1],
|
|
17
|
+
[0, 1, 1, 1, 0],
|
|
18
|
+
],
|
|
19
|
+
'1': [
|
|
20
|
+
[0, 0, 1, 0, 0],
|
|
21
|
+
[0, 1, 1, 0, 0],
|
|
22
|
+
[0, 0, 1, 0, 0],
|
|
23
|
+
[0, 0, 1, 0, 0],
|
|
24
|
+
[0, 0, 1, 0, 0],
|
|
25
|
+
[0, 0, 1, 0, 0],
|
|
26
|
+
[0, 1, 1, 1, 0],
|
|
27
|
+
],
|
|
28
|
+
'2': [
|
|
29
|
+
[0, 1, 1, 1, 0],
|
|
30
|
+
[1, 0, 0, 0, 1],
|
|
31
|
+
[0, 0, 0, 0, 1],
|
|
32
|
+
[0, 0, 1, 1, 0],
|
|
33
|
+
[0, 1, 0, 0, 0],
|
|
34
|
+
[1, 0, 0, 0, 0],
|
|
35
|
+
[1, 1, 1, 1, 1],
|
|
36
|
+
],
|
|
37
|
+
'3': [
|
|
38
|
+
[0, 1, 1, 1, 0],
|
|
39
|
+
[1, 0, 0, 0, 1],
|
|
40
|
+
[0, 0, 0, 0, 1],
|
|
41
|
+
[0, 0, 1, 1, 0],
|
|
42
|
+
[0, 0, 0, 0, 1],
|
|
43
|
+
[1, 0, 0, 0, 1],
|
|
44
|
+
[0, 1, 1, 1, 0],
|
|
45
|
+
],
|
|
46
|
+
'4': [
|
|
47
|
+
[0, 0, 0, 1, 0],
|
|
48
|
+
[0, 0, 1, 1, 0],
|
|
49
|
+
[0, 1, 0, 1, 0],
|
|
50
|
+
[1, 0, 0, 1, 0],
|
|
51
|
+
[1, 1, 1, 1, 1],
|
|
52
|
+
[0, 0, 0, 1, 0],
|
|
53
|
+
[0, 0, 0, 1, 0],
|
|
54
|
+
],
|
|
55
|
+
'5': [
|
|
56
|
+
[1, 1, 1, 1, 1],
|
|
57
|
+
[1, 0, 0, 0, 0],
|
|
58
|
+
[1, 1, 1, 1, 0],
|
|
59
|
+
[0, 0, 0, 0, 1],
|
|
60
|
+
[0, 0, 0, 0, 1],
|
|
61
|
+
[1, 0, 0, 0, 1],
|
|
62
|
+
[0, 1, 1, 1, 0],
|
|
63
|
+
],
|
|
64
|
+
'6': [
|
|
65
|
+
[0, 1, 1, 1, 0],
|
|
66
|
+
[1, 0, 0, 0, 0],
|
|
67
|
+
[1, 0, 0, 0, 0],
|
|
68
|
+
[1, 1, 1, 1, 0],
|
|
69
|
+
[1, 0, 0, 0, 1],
|
|
70
|
+
[1, 0, 0, 0, 1],
|
|
71
|
+
[0, 1, 1, 1, 0],
|
|
72
|
+
],
|
|
73
|
+
'7': [
|
|
74
|
+
[1, 1, 1, 1, 1],
|
|
75
|
+
[0, 0, 0, 0, 1],
|
|
76
|
+
[0, 0, 0, 1, 0],
|
|
77
|
+
[0, 0, 1, 0, 0],
|
|
78
|
+
[0, 0, 1, 0, 0],
|
|
79
|
+
[0, 0, 1, 0, 0],
|
|
80
|
+
[0, 0, 1, 0, 0],
|
|
81
|
+
],
|
|
82
|
+
'8': [
|
|
83
|
+
[0, 1, 1, 1, 0],
|
|
84
|
+
[1, 0, 0, 0, 1],
|
|
85
|
+
[1, 0, 0, 0, 1],
|
|
86
|
+
[0, 1, 1, 1, 0],
|
|
87
|
+
[1, 0, 0, 0, 1],
|
|
88
|
+
[1, 0, 0, 0, 1],
|
|
89
|
+
[0, 1, 1, 1, 0],
|
|
90
|
+
],
|
|
91
|
+
'9': [
|
|
92
|
+
[0, 1, 1, 1, 0],
|
|
93
|
+
[1, 0, 0, 0, 1],
|
|
94
|
+
[1, 0, 0, 0, 1],
|
|
95
|
+
[0, 1, 1, 1, 1],
|
|
96
|
+
[0, 0, 0, 0, 1],
|
|
97
|
+
[0, 0, 0, 0, 1],
|
|
98
|
+
[0, 1, 1, 1, 0],
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const FONT_WIDTH = 5;
|
|
103
|
+
const FONT_HEIGHT = 7;
|
|
104
|
+
const FONT_SCALE = 2; // Scale up for better visibility
|
|
105
|
+
|
|
106
|
+
interface ElementForOverlay {
|
|
107
|
+
rect: Rect;
|
|
108
|
+
indexId?: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function drawDigit(
|
|
112
|
+
pixels: Uint8Array,
|
|
113
|
+
width: number,
|
|
114
|
+
height: number,
|
|
115
|
+
digit: string,
|
|
116
|
+
startX: number,
|
|
117
|
+
startY: number,
|
|
118
|
+
color: { r: number; g: number; b: number; a: number },
|
|
119
|
+
) {
|
|
120
|
+
const bitmap = DIGIT_FONT[digit];
|
|
121
|
+
if (!bitmap) return;
|
|
122
|
+
|
|
123
|
+
for (let row = 0; row < FONT_HEIGHT; row++) {
|
|
124
|
+
for (let col = 0; col < FONT_WIDTH; col++) {
|
|
125
|
+
if (bitmap[row][col] === 1) {
|
|
126
|
+
// Scale the pixel
|
|
127
|
+
for (let sy = 0; sy < FONT_SCALE; sy++) {
|
|
128
|
+
for (let sx = 0; sx < FONT_SCALE; sx++) {
|
|
129
|
+
const x = startX + col * FONT_SCALE + sx;
|
|
130
|
+
const y = startY + row * FONT_SCALE + sy;
|
|
131
|
+
if (x >= 0 && x < width && y >= 0 && y < height) {
|
|
132
|
+
const idx = (y * width + x) * 4;
|
|
133
|
+
pixels[idx + 0] = color.r;
|
|
134
|
+
pixels[idx + 1] = color.g;
|
|
135
|
+
pixels[idx + 2] = color.b;
|
|
136
|
+
pixels[idx + 3] = color.a;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function drawNumber(
|
|
146
|
+
pixels: Uint8Array,
|
|
147
|
+
width: number,
|
|
148
|
+
height: number,
|
|
149
|
+
num: number,
|
|
150
|
+
startX: number,
|
|
151
|
+
startY: number,
|
|
152
|
+
color: { r: number; g: number; b: number; a: number },
|
|
153
|
+
) {
|
|
154
|
+
const str = num.toString();
|
|
155
|
+
let x = startX;
|
|
156
|
+
for (const digit of str) {
|
|
157
|
+
drawDigit(pixels, width, height, digit, x, startY, color);
|
|
158
|
+
x += FONT_WIDTH * FONT_SCALE + 1; // 1px spacing between digits
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function getNumberWidth(num: number): number {
|
|
163
|
+
return num.toString().length * (FONT_WIDTH * FONT_SCALE + 1) - 1;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function drawRect(
|
|
167
|
+
pixels: Uint8Array,
|
|
168
|
+
width: number,
|
|
169
|
+
height: number,
|
|
170
|
+
rect: { x: number; y: number; w: number; h: number },
|
|
171
|
+
color: { r: number; g: number; b: number; a: number },
|
|
172
|
+
thickness: number,
|
|
173
|
+
) {
|
|
174
|
+
// Round to integers to avoid floating point precision issues
|
|
175
|
+
const x = Math.floor(rect.x);
|
|
176
|
+
const y = Math.floor(rect.y);
|
|
177
|
+
const w = Math.floor(rect.w);
|
|
178
|
+
const h = Math.floor(rect.h);
|
|
179
|
+
|
|
180
|
+
for (let py = y; py < y + h && py < height; py++) {
|
|
181
|
+
for (let px = x; px < x + w && px < width; px++) {
|
|
182
|
+
if (px < 0 || py < 0) continue;
|
|
183
|
+
|
|
184
|
+
// Check if this pixel is on the border
|
|
185
|
+
const isLeftBorder = px >= x && px < x + thickness;
|
|
186
|
+
const isRightBorder = px <= x + w - 1 && px > x + w - thickness - 1;
|
|
187
|
+
const isTopBorder = py >= y && py < y + thickness;
|
|
188
|
+
const isBottomBorder = py <= y + h - 1 && py > y + h - thickness - 1;
|
|
189
|
+
|
|
190
|
+
if (isLeftBorder || isRightBorder || isTopBorder || isBottomBorder) {
|
|
191
|
+
const idx = (py * width + px) * 4;
|
|
192
|
+
pixels[idx + 0] = color.r;
|
|
193
|
+
pixels[idx + 1] = color.g;
|
|
194
|
+
pixels[idx + 2] = color.b;
|
|
195
|
+
pixels[idx + 3] = color.a;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function fillRect(
|
|
202
|
+
pixels: Uint8Array,
|
|
203
|
+
width: number,
|
|
204
|
+
height: number,
|
|
205
|
+
rect: { x: number; y: number; w: number; h: number },
|
|
206
|
+
color: { r: number; g: number; b: number; a: number },
|
|
207
|
+
) {
|
|
208
|
+
// Round to integers to avoid floating point precision issues
|
|
209
|
+
const x = Math.floor(rect.x);
|
|
210
|
+
const y = Math.floor(rect.y);
|
|
211
|
+
const w = Math.floor(rect.w);
|
|
212
|
+
const h = Math.floor(rect.h);
|
|
213
|
+
|
|
214
|
+
for (let py = y; py < y + h && py < height; py++) {
|
|
215
|
+
for (let px = x; px < x + w && px < width; px++) {
|
|
216
|
+
if (px < 0 || py < 0) continue;
|
|
217
|
+
const idx = (py * width + px) * 4;
|
|
218
|
+
pixels[idx + 0] = color.r;
|
|
219
|
+
pixels[idx + 1] = color.g;
|
|
220
|
+
pixels[idx + 2] = color.b;
|
|
221
|
+
pixels[idx + 3] = color.a;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function blendPixels(
|
|
227
|
+
basePixels: Uint8Array,
|
|
228
|
+
overlayPixels: Uint8Array,
|
|
229
|
+
width: number,
|
|
230
|
+
height: number,
|
|
231
|
+
): Uint8Array {
|
|
232
|
+
const result = new Uint8Array(basePixels.length);
|
|
233
|
+
for (let i = 0; i < basePixels.length; i += 4) {
|
|
234
|
+
const overlayAlpha = overlayPixels[i + 3] / 255;
|
|
235
|
+
const baseAlpha = basePixels[i + 3] / 255;
|
|
236
|
+
|
|
237
|
+
if (overlayAlpha === 0) {
|
|
238
|
+
result[i + 0] = basePixels[i + 0];
|
|
239
|
+
result[i + 1] = basePixels[i + 1];
|
|
240
|
+
result[i + 2] = basePixels[i + 2];
|
|
241
|
+
result[i + 3] = basePixels[i + 3];
|
|
242
|
+
} else {
|
|
243
|
+
const outAlpha = overlayAlpha + baseAlpha * (1 - overlayAlpha);
|
|
244
|
+
if (outAlpha === 0) {
|
|
245
|
+
// Both alphas are effectively zero, copy overlay pixel
|
|
246
|
+
result[i + 0] = overlayPixels[i + 0];
|
|
247
|
+
result[i + 1] = overlayPixels[i + 1];
|
|
248
|
+
result[i + 2] = overlayPixels[i + 2];
|
|
249
|
+
result[i + 3] = overlayPixels[i + 3];
|
|
250
|
+
} else {
|
|
251
|
+
result[i + 0] = Math.round(
|
|
252
|
+
(overlayPixels[i + 0] * overlayAlpha +
|
|
253
|
+
basePixels[i + 0] * baseAlpha * (1 - overlayAlpha)) /
|
|
254
|
+
outAlpha,
|
|
255
|
+
);
|
|
256
|
+
result[i + 1] = Math.round(
|
|
257
|
+
(overlayPixels[i + 1] * overlayAlpha +
|
|
258
|
+
basePixels[i + 1] * baseAlpha * (1 - overlayAlpha)) /
|
|
259
|
+
outAlpha,
|
|
260
|
+
);
|
|
261
|
+
result[i + 2] = Math.round(
|
|
262
|
+
(overlayPixels[i + 2] * overlayAlpha +
|
|
263
|
+
basePixels[i + 2] * baseAlpha * (1 - overlayAlpha)) /
|
|
264
|
+
outAlpha,
|
|
265
|
+
);
|
|
266
|
+
result[i + 3] = Math.round(outAlpha * 255);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const createSvgOverlay = async (
|
|
274
|
+
elements: Array<ElementForOverlay>,
|
|
275
|
+
imageWidth: number,
|
|
276
|
+
imageHeight: number,
|
|
277
|
+
boxPadding = 5,
|
|
278
|
+
borderThickness = 2,
|
|
279
|
+
prompt?: string,
|
|
280
|
+
): Promise<Uint8Array> => {
|
|
281
|
+
// Create transparent overlay
|
|
282
|
+
const overlayPixels = new Uint8Array(imageWidth * imageHeight * 4);
|
|
283
|
+
|
|
284
|
+
// Define color array
|
|
285
|
+
const colors = [
|
|
286
|
+
{
|
|
287
|
+
rect: { r: 0xc6, g: 0x23, b: 0x00, a: 0xff },
|
|
288
|
+
text: { r: 0xff, g: 0xff, b: 0xff, a: 0xff },
|
|
289
|
+
}, // red, white
|
|
290
|
+
{
|
|
291
|
+
rect: { r: 0x00, g: 0x00, b: 0xff, a: 0xff },
|
|
292
|
+
text: { r: 0xff, g: 0xff, b: 0xff, a: 0xff },
|
|
293
|
+
}, // blue, white
|
|
294
|
+
{
|
|
295
|
+
rect: { r: 0x8b, g: 0x45, b: 0x13, a: 0xff },
|
|
296
|
+
text: { r: 0xff, g: 0xff, b: 0xff, a: 0xff },
|
|
297
|
+
}, // brown, white
|
|
298
|
+
{
|
|
299
|
+
rect: { r: 0x3e, g: 0x7b, b: 0x27, a: 0xff },
|
|
300
|
+
text: { r: 0xff, g: 0xff, b: 0xff, a: 0xff },
|
|
301
|
+
}, // green, white
|
|
302
|
+
{
|
|
303
|
+
rect: { r: 0x50, g: 0x00, b: 0x73, a: 0xff },
|
|
304
|
+
text: { r: 0xff, g: 0xff, b: 0xff, a: 0xff },
|
|
305
|
+
}, // purple, white
|
|
306
|
+
];
|
|
307
|
+
|
|
308
|
+
// Draw prompt text if provided
|
|
309
|
+
if (prompt) {
|
|
310
|
+
const promptMargin = 20;
|
|
311
|
+
const promptHeight = 30;
|
|
312
|
+
const promptY = imageHeight - promptHeight - promptMargin;
|
|
313
|
+
|
|
314
|
+
// Draw prompt background (semi-transparent black)
|
|
315
|
+
fillRect(
|
|
316
|
+
overlayPixels,
|
|
317
|
+
imageWidth,
|
|
318
|
+
imageHeight,
|
|
319
|
+
{
|
|
320
|
+
x: 0,
|
|
321
|
+
y: promptY,
|
|
322
|
+
w: imageWidth,
|
|
323
|
+
h: promptHeight,
|
|
324
|
+
},
|
|
325
|
+
{ r: 0x00, g: 0x00, b: 0x00, a: 0xcc },
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
// Note: We skip drawing prompt text since we only have digit font
|
|
329
|
+
// The prompt feature was mostly for debugging anyway
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
for (let index = 0; index < elements.length; index++) {
|
|
333
|
+
const element = elements[index];
|
|
334
|
+
const color = colors[index % colors.length];
|
|
335
|
+
|
|
336
|
+
// Add padding to the rect
|
|
337
|
+
const paddedLeft = Math.max(0, element.rect.left - boxPadding);
|
|
338
|
+
const paddedTop = Math.max(0, element.rect.top - boxPadding);
|
|
339
|
+
const paddedWidth = Math.min(
|
|
340
|
+
imageWidth - paddedLeft,
|
|
341
|
+
element.rect.width + boxPadding * 2,
|
|
342
|
+
);
|
|
343
|
+
const paddedHeight = Math.min(
|
|
344
|
+
imageHeight - paddedTop,
|
|
345
|
+
element.rect.height + boxPadding * 2,
|
|
346
|
+
);
|
|
347
|
+
const paddedRect = {
|
|
348
|
+
x: paddedLeft,
|
|
349
|
+
y: paddedTop,
|
|
350
|
+
w: paddedWidth,
|
|
351
|
+
h: paddedHeight,
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// Draw rectangle border
|
|
355
|
+
drawRect(
|
|
356
|
+
overlayPixels,
|
|
357
|
+
imageWidth,
|
|
358
|
+
imageHeight,
|
|
359
|
+
paddedRect,
|
|
360
|
+
color.rect,
|
|
361
|
+
borderThickness,
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
// Calculate text position
|
|
365
|
+
const indexId = element.indexId;
|
|
366
|
+
if (typeof indexId !== 'number') {
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const textWidth = getNumberWidth(indexId);
|
|
371
|
+
const textHeight = FONT_HEIGHT * FONT_SCALE;
|
|
372
|
+
const rectWidth = textWidth + 5;
|
|
373
|
+
const rectHeight = textHeight + 4;
|
|
374
|
+
let rectX = paddedLeft - rectWidth;
|
|
375
|
+
let rectY =
|
|
376
|
+
paddedTop + Math.floor(paddedHeight / 2) - Math.floor(textHeight / 2) - 2;
|
|
377
|
+
|
|
378
|
+
// Check if this new position overlaps with any existing boxes
|
|
379
|
+
const checkOverlap = (x: number, y: number) => {
|
|
380
|
+
return elements.slice(0, index).some((otherElement) => {
|
|
381
|
+
return (
|
|
382
|
+
x < otherElement.rect.left + otherElement.rect.width &&
|
|
383
|
+
x + rectWidth > otherElement.rect.left &&
|
|
384
|
+
y < otherElement.rect.top + otherElement.rect.height &&
|
|
385
|
+
y + rectHeight > otherElement.rect.top
|
|
386
|
+
);
|
|
387
|
+
});
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const isWithinBounds = (x: number, y: number) => {
|
|
391
|
+
return (
|
|
392
|
+
x >= 0 &&
|
|
393
|
+
x + rectWidth <= imageWidth &&
|
|
394
|
+
y >= 0 &&
|
|
395
|
+
y + rectHeight <= imageHeight
|
|
396
|
+
);
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// Check left side (original position)
|
|
400
|
+
if (checkOverlap(rectX, rectY) || !isWithinBounds(rectX, rectY)) {
|
|
401
|
+
// Check top position
|
|
402
|
+
if (
|
|
403
|
+
!checkOverlap(paddedLeft, paddedTop - rectHeight - 2) &&
|
|
404
|
+
isWithinBounds(paddedLeft, paddedTop - rectHeight - 2)
|
|
405
|
+
) {
|
|
406
|
+
rectX = paddedLeft;
|
|
407
|
+
rectY = paddedTop - rectHeight - 2;
|
|
408
|
+
}
|
|
409
|
+
// Check bottom position
|
|
410
|
+
else if (
|
|
411
|
+
!checkOverlap(paddedLeft, paddedTop + paddedHeight + 2) &&
|
|
412
|
+
isWithinBounds(paddedLeft, paddedTop + paddedHeight + 2)
|
|
413
|
+
) {
|
|
414
|
+
rectX = paddedLeft;
|
|
415
|
+
rectY = paddedTop + paddedHeight + 2;
|
|
416
|
+
}
|
|
417
|
+
// Check right position
|
|
418
|
+
else if (
|
|
419
|
+
!checkOverlap(paddedLeft + paddedWidth + 2, paddedTop) &&
|
|
420
|
+
isWithinBounds(paddedLeft + paddedWidth + 2, paddedTop)
|
|
421
|
+
) {
|
|
422
|
+
rectX = paddedLeft + paddedWidth + 2;
|
|
423
|
+
rectY = paddedTop;
|
|
424
|
+
}
|
|
425
|
+
// If all sides are overlapped or out of bounds, place it inside the box at the top
|
|
426
|
+
else {
|
|
427
|
+
rectX = paddedLeft;
|
|
428
|
+
rectY = paddedTop + 2;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Draw text background
|
|
433
|
+
fillRect(
|
|
434
|
+
overlayPixels,
|
|
435
|
+
imageWidth,
|
|
436
|
+
imageHeight,
|
|
437
|
+
{
|
|
438
|
+
x: rectX,
|
|
439
|
+
y: rectY,
|
|
440
|
+
w: rectWidth,
|
|
441
|
+
h: rectHeight,
|
|
442
|
+
},
|
|
443
|
+
color.rect,
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
// Draw text (centered in the background rect)
|
|
447
|
+
const textX = rectX + Math.floor((rectWidth - textWidth) / 2);
|
|
448
|
+
const textY = rectY + Math.floor((rectHeight - textHeight) / 2);
|
|
449
|
+
drawNumber(
|
|
450
|
+
overlayPixels,
|
|
451
|
+
imageWidth,
|
|
452
|
+
imageHeight,
|
|
453
|
+
indexId,
|
|
454
|
+
textX,
|
|
455
|
+
textY,
|
|
456
|
+
color.text,
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return overlayPixels;
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
export const compositeElementInfoImg = async (options: {
|
|
464
|
+
inputImgBase64: string;
|
|
465
|
+
elementsPositionInfo: Array<ElementForOverlay>;
|
|
466
|
+
size?: { width: number; height: number };
|
|
467
|
+
annotationPadding?: number;
|
|
468
|
+
borderThickness?: number;
|
|
469
|
+
prompt?: string;
|
|
470
|
+
}) => {
|
|
471
|
+
assert(options.inputImgBase64, 'inputImgBase64 is required');
|
|
472
|
+
const { PhotonImage, SamplingFilter, resize } = await getPhoton();
|
|
473
|
+
|
|
474
|
+
let width = 0;
|
|
475
|
+
let height = 0;
|
|
476
|
+
|
|
477
|
+
if (options.size) {
|
|
478
|
+
width = options.size.width;
|
|
479
|
+
height = options.size.height;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
let photonImage = await photonFromBase64(options.inputImgBase64);
|
|
483
|
+
|
|
484
|
+
if (!width || !height) {
|
|
485
|
+
width = photonImage.get_width();
|
|
486
|
+
height = photonImage.get_height();
|
|
487
|
+
} else {
|
|
488
|
+
const imageWidth = photonImage.get_width();
|
|
489
|
+
const imageHeight = photonImage.get_height();
|
|
490
|
+
// Resize the image to the specified width and height if it's not already the same
|
|
491
|
+
if (imageWidth !== width || imageHeight !== height) {
|
|
492
|
+
const resized = resize(
|
|
493
|
+
photonImage,
|
|
494
|
+
width,
|
|
495
|
+
height,
|
|
496
|
+
SamplingFilter.Nearest,
|
|
497
|
+
);
|
|
498
|
+
photonImage.free();
|
|
499
|
+
photonImage = resized;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (!width || !height) {
|
|
504
|
+
photonImage.free();
|
|
505
|
+
throw Error('Image processing failed because width or height is undefined');
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const { elementsPositionInfo, prompt } = options;
|
|
509
|
+
|
|
510
|
+
try {
|
|
511
|
+
// Get base image pixels
|
|
512
|
+
const basePixels = photonImage.get_raw_pixels();
|
|
513
|
+
|
|
514
|
+
// Create overlay with annotations
|
|
515
|
+
const overlayPixels = await createSvgOverlay(
|
|
516
|
+
elementsPositionInfo,
|
|
517
|
+
width,
|
|
518
|
+
height,
|
|
519
|
+
options.annotationPadding,
|
|
520
|
+
options.borderThickness,
|
|
521
|
+
prompt,
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
// Blend overlay onto base image
|
|
525
|
+
const blendedPixels = blendPixels(basePixels, overlayPixels, width, height);
|
|
526
|
+
|
|
527
|
+
// Create result image
|
|
528
|
+
const resultImage = new PhotonImage(blendedPixels, width, height);
|
|
529
|
+
const base64 = await photonToBase64(resultImage, 90);
|
|
530
|
+
|
|
531
|
+
resultImage.free();
|
|
532
|
+
return base64;
|
|
533
|
+
} finally {
|
|
534
|
+
photonImage.free();
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
export const processImageElementInfo = async (options: {
|
|
539
|
+
inputImgBase64: string;
|
|
540
|
+
elementsPositionInfo: Array<BaseElement>;
|
|
541
|
+
elementsPositionInfoWithoutText: Array<BaseElement>;
|
|
542
|
+
}) => {
|
|
543
|
+
// Get the size of the original image
|
|
544
|
+
const base64Image = options.inputImgBase64.split(';base64,').pop();
|
|
545
|
+
assert(base64Image, 'base64Image is undefined');
|
|
546
|
+
|
|
547
|
+
const [
|
|
548
|
+
compositeElementInfoImgBase64,
|
|
549
|
+
compositeElementInfoImgWithoutTextBase64,
|
|
550
|
+
] = await Promise.all([
|
|
551
|
+
compositeElementInfoImg({
|
|
552
|
+
inputImgBase64: options.inputImgBase64,
|
|
553
|
+
elementsPositionInfo: options.elementsPositionInfo,
|
|
554
|
+
}),
|
|
555
|
+
compositeElementInfoImg({
|
|
556
|
+
inputImgBase64: options.inputImgBase64,
|
|
557
|
+
elementsPositionInfo: options.elementsPositionInfoWithoutText,
|
|
558
|
+
}),
|
|
559
|
+
]);
|
|
560
|
+
|
|
561
|
+
return {
|
|
562
|
+
compositeElementInfoImgBase64,
|
|
563
|
+
compositeElementInfoImgWithoutTextBase64,
|
|
564
|
+
};
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
export async function annotateRects(
|
|
568
|
+
imgBase64: string,
|
|
569
|
+
rects: Rect[],
|
|
570
|
+
prompt?: string,
|
|
571
|
+
) {
|
|
572
|
+
const markedImage = await compositeElementInfoImg({
|
|
573
|
+
inputImgBase64: imgBase64,
|
|
574
|
+
elementsPositionInfo: rects.map((rect, index) => {
|
|
575
|
+
return {
|
|
576
|
+
id: `rect-${index}`,
|
|
577
|
+
rect,
|
|
578
|
+
indexId: index + 1,
|
|
579
|
+
attributes: { nodeType: NodeType.CONTAINER },
|
|
580
|
+
content: '',
|
|
581
|
+
center: [rect.left + rect.width / 2, rect.top + rect.height / 2],
|
|
582
|
+
};
|
|
583
|
+
}),
|
|
584
|
+
annotationPadding: 0,
|
|
585
|
+
prompt,
|
|
586
|
+
});
|
|
587
|
+
return markedImage;
|
|
588
|
+
}
|