@midscene/shared 0.4.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.
@@ -0,0 +1,240 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import Jimp from 'jimp';
3
+ import type { Rect } from '../types';
4
+
5
+ /**
6
+ * Saves a Base64-encoded image to a file
7
+ *
8
+ * @param options - An object containing the Base64-encoded image data and the output file path
9
+ * @param options.base64Data - The Base64-encoded image data
10
+ * @param options.outputPath - The path where the image will be saved
11
+ * @throws Error if there is an error during the saving process
12
+ */
13
+ export async function saveBase64Image(options: {
14
+ base64Data: string;
15
+ outputPath: string;
16
+ }): Promise<void> {
17
+ const { base64Data, outputPath } = options;
18
+ // Remove the base64 data prefix (if any)
19
+ const base64Image = base64Data.split(';base64,').pop() || base64Data;
20
+
21
+ // Converts base64 data to buffer
22
+ const imageBuffer = Buffer.from(base64Image, 'base64');
23
+
24
+ // Use Jimp to process the image and save it to the specified location
25
+ const image = await Jimp.read(imageBuffer);
26
+ await image.writeAsync(outputPath);
27
+
28
+ console.log('Image successfully written to file.');
29
+ }
30
+
31
+ /**
32
+ * Transforms an image path into a base64-encoded string
33
+ * @param inputPath - The path of the image file to be encoded
34
+ * @returns A Promise that resolves to a base64-encoded string representing the image file
35
+ */
36
+ export async function transformImgPathToBase64(inputPath: string) {
37
+ // Use Jimp to process images and generate base64 data
38
+ const image = await Jimp.read(inputPath);
39
+ const buffer = await image.getBufferAsync(Jimp.MIME_PNG);
40
+ return buffer.toString('base64');
41
+ }
42
+
43
+ /**
44
+ * Resizes an image from a base64-encoded string
45
+ *
46
+ * @param base64Data - A base64-encoded string representing the image
47
+ * @returns A Promise that resolves to a base64-encoded string representing the resized image
48
+ * @throws An error if the width or height cannot be determined from the metadata
49
+ */
50
+ export async function resizeImg(base64Data: string) {
51
+ // Remove the base64 data prefix (if any)
52
+ const base64Image = base64Data.split(';base64,').pop() || base64Data;
53
+
54
+ // Converts base64 data to buffer
55
+ const imageBuffer = Buffer.from(base64Image, 'base64');
56
+
57
+ const image = await Jimp.read(imageBuffer);
58
+ const { width, height } = image.bitmap;
59
+ if (!width || !height) {
60
+ throw Error('undefined width or height with url');
61
+ }
62
+
63
+ const newSize = calculateNewDimensions(width, height);
64
+
65
+ image.resize(newSize.width, newSize.height);
66
+ const buffer = await image.getBufferAsync(Jimp.MIME_PNG);
67
+ return buffer.toString('base64');
68
+ }
69
+
70
+ /**
71
+ * Calculates new dimensions for an image while maintaining its aspect ratio.
72
+ *
73
+ * This function is designed to resize an image to fit within a specified maximum width and height
74
+ * while maintaining the original aspect ratio. If the original width or height exceeds the maximum
75
+ * dimensions, the image will be scaled down to fit.
76
+ *
77
+ * @param {number} originalWidth - The original width of the image.
78
+ * @param {number} originalHeight - The original height of the image.
79
+ * @returns {Object} An object containing the new width and height.
80
+ * @throws {Error} Throws an error if the width or height is not a positive number.
81
+ */
82
+ export function calculateNewDimensions(
83
+ originalWidth: number,
84
+ originalHeight: number,
85
+ ) {
86
+ // In low mode, the image is scaled to 512x512 pixels and 85 tokens are used to represent the image.
87
+ // In high mode, the model looks at low-resolution images and then creates detailed crop images, using 170 tokens for each 512x512 pixel tile. In practical applications, it is recommended to control the image size within 2048x768 pixels
88
+ const maxWidth = 768; // Maximum width
89
+ const maxHeight = 2048; // Maximum height
90
+ let newWidth = originalWidth;
91
+ let newHeight = originalHeight;
92
+
93
+ // Calculate the aspect ratio
94
+ const aspectRatio = originalWidth / originalHeight;
95
+
96
+ // Width adjustment
97
+ if (originalWidth > maxWidth) {
98
+ newWidth = maxWidth;
99
+ newHeight = newWidth / aspectRatio;
100
+ }
101
+
102
+ // Adjust height
103
+ if (newHeight > maxHeight) {
104
+ newHeight = maxHeight;
105
+ newWidth = newHeight * aspectRatio;
106
+ }
107
+
108
+ return {
109
+ width: Math.round(newWidth),
110
+ height: Math.round(newHeight),
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Trims an image and returns the trimming information, including the offset from the left and top edges, and the trimmed width and height
116
+ *
117
+ * @param image - The image to be trimmed. This can be a file path or a Buffer object containing the image data
118
+ * @returns A Promise that resolves to an object containing the trimming information. If the image does not need to be trimmed, this object will be null
119
+ */
120
+ export async function trimImage(image: string | Buffer): Promise<{
121
+ trimOffsetLeft: number; // attention: trimOffsetLeft is a negative number
122
+ trimOffsetTop: number; // so as trimOffsetTop
123
+ width: number;
124
+ height: number;
125
+ } | null> {
126
+ const jimpImage = await Jimp.read(
127
+ Buffer.isBuffer(image) ? image : Buffer.from(image),
128
+ );
129
+ const { width, height } = jimpImage.bitmap;
130
+
131
+ if (width <= 3 || height <= 3) {
132
+ return null;
133
+ }
134
+
135
+ const trimmedImage = jimpImage.autocrop();
136
+ const { width: trimmedWidth, height: trimmedHeight } = trimmedImage.bitmap;
137
+
138
+ const trimOffsetLeft = (width - trimmedWidth) / 2;
139
+ const trimOffsetTop = (height - trimmedHeight) / 2;
140
+
141
+ if (trimOffsetLeft === 0 && trimOffsetTop === 0) {
142
+ return null;
143
+ }
144
+
145
+ return {
146
+ trimOffsetLeft: -trimOffsetLeft,
147
+ trimOffsetTop: -trimOffsetTop,
148
+ width: trimmedWidth,
149
+ height: trimmedHeight,
150
+ };
151
+ }
152
+
153
+ /**
154
+ * Aligns an image's coordinate system based on trimming information
155
+ *
156
+ * This function takes an image and a center rectangle as input. It first extracts the center
157
+ * rectangle from the image using Jimp and converts it to a buffer. Then, it calls
158
+ * the trimImage function to obtain the trimming information of the buffer image. If there is no
159
+ * trimming information, the original center rectangle is returned. If there is trimming information,
160
+ * a new rectangle is created based on the trimming information, with its top-left corner
161
+ * positioned at the negative offset of the trimming from the original center rectangle's top-left
162
+ * corner, and its width and height set to the trimmed image's dimensions.
163
+ *
164
+ * @param image The image file path or buffer to be processed
165
+ * @param center The center rectangle of the image, which is used to extract and align
166
+ * @returns A Promise that resolves to a rectangle object representing the aligned coordinates
167
+ * @throws Error if there is an error during image processing
168
+ */
169
+ // export async function alignCoordByTrim(
170
+ // image: string | Buffer,
171
+ // centerRect: Rect,
172
+ // ): Promise<Rect> {
173
+ // const isBuffer = Buffer.isBuffer(image);
174
+ // let jimpImage;
175
+ // if (isBuffer) {
176
+ // jimpImage = await Jimp.read(image);
177
+ // } else {
178
+ // jimpImage = await Jimp.read(image);
179
+ // }
180
+
181
+ // const { width, height } = jimpImage.bitmap;
182
+ // if (width <= 3 || height <= 3) {
183
+ // return centerRect;
184
+ // }
185
+ // const zeroSize: Rect = {
186
+ // left: 0,
187
+ // top: 0,
188
+ // width: -1,
189
+ // height: -1,
190
+ // };
191
+ // const finalCenterRect: Rect = { ...centerRect };
192
+ // if (centerRect.left > width || centerRect.top > height) {
193
+ // return zeroSize;
194
+ // }
195
+
196
+ // if (finalCenterRect.left < 0) {
197
+ // finalCenterRect.width += finalCenterRect.left;
198
+ // finalCenterRect.left = 0;
199
+ // }
200
+
201
+ // if (finalCenterRect.top < 0) {
202
+ // finalCenterRect.height += finalCenterRect.top;
203
+ // finalCenterRect.top = 0;
204
+ // }
205
+
206
+ // if (finalCenterRect.left + finalCenterRect.width > width) {
207
+ // finalCenterRect.width = width - finalCenterRect.left;
208
+ // }
209
+ // if (finalCenterRect.top + finalCenterRect.height > height) {
210
+ // finalCenterRect.height = height - finalCenterRect.top;
211
+ // }
212
+
213
+ // if (finalCenterRect.width <= 3 || finalCenterRect.height <= 3) {
214
+ // return finalCenterRect;
215
+ // }
216
+
217
+ // try {
218
+ // const croppedImage = jimpImage.crop(
219
+ // centerRect.left,
220
+ // centerRect.top,
221
+ // centerRect.width,
222
+ // centerRect.height,
223
+ // );
224
+ // const buffer = await croppedImage.getBufferAsync(Jimp.MIME_PNG);
225
+ // const trimInfo = await trimImage(buffer);
226
+ // if (!trimInfo) {
227
+ // return centerRect;
228
+ // }
229
+
230
+ // return {
231
+ // left: centerRect.left - trimInfo.trimOffsetLeft,
232
+ // top: centerRect.top - trimInfo.trimOffsetTop,
233
+ // width: trimInfo.width,
234
+ // height: trimInfo.height,
235
+ // };
236
+ // } catch (e) {
237
+ // console.log(jimpImage.bitmap);
238
+ // throw e;
239
+ // }
240
+ // }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export default function () {
2
+ return 'hello world';
3
+ }
@@ -0,0 +1 @@
1
+ /// <reference types='@modern-js/module-tools/types' />
@@ -0,0 +1,11 @@
1
+ export interface Point {
2
+ left: number;
3
+ top: number;
4
+ }
5
+
6
+ export interface Size {
7
+ width: number;
8
+ height: number;
9
+ }
10
+
11
+ export type Rect = Point & Size;