@midscene/shared 1.3.5-beta-20260202030636.0 → 1.3.5-beta-20260202075348.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.
@@ -1,64 +1,698 @@
1
1
  import node_assert from "node:assert";
2
- import get_jimp from "./get-jimp.mjs";
3
- import { bufferFromBase64, imageInfoOfBase64 } from "./index.mjs";
4
- import { readImageBuffer } from "./safe-jimp.mjs";
5
- let cachedFont = null;
6
- const loadFonts = async ()=>{
7
- const Jimp = await get_jimp();
8
- try {
9
- const fonts = await Jimp.loadFont(Jimp.FONT_SANS_16_WHITE);
10
- return fonts;
11
- } catch (error) {
12
- console.warn('Error loading font, will try to load online fonts', error);
13
- const onlineFonts = 'https://cdn.jsdelivr.net/npm/jimp-compact@0.16.1-2/fonts/open-sans/open-sans-16-white/open-sans-16-white.fnt';
14
- const fonts = await Jimp.loadFont(onlineFonts);
15
- return fonts;
16
- }
2
+ import { NodeType } from "../constants/index.mjs";
3
+ import get_photon from "./get-photon.mjs";
4
+ import { photonFromBase64, photonToBase64 } from "./transform.mjs";
5
+ const DIGIT_FONT = {
6
+ 0: [
7
+ [
8
+ 0,
9
+ 1,
10
+ 1,
11
+ 1,
12
+ 0
13
+ ],
14
+ [
15
+ 1,
16
+ 0,
17
+ 0,
18
+ 0,
19
+ 1
20
+ ],
21
+ [
22
+ 1,
23
+ 0,
24
+ 0,
25
+ 0,
26
+ 1
27
+ ],
28
+ [
29
+ 1,
30
+ 0,
31
+ 0,
32
+ 0,
33
+ 1
34
+ ],
35
+ [
36
+ 1,
37
+ 0,
38
+ 0,
39
+ 0,
40
+ 1
41
+ ],
42
+ [
43
+ 1,
44
+ 0,
45
+ 0,
46
+ 0,
47
+ 1
48
+ ],
49
+ [
50
+ 0,
51
+ 1,
52
+ 1,
53
+ 1,
54
+ 0
55
+ ]
56
+ ],
57
+ 1: [
58
+ [
59
+ 0,
60
+ 0,
61
+ 1,
62
+ 0,
63
+ 0
64
+ ],
65
+ [
66
+ 0,
67
+ 1,
68
+ 1,
69
+ 0,
70
+ 0
71
+ ],
72
+ [
73
+ 0,
74
+ 0,
75
+ 1,
76
+ 0,
77
+ 0
78
+ ],
79
+ [
80
+ 0,
81
+ 0,
82
+ 1,
83
+ 0,
84
+ 0
85
+ ],
86
+ [
87
+ 0,
88
+ 0,
89
+ 1,
90
+ 0,
91
+ 0
92
+ ],
93
+ [
94
+ 0,
95
+ 0,
96
+ 1,
97
+ 0,
98
+ 0
99
+ ],
100
+ [
101
+ 0,
102
+ 1,
103
+ 1,
104
+ 1,
105
+ 0
106
+ ]
107
+ ],
108
+ 2: [
109
+ [
110
+ 0,
111
+ 1,
112
+ 1,
113
+ 1,
114
+ 0
115
+ ],
116
+ [
117
+ 1,
118
+ 0,
119
+ 0,
120
+ 0,
121
+ 1
122
+ ],
123
+ [
124
+ 0,
125
+ 0,
126
+ 0,
127
+ 0,
128
+ 1
129
+ ],
130
+ [
131
+ 0,
132
+ 0,
133
+ 1,
134
+ 1,
135
+ 0
136
+ ],
137
+ [
138
+ 0,
139
+ 1,
140
+ 0,
141
+ 0,
142
+ 0
143
+ ],
144
+ [
145
+ 1,
146
+ 0,
147
+ 0,
148
+ 0,
149
+ 0
150
+ ],
151
+ [
152
+ 1,
153
+ 1,
154
+ 1,
155
+ 1,
156
+ 1
157
+ ]
158
+ ],
159
+ 3: [
160
+ [
161
+ 0,
162
+ 1,
163
+ 1,
164
+ 1,
165
+ 0
166
+ ],
167
+ [
168
+ 1,
169
+ 0,
170
+ 0,
171
+ 0,
172
+ 1
173
+ ],
174
+ [
175
+ 0,
176
+ 0,
177
+ 0,
178
+ 0,
179
+ 1
180
+ ],
181
+ [
182
+ 0,
183
+ 0,
184
+ 1,
185
+ 1,
186
+ 0
187
+ ],
188
+ [
189
+ 0,
190
+ 0,
191
+ 0,
192
+ 0,
193
+ 1
194
+ ],
195
+ [
196
+ 1,
197
+ 0,
198
+ 0,
199
+ 0,
200
+ 1
201
+ ],
202
+ [
203
+ 0,
204
+ 1,
205
+ 1,
206
+ 1,
207
+ 0
208
+ ]
209
+ ],
210
+ 4: [
211
+ [
212
+ 0,
213
+ 0,
214
+ 0,
215
+ 1,
216
+ 0
217
+ ],
218
+ [
219
+ 0,
220
+ 0,
221
+ 1,
222
+ 1,
223
+ 0
224
+ ],
225
+ [
226
+ 0,
227
+ 1,
228
+ 0,
229
+ 1,
230
+ 0
231
+ ],
232
+ [
233
+ 1,
234
+ 0,
235
+ 0,
236
+ 1,
237
+ 0
238
+ ],
239
+ [
240
+ 1,
241
+ 1,
242
+ 1,
243
+ 1,
244
+ 1
245
+ ],
246
+ [
247
+ 0,
248
+ 0,
249
+ 0,
250
+ 1,
251
+ 0
252
+ ],
253
+ [
254
+ 0,
255
+ 0,
256
+ 0,
257
+ 1,
258
+ 0
259
+ ]
260
+ ],
261
+ 5: [
262
+ [
263
+ 1,
264
+ 1,
265
+ 1,
266
+ 1,
267
+ 1
268
+ ],
269
+ [
270
+ 1,
271
+ 0,
272
+ 0,
273
+ 0,
274
+ 0
275
+ ],
276
+ [
277
+ 1,
278
+ 1,
279
+ 1,
280
+ 1,
281
+ 0
282
+ ],
283
+ [
284
+ 0,
285
+ 0,
286
+ 0,
287
+ 0,
288
+ 1
289
+ ],
290
+ [
291
+ 0,
292
+ 0,
293
+ 0,
294
+ 0,
295
+ 1
296
+ ],
297
+ [
298
+ 1,
299
+ 0,
300
+ 0,
301
+ 0,
302
+ 1
303
+ ],
304
+ [
305
+ 0,
306
+ 1,
307
+ 1,
308
+ 1,
309
+ 0
310
+ ]
311
+ ],
312
+ 6: [
313
+ [
314
+ 0,
315
+ 1,
316
+ 1,
317
+ 1,
318
+ 0
319
+ ],
320
+ [
321
+ 1,
322
+ 0,
323
+ 0,
324
+ 0,
325
+ 0
326
+ ],
327
+ [
328
+ 1,
329
+ 0,
330
+ 0,
331
+ 0,
332
+ 0
333
+ ],
334
+ [
335
+ 1,
336
+ 1,
337
+ 1,
338
+ 1,
339
+ 0
340
+ ],
341
+ [
342
+ 1,
343
+ 0,
344
+ 0,
345
+ 0,
346
+ 1
347
+ ],
348
+ [
349
+ 1,
350
+ 0,
351
+ 0,
352
+ 0,
353
+ 1
354
+ ],
355
+ [
356
+ 0,
357
+ 1,
358
+ 1,
359
+ 1,
360
+ 0
361
+ ]
362
+ ],
363
+ 7: [
364
+ [
365
+ 1,
366
+ 1,
367
+ 1,
368
+ 1,
369
+ 1
370
+ ],
371
+ [
372
+ 0,
373
+ 0,
374
+ 0,
375
+ 0,
376
+ 1
377
+ ],
378
+ [
379
+ 0,
380
+ 0,
381
+ 0,
382
+ 1,
383
+ 0
384
+ ],
385
+ [
386
+ 0,
387
+ 0,
388
+ 1,
389
+ 0,
390
+ 0
391
+ ],
392
+ [
393
+ 0,
394
+ 0,
395
+ 1,
396
+ 0,
397
+ 0
398
+ ],
399
+ [
400
+ 0,
401
+ 0,
402
+ 1,
403
+ 0,
404
+ 0
405
+ ],
406
+ [
407
+ 0,
408
+ 0,
409
+ 1,
410
+ 0,
411
+ 0
412
+ ]
413
+ ],
414
+ 8: [
415
+ [
416
+ 0,
417
+ 1,
418
+ 1,
419
+ 1,
420
+ 0
421
+ ],
422
+ [
423
+ 1,
424
+ 0,
425
+ 0,
426
+ 0,
427
+ 1
428
+ ],
429
+ [
430
+ 1,
431
+ 0,
432
+ 0,
433
+ 0,
434
+ 1
435
+ ],
436
+ [
437
+ 0,
438
+ 1,
439
+ 1,
440
+ 1,
441
+ 0
442
+ ],
443
+ [
444
+ 1,
445
+ 0,
446
+ 0,
447
+ 0,
448
+ 1
449
+ ],
450
+ [
451
+ 1,
452
+ 0,
453
+ 0,
454
+ 0,
455
+ 1
456
+ ],
457
+ [
458
+ 0,
459
+ 1,
460
+ 1,
461
+ 1,
462
+ 0
463
+ ]
464
+ ],
465
+ 9: [
466
+ [
467
+ 0,
468
+ 1,
469
+ 1,
470
+ 1,
471
+ 0
472
+ ],
473
+ [
474
+ 1,
475
+ 0,
476
+ 0,
477
+ 0,
478
+ 1
479
+ ],
480
+ [
481
+ 1,
482
+ 0,
483
+ 0,
484
+ 0,
485
+ 1
486
+ ],
487
+ [
488
+ 0,
489
+ 1,
490
+ 1,
491
+ 1,
492
+ 1
493
+ ],
494
+ [
495
+ 0,
496
+ 0,
497
+ 0,
498
+ 0,
499
+ 1
500
+ ],
501
+ [
502
+ 0,
503
+ 0,
504
+ 0,
505
+ 0,
506
+ 1
507
+ ],
508
+ [
509
+ 0,
510
+ 1,
511
+ 1,
512
+ 1,
513
+ 0
514
+ ]
515
+ ]
17
516
  };
517
+ const FONT_WIDTH = 5;
518
+ const FONT_HEIGHT = 7;
519
+ const FONT_SCALE = 2;
520
+ function drawDigit(pixels, width, height, digit, startX, startY, color) {
521
+ const bitmap = DIGIT_FONT[digit];
522
+ if (!bitmap) return;
523
+ for(let row = 0; row < FONT_HEIGHT; row++)for(let col = 0; col < FONT_WIDTH; col++)if (1 === bitmap[row][col]) for(let sy = 0; sy < FONT_SCALE; sy++)for(let sx = 0; sx < FONT_SCALE; sx++){
524
+ const x = startX + col * FONT_SCALE + sx;
525
+ const y = startY + row * FONT_SCALE + sy;
526
+ if (x >= 0 && x < width && y >= 0 && y < height) {
527
+ const idx = (y * width + x) * 4;
528
+ pixels[idx + 0] = color.r;
529
+ pixels[idx + 1] = color.g;
530
+ pixels[idx + 2] = color.b;
531
+ pixels[idx + 3] = color.a;
532
+ }
533
+ }
534
+ }
535
+ function drawNumber(pixels, width, height, num, startX, startY, color) {
536
+ const str = num.toString();
537
+ let x = startX;
538
+ for (const digit of str){
539
+ drawDigit(pixels, width, height, digit, x, startY, color);
540
+ x += FONT_WIDTH * FONT_SCALE + 1;
541
+ }
542
+ }
543
+ function getNumberWidth(num) {
544
+ return num.toString().length * (FONT_WIDTH * FONT_SCALE + 1) - 1;
545
+ }
546
+ function drawRect(pixels, width, height, rect, color, thickness) {
547
+ const x = Math.floor(rect.x);
548
+ const y = Math.floor(rect.y);
549
+ const w = Math.floor(rect.w);
550
+ const h = Math.floor(rect.h);
551
+ for(let py = y; py < y + h && py < height; py++)for(let px = x; px < x + w && px < width; px++){
552
+ if (px < 0 || py < 0) continue;
553
+ const isLeftBorder = px >= x && px < x + thickness;
554
+ const isRightBorder = px <= x + w - 1 && px > x + w - thickness - 1;
555
+ const isTopBorder = py >= y && py < y + thickness;
556
+ const isBottomBorder = py <= y + h - 1 && py > y + h - thickness - 1;
557
+ if (isLeftBorder || isRightBorder || isTopBorder || isBottomBorder) {
558
+ const idx = (py * width + px) * 4;
559
+ pixels[idx + 0] = color.r;
560
+ pixels[idx + 1] = color.g;
561
+ pixels[idx + 2] = color.b;
562
+ pixels[idx + 3] = color.a;
563
+ }
564
+ }
565
+ }
566
+ function fillRect(pixels, width, height, rect, color) {
567
+ const x = Math.floor(rect.x);
568
+ const y = Math.floor(rect.y);
569
+ const w = Math.floor(rect.w);
570
+ const h = Math.floor(rect.h);
571
+ for(let py = y; py < y + h && py < height; py++)for(let px = x; px < x + w && px < width; px++){
572
+ if (px < 0 || py < 0) continue;
573
+ const idx = (py * width + px) * 4;
574
+ pixels[idx + 0] = color.r;
575
+ pixels[idx + 1] = color.g;
576
+ pixels[idx + 2] = color.b;
577
+ pixels[idx + 3] = color.a;
578
+ }
579
+ }
580
+ function blendPixels(basePixels, overlayPixels, width, height) {
581
+ const result = new Uint8Array(basePixels.length);
582
+ for(let i = 0; i < basePixels.length; i += 4){
583
+ const overlayAlpha = overlayPixels[i + 3] / 255;
584
+ const baseAlpha = basePixels[i + 3] / 255;
585
+ if (0 === overlayAlpha) {
586
+ result[i + 0] = basePixels[i + 0];
587
+ result[i + 1] = basePixels[i + 1];
588
+ result[i + 2] = basePixels[i + 2];
589
+ result[i + 3] = basePixels[i + 3];
590
+ } else {
591
+ const outAlpha = overlayAlpha + baseAlpha * (1 - overlayAlpha);
592
+ if (0 === outAlpha) {
593
+ result[i + 0] = overlayPixels[i + 0];
594
+ result[i + 1] = overlayPixels[i + 1];
595
+ result[i + 2] = overlayPixels[i + 2];
596
+ result[i + 3] = overlayPixels[i + 3];
597
+ } else {
598
+ result[i + 0] = Math.round((overlayPixels[i + 0] * overlayAlpha + basePixels[i + 0] * baseAlpha * (1 - overlayAlpha)) / outAlpha);
599
+ result[i + 1] = Math.round((overlayPixels[i + 1] * overlayAlpha + basePixels[i + 1] * baseAlpha * (1 - overlayAlpha)) / outAlpha);
600
+ result[i + 2] = Math.round((overlayPixels[i + 2] * overlayAlpha + basePixels[i + 2] * baseAlpha * (1 - overlayAlpha)) / outAlpha);
601
+ result[i + 3] = Math.round(255 * outAlpha);
602
+ }
603
+ }
604
+ }
605
+ return result;
606
+ }
18
607
  const createSvgOverlay = async (elements, imageWidth, imageHeight, boxPadding = 5, borderThickness = 2, prompt)=>{
19
- const Jimp = await get_jimp();
20
- const image = new Jimp(imageWidth, imageHeight, 0x00000000);
608
+ const overlayPixels = new Uint8Array(imageWidth * imageHeight * 4);
21
609
  const colors = [
22
610
  {
23
- rect: 0xc62300ff,
24
- text: 0xffffffff
611
+ rect: {
612
+ r: 0xc6,
613
+ g: 0x23,
614
+ b: 0x00,
615
+ a: 0xff
616
+ },
617
+ text: {
618
+ r: 0xff,
619
+ g: 0xff,
620
+ b: 0xff,
621
+ a: 0xff
622
+ }
25
623
  },
26
624
  {
27
- rect: 0x0000ffff,
28
- text: 0xffffffff
625
+ rect: {
626
+ r: 0x00,
627
+ g: 0x00,
628
+ b: 0xff,
629
+ a: 0xff
630
+ },
631
+ text: {
632
+ r: 0xff,
633
+ g: 0xff,
634
+ b: 0xff,
635
+ a: 0xff
636
+ }
29
637
  },
30
638
  {
31
- rect: 0x8b4513ff,
32
- text: 0xffffffff
639
+ rect: {
640
+ r: 0x8b,
641
+ g: 0x45,
642
+ b: 0x13,
643
+ a: 0xff
644
+ },
645
+ text: {
646
+ r: 0xff,
647
+ g: 0xff,
648
+ b: 0xff,
649
+ a: 0xff
650
+ }
33
651
  },
34
652
  {
35
- rect: 0x3e7b27ff,
36
- text: 0xffffffff
653
+ rect: {
654
+ r: 0x3e,
655
+ g: 0x7b,
656
+ b: 0x27,
657
+ a: 0xff
658
+ },
659
+ text: {
660
+ r: 0xff,
661
+ g: 0xff,
662
+ b: 0xff,
663
+ a: 0xff
664
+ }
37
665
  },
38
666
  {
39
- rect: 0x500073ff,
40
- text: 0xffffffff
667
+ rect: {
668
+ r: 0x50,
669
+ g: 0x00,
670
+ b: 0x73,
671
+ a: 0xff
672
+ },
673
+ text: {
674
+ r: 0xff,
675
+ g: 0xff,
676
+ b: 0xff,
677
+ a: 0xff
678
+ }
41
679
  }
42
680
  ];
43
- if (prompt) try {
44
- cachedFont = cachedFont || await loadFonts();
45
- const promptPadding = 10;
681
+ if (prompt) {
46
682
  const promptMargin = 20;
47
683
  const promptHeight = 30;
48
684
  const promptY = imageHeight - promptHeight - promptMargin;
49
- image.scan(0, promptY, imageWidth, promptHeight, (x, y, idx)=>{
50
- image.bitmap.data[idx + 0] = 0x00;
51
- image.bitmap.data[idx + 1] = 0x00;
52
- image.bitmap.data[idx + 2] = 0x00;
53
- image.bitmap.data[idx + 3] = 0xcc;
685
+ fillRect(overlayPixels, imageWidth, imageHeight, {
686
+ x: 0,
687
+ y: promptY,
688
+ w: imageWidth,
689
+ h: promptHeight
690
+ }, {
691
+ r: 0x00,
692
+ g: 0x00,
693
+ b: 0x00,
694
+ a: 0xcc
54
695
  });
55
- image.print(cachedFont, promptPadding, promptY, {
56
- text: prompt,
57
- alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT,
58
- alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE
59
- }, imageWidth - 2 * promptPadding, promptHeight);
60
- } catch (error) {
61
- console.error('Error drawing prompt text', error);
62
696
  }
63
697
  for(let index = 0; index < elements.length; index++){
64
698
  const element = elements[index];
@@ -68,101 +702,85 @@ const createSvgOverlay = async (elements, imageWidth, imageHeight, boxPadding =
68
702
  const paddedWidth = Math.min(imageWidth - paddedLeft, element.rect.width + 2 * boxPadding);
69
703
  const paddedHeight = Math.min(imageHeight - paddedTop, element.rect.height + 2 * boxPadding);
70
704
  const paddedRect = {
71
- left: paddedLeft,
72
- top: paddedTop,
73
- width: paddedWidth,
74
- height: paddedHeight
705
+ x: paddedLeft,
706
+ y: paddedTop,
707
+ w: paddedWidth,
708
+ h: paddedHeight
75
709
  };
76
- image.scan(paddedRect.left, paddedRect.top, paddedRect.width, paddedRect.height, (x, y, idx)=>{
77
- if (x >= paddedRect.left && x < paddedRect.left + borderThickness || x <= paddedRect.left + paddedRect.width - 1 && x > paddedRect.left + paddedRect.width - borderThickness || y >= paddedRect.top && y < paddedRect.top + borderThickness || y <= paddedRect.top + paddedRect.height - 1 && y > paddedRect.top + paddedRect.height - borderThickness) {
78
- image.bitmap.data[idx + 0] = color.rect >> 24 & 0xff;
79
- image.bitmap.data[idx + 1] = color.rect >> 16 & 0xff;
80
- image.bitmap.data[idx + 2] = color.rect >> 8 & 0xff;
81
- image.bitmap.data[idx + 3] = 0xff & color.rect;
82
- }
83
- });
710
+ drawRect(overlayPixels, imageWidth, imageHeight, paddedRect, color.rect, borderThickness);
84
711
  const indexId = element.indexId;
85
712
  if ('number' != typeof indexId) continue;
86
- const textWidth = 8 * indexId.toString().length;
87
- const textHeight = 12;
713
+ const textWidth = getNumberWidth(indexId);
714
+ const textHeight = FONT_HEIGHT * FONT_SCALE;
88
715
  const rectWidth = textWidth + 5;
89
716
  const rectHeight = textHeight + 4;
90
- let rectX = paddedRect.left - rectWidth;
91
- let rectY = paddedRect.top + paddedRect.height / 2 - textHeight / 2 - 2;
717
+ let rectX = paddedLeft - rectWidth;
718
+ let rectY = paddedTop + Math.floor(paddedHeight / 2) - Math.floor(textHeight / 2) - 2;
92
719
  const checkOverlap = (x, y)=>elements.slice(0, index).some((otherElement)=>x < otherElement.rect.left + otherElement.rect.width && x + rectWidth > otherElement.rect.left && y < otherElement.rect.top + otherElement.rect.height && y + rectHeight > otherElement.rect.top);
93
720
  const isWithinBounds = (x, y)=>x >= 0 && x + rectWidth <= imageWidth && y >= 0 && y + rectHeight <= imageHeight;
94
- if (checkOverlap(rectX, rectY) || !isWithinBounds(rectX, rectY)) if (!checkOverlap(paddedRect.left, paddedRect.top - rectHeight - 2) && isWithinBounds(paddedRect.left, paddedRect.top - rectHeight - 2)) {
95
- rectX = paddedRect.left;
96
- rectY = paddedRect.top - rectHeight - 2;
97
- } else if (!checkOverlap(paddedRect.left, paddedRect.top + paddedRect.height + 2) && isWithinBounds(paddedRect.left, paddedRect.top + paddedRect.height + 2)) {
98
- rectX = paddedRect.left;
99
- rectY = paddedRect.top + paddedRect.height + 2;
100
- } else if (!checkOverlap(paddedRect.left + paddedRect.width + 2, paddedRect.top) && isWithinBounds(paddedRect.left + paddedRect.width + 2, paddedRect.top)) {
101
- rectX = paddedRect.left + paddedRect.width + 2;
102
- rectY = paddedRect.top;
721
+ if (checkOverlap(rectX, rectY) || !isWithinBounds(rectX, rectY)) if (!checkOverlap(paddedLeft, paddedTop - rectHeight - 2) && isWithinBounds(paddedLeft, paddedTop - rectHeight - 2)) {
722
+ rectX = paddedLeft;
723
+ rectY = paddedTop - rectHeight - 2;
724
+ } else if (!checkOverlap(paddedLeft, paddedTop + paddedHeight + 2) && isWithinBounds(paddedLeft, paddedTop + paddedHeight + 2)) {
725
+ rectX = paddedLeft;
726
+ rectY = paddedTop + paddedHeight + 2;
727
+ } else if (!checkOverlap(paddedLeft + paddedWidth + 2, paddedTop) && isWithinBounds(paddedLeft + paddedWidth + 2, paddedTop)) {
728
+ rectX = paddedLeft + paddedWidth + 2;
729
+ rectY = paddedTop;
103
730
  } else {
104
- rectX = paddedRect.left;
105
- rectY = paddedRect.top + 2;
106
- }
107
- image.scan(rectX, rectY, rectWidth, rectHeight, (x, y, idx)=>{
108
- image.bitmap.data[idx + 0] = color.rect >> 24 & 0xff;
109
- image.bitmap.data[idx + 1] = color.rect >> 16 & 0xff;
110
- image.bitmap.data[idx + 2] = color.rect >> 8 & 0xff;
111
- image.bitmap.data[idx + 3] = 0xff & color.rect;
112
- });
113
- try {
114
- cachedFont = cachedFont || await loadFonts();
115
- } catch (error) {
116
- console.error('Error loading font', error);
731
+ rectX = paddedLeft;
732
+ rectY = paddedTop + 2;
117
733
  }
118
- image.print(cachedFont, rectX, rectY, {
119
- text: indexId.toString(),
120
- alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
121
- alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE
122
- }, rectWidth, rectHeight);
734
+ fillRect(overlayPixels, imageWidth, imageHeight, {
735
+ x: rectX,
736
+ y: rectY,
737
+ w: rectWidth,
738
+ h: rectHeight
739
+ }, color.rect);
740
+ const textX = rectX + Math.floor((rectWidth - textWidth) / 2);
741
+ const textY = rectY + Math.floor((rectHeight - textHeight) / 2);
742
+ drawNumber(overlayPixels, imageWidth, imageHeight, indexId, textX, textY, color.text);
123
743
  }
124
- return image;
744
+ return overlayPixels;
125
745
  };
126
746
  const compositeElementInfoImg = async (options)=>{
127
747
  node_assert(options.inputImgBase64, 'inputImgBase64 is required');
748
+ const { PhotonImage, SamplingFilter, resize } = await get_photon();
128
749
  let width = 0;
129
750
  let height = 0;
130
- let jimpImage;
131
- const Jimp = await get_jimp();
132
751
  if (options.size) {
133
752
  width = options.size.width;
134
753
  height = options.size.height;
135
754
  }
755
+ let photonImage = await photonFromBase64(options.inputImgBase64);
136
756
  if (width && height) {
137
- const imageBuffer = await bufferFromBase64(options.inputImgBase64);
138
- jimpImage = await readImageBuffer(imageBuffer, Jimp);
139
- const imageBitmap = jimpImage.bitmap;
140
- if (imageBitmap.width !== width || imageBitmap.height !== height) jimpImage.resize(width, height, Jimp.RESIZE_NEAREST_NEIGHBOR);
757
+ const imageWidth = photonImage.get_width();
758
+ const imageHeight = photonImage.get_height();
759
+ if (imageWidth !== width || imageHeight !== height) {
760
+ const resized = resize(photonImage, width, height, SamplingFilter.Nearest);
761
+ photonImage.free();
762
+ photonImage = resized;
763
+ }
141
764
  } else {
142
- const info = await imageInfoOfBase64(options.inputImgBase64);
143
- width = info.width;
144
- height = info.height;
145
- jimpImage = info.jimpImage;
765
+ width = photonImage.get_width();
766
+ height = photonImage.get_height();
767
+ }
768
+ if (!width || !height) {
769
+ photonImage.free();
770
+ throw Error('Image processing failed because width or height is undefined');
146
771
  }
147
- if (!width || !height) throw Error('Image processing failed because width or height is undefined');
148
772
  const { elementsPositionInfo, prompt } = options;
149
- const result = await Promise.resolve(jimpImage).then(async (image)=>{
150
- const svgOverlay = await createSvgOverlay(elementsPositionInfo, width, height, options.annotationPadding, options.borderThickness, prompt);
151
- const svgImage = await Jimp.read(svgOverlay);
152
- const compositeImage = await image.composite(svgImage, 0, 0, {
153
- mode: Jimp.BLEND_SOURCE_OVER,
154
- opacitySource: 1,
155
- opacityDest: 1
156
- });
157
- return compositeImage;
158
- }).then(async (compositeImage)=>{
159
- compositeImage.quality(90);
160
- const base64 = await compositeImage.getBase64Async(Jimp.MIME_JPEG);
773
+ try {
774
+ const basePixels = photonImage.get_raw_pixels();
775
+ const overlayPixels = await createSvgOverlay(elementsPositionInfo, width, height, options.annotationPadding, options.borderThickness, prompt);
776
+ const blendedPixels = blendPixels(basePixels, overlayPixels, width, height);
777
+ const resultImage = new PhotonImage(blendedPixels, width, height);
778
+ const base64 = await photonToBase64(resultImage, 90);
779
+ resultImage.free();
161
780
  return base64;
162
- }).catch((error)=>{
163
- throw error;
164
- });
165
- return result;
781
+ } finally{
782
+ photonImage.free();
783
+ }
166
784
  };
167
785
  const processImageElementInfo = async (options)=>{
168
786
  const base64Image = options.inputImgBase64.split(';base64,').pop();
@@ -182,4 +800,25 @@ const processImageElementInfo = async (options)=>{
182
800
  compositeElementInfoImgWithoutTextBase64
183
801
  };
184
802
  };
185
- export { compositeElementInfoImg, processImageElementInfo };
803
+ async function annotateRects(imgBase64, rects, prompt) {
804
+ const markedImage = await compositeElementInfoImg({
805
+ inputImgBase64: imgBase64,
806
+ elementsPositionInfo: rects.map((rect, index)=>({
807
+ id: `rect-${index}`,
808
+ rect,
809
+ indexId: index + 1,
810
+ attributes: {
811
+ nodeType: NodeType.CONTAINER
812
+ },
813
+ content: '',
814
+ center: [
815
+ rect.left + rect.width / 2,
816
+ rect.top + rect.height / 2
817
+ ]
818
+ })),
819
+ annotationPadding: 0,
820
+ prompt
821
+ });
822
+ return markedImage;
823
+ }
824
+ export { annotateRects, compositeElementInfoImg, processImageElementInfo };