koishi-plugin-img-tool 0.0.2 → 1.0.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/lib/index.d.ts +0 -8
- package/lib/index.js +182 -125
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -4,12 +4,4 @@ export declare const using: readonly ["canvas"];
|
|
|
4
4
|
export interface Config {
|
|
5
5
|
}
|
|
6
6
|
export declare const Config: Schema<Config>;
|
|
7
|
-
declare module "koishi" {
|
|
8
|
-
interface Context {
|
|
9
|
-
canvas: {
|
|
10
|
-
createCanvas: (width: number, height: number) => any;
|
|
11
|
-
loadImage: (data: Buffer) => Promise<any>;
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
7
|
export declare function apply(ctx: Context, config: Config): void;
|
package/lib/index.js
CHANGED
|
@@ -44,8 +44,8 @@ var using = ["canvas"];
|
|
|
44
44
|
var Config = import_koishi.Schema.object({});
|
|
45
45
|
function selectFirstImageElement(content) {
|
|
46
46
|
if (!content) return;
|
|
47
|
-
const nodes = import_koishi.h.
|
|
48
|
-
return
|
|
47
|
+
const nodes = import_koishi.h.select(content.elements, "img");
|
|
48
|
+
return nodes?.[0];
|
|
49
49
|
}
|
|
50
50
|
__name(selectFirstImageElement, "selectFirstImageElement");
|
|
51
51
|
function getImageSrc(el) {
|
|
@@ -54,17 +54,16 @@ function getImageSrc(el) {
|
|
|
54
54
|
}
|
|
55
55
|
__name(getImageSrc, "getImageSrc");
|
|
56
56
|
function findImageSrc(session) {
|
|
57
|
-
const direct = selectFirstImageElement(session.
|
|
57
|
+
const direct = selectFirstImageElement(session.event.message);
|
|
58
58
|
const directSrc = getImageSrc(direct);
|
|
59
59
|
if (directSrc) return directSrc;
|
|
60
60
|
const quote = session.quote;
|
|
61
|
-
const
|
|
62
|
-
const quoteEl = selectFirstImageElement(quoteContent);
|
|
61
|
+
const quoteEl = selectFirstImageElement(quote);
|
|
63
62
|
const quoteSrc = getImageSrc(quoteEl);
|
|
64
63
|
if (quoteSrc) return quoteSrc;
|
|
65
64
|
const quoteElements = quote?.elements;
|
|
66
65
|
if (quoteElements) {
|
|
67
|
-
const quoteEl2 = import_koishi.h.select(quoteElements, "img")
|
|
66
|
+
const quoteEl2 = import_koishi.h.select(quoteElements, "img")?.[0];
|
|
68
67
|
const quoteSrc2 = getImageSrc(quoteEl2);
|
|
69
68
|
if (quoteSrc2) return quoteSrc2;
|
|
70
69
|
}
|
|
@@ -83,107 +82,140 @@ function toArrayBuffer(buffer) {
|
|
|
83
82
|
);
|
|
84
83
|
}
|
|
85
84
|
__name(toArrayBuffer, "toArrayBuffer");
|
|
86
|
-
function
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
rightKeepWidth,
|
|
113
|
-
h2
|
|
114
|
-
);
|
|
115
|
-
out.save();
|
|
116
|
-
out.translate(w, 0);
|
|
117
|
-
out.scale(-1, 1);
|
|
118
|
-
out.drawImage(
|
|
119
|
-
srcCanvas,
|
|
120
|
-
w - leftWidth,
|
|
121
|
-
0,
|
|
122
|
-
leftWidth,
|
|
123
|
-
h2,
|
|
124
|
-
w - leftWidth,
|
|
125
|
-
0,
|
|
126
|
-
leftWidth,
|
|
127
|
-
h2
|
|
128
|
-
);
|
|
129
|
-
out.restore();
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
if (mode === "up") {
|
|
133
|
-
const bottomHeight = Math.floor(h2 / 2);
|
|
134
|
-
const topKeepHeight = h2 - bottomHeight;
|
|
135
|
-
out.drawImage(srcCanvas, 0, 0, w, topKeepHeight, 0, 0, w, topKeepHeight);
|
|
136
|
-
out.save();
|
|
137
|
-
out.translate(0, h2);
|
|
138
|
-
out.scale(1, -1);
|
|
139
|
-
out.drawImage(srcCanvas, 0, 0, w, bottomHeight, 0, 0, w, bottomHeight);
|
|
140
|
-
out.restore();
|
|
141
|
-
return;
|
|
85
|
+
function mirrorPixels(src, width, height, mode) {
|
|
86
|
+
const dst = new Uint8ClampedArray(src.length);
|
|
87
|
+
const ceilHalfW = Math.ceil(width / 2);
|
|
88
|
+
const floorHalfW = Math.floor(width / 2);
|
|
89
|
+
const ceilHalfH = Math.ceil(height / 2);
|
|
90
|
+
const floorHalfH = Math.floor(height / 2);
|
|
91
|
+
for (let y = 0; y < height; y++) {
|
|
92
|
+
for (let x = 0; x < width; x++) {
|
|
93
|
+
let sx = x;
|
|
94
|
+
let sy = y;
|
|
95
|
+
if (mode === "left") {
|
|
96
|
+
sx = x < ceilHalfW ? x : width - 1 - x;
|
|
97
|
+
} else if (mode === "right") {
|
|
98
|
+
sx = x < floorHalfW ? width - 1 - x : x;
|
|
99
|
+
} else if (mode === "up") {
|
|
100
|
+
sy = y < ceilHalfH ? y : height - 1 - y;
|
|
101
|
+
} else {
|
|
102
|
+
sy = y < floorHalfH ? height - 1 - y : y;
|
|
103
|
+
}
|
|
104
|
+
const si = (sy * width + sx) * 4;
|
|
105
|
+
const di = (y * width + x) * 4;
|
|
106
|
+
dst[di] = src[si];
|
|
107
|
+
dst[di + 1] = src[si + 1];
|
|
108
|
+
dst[di + 2] = src[si + 2];
|
|
109
|
+
dst[di + 3] = src[si + 3];
|
|
110
|
+
}
|
|
142
111
|
}
|
|
143
|
-
|
|
144
|
-
const bottomKeepHeight = h2 - topHeight;
|
|
145
|
-
out.drawImage(
|
|
146
|
-
srcCanvas,
|
|
147
|
-
0,
|
|
148
|
-
topHeight,
|
|
149
|
-
w,
|
|
150
|
-
bottomKeepHeight,
|
|
151
|
-
0,
|
|
152
|
-
topHeight,
|
|
153
|
-
w,
|
|
154
|
-
bottomKeepHeight
|
|
155
|
-
);
|
|
156
|
-
out.save();
|
|
157
|
-
out.translate(0, h2);
|
|
158
|
-
out.scale(1, -1);
|
|
159
|
-
out.drawImage(
|
|
160
|
-
srcCanvas,
|
|
161
|
-
0,
|
|
162
|
-
h2 - topHeight,
|
|
163
|
-
w,
|
|
164
|
-
topHeight,
|
|
165
|
-
0,
|
|
166
|
-
h2 - topHeight,
|
|
167
|
-
w,
|
|
168
|
-
topHeight
|
|
169
|
-
);
|
|
170
|
-
out.restore();
|
|
112
|
+
return dst;
|
|
171
113
|
}
|
|
172
|
-
__name(
|
|
114
|
+
__name(mirrorPixels, "mirrorPixels");
|
|
173
115
|
async function mirrorStaticImage(canvas, buffer, mode) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
116
|
+
let img;
|
|
117
|
+
let outCanvas;
|
|
118
|
+
try {
|
|
119
|
+
img = await canvas.loadImage(buffer);
|
|
120
|
+
const w = img.naturalWidth ?? img.width;
|
|
121
|
+
const h2 = img.naturalHeight ?? img.height;
|
|
122
|
+
outCanvas = await canvas.createCanvas(w, h2);
|
|
123
|
+
const out = outCanvas.getContext("2d");
|
|
124
|
+
out.clearRect(0, 0, w, h2);
|
|
125
|
+
if (mode === "left") {
|
|
126
|
+
const rightWidth = Math.floor(w / 2);
|
|
127
|
+
const leftKeepWidth = w - rightWidth;
|
|
128
|
+
out.drawImage(
|
|
129
|
+
img,
|
|
130
|
+
0,
|
|
131
|
+
0,
|
|
132
|
+
leftKeepWidth,
|
|
133
|
+
h2,
|
|
134
|
+
0,
|
|
135
|
+
0,
|
|
136
|
+
leftKeepWidth,
|
|
137
|
+
h2
|
|
138
|
+
);
|
|
139
|
+
out.save();
|
|
140
|
+
out.translate(w, 0);
|
|
141
|
+
out.scale(-1, 1);
|
|
142
|
+
out.drawImage(img, 0, 0, rightWidth, h2, 0, 0, rightWidth, h2);
|
|
143
|
+
out.restore();
|
|
144
|
+
} else if (mode === "right") {
|
|
145
|
+
const leftWidth = Math.floor(w / 2);
|
|
146
|
+
const rightKeepWidth = w - leftWidth;
|
|
147
|
+
out.drawImage(
|
|
148
|
+
img,
|
|
149
|
+
w - rightKeepWidth,
|
|
150
|
+
0,
|
|
151
|
+
rightKeepWidth,
|
|
152
|
+
h2,
|
|
153
|
+
w - rightKeepWidth,
|
|
154
|
+
0,
|
|
155
|
+
rightKeepWidth,
|
|
156
|
+
h2
|
|
157
|
+
);
|
|
158
|
+
out.save();
|
|
159
|
+
out.translate(leftWidth, 0);
|
|
160
|
+
out.scale(-1, 1);
|
|
161
|
+
out.drawImage(
|
|
162
|
+
img,
|
|
163
|
+
w - leftWidth,
|
|
164
|
+
0,
|
|
165
|
+
leftWidth,
|
|
166
|
+
h2,
|
|
167
|
+
0,
|
|
168
|
+
0,
|
|
169
|
+
leftWidth,
|
|
170
|
+
h2
|
|
171
|
+
);
|
|
172
|
+
out.restore();
|
|
173
|
+
} else if (mode === "up") {
|
|
174
|
+
const bottomHeight = Math.floor(h2 / 2);
|
|
175
|
+
const topKeepHeight = h2 - bottomHeight;
|
|
176
|
+
out.drawImage(img, 0, 0, w, topKeepHeight, 0, 0, w, topKeepHeight);
|
|
177
|
+
out.save();
|
|
178
|
+
out.translate(0, h2);
|
|
179
|
+
out.scale(1, -1);
|
|
180
|
+
out.drawImage(img, 0, 0, w, bottomHeight, 0, 0, w, bottomHeight);
|
|
181
|
+
out.restore();
|
|
182
|
+
} else {
|
|
183
|
+
const topHeight = Math.floor(h2 / 2);
|
|
184
|
+
const bottomKeepHeight = h2 - topHeight;
|
|
185
|
+
out.drawImage(
|
|
186
|
+
img,
|
|
187
|
+
0,
|
|
188
|
+
h2 - bottomKeepHeight,
|
|
189
|
+
w,
|
|
190
|
+
bottomKeepHeight,
|
|
191
|
+
0,
|
|
192
|
+
h2 - bottomKeepHeight,
|
|
193
|
+
w,
|
|
194
|
+
bottomKeepHeight
|
|
195
|
+
);
|
|
196
|
+
out.save();
|
|
197
|
+
out.translate(0, topHeight);
|
|
198
|
+
out.scale(1, -1);
|
|
199
|
+
out.drawImage(
|
|
200
|
+
img,
|
|
201
|
+
0,
|
|
202
|
+
h2 - topHeight,
|
|
203
|
+
w,
|
|
204
|
+
topHeight,
|
|
205
|
+
0,
|
|
206
|
+
0,
|
|
207
|
+
w,
|
|
208
|
+
topHeight
|
|
209
|
+
);
|
|
210
|
+
out.restore();
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
buffer: await outCanvas.toBuffer("image/png"),
|
|
214
|
+
mime: "image/png"
|
|
215
|
+
};
|
|
216
|
+
} finally {
|
|
217
|
+
await Promise.allSettled([img?.dispose?.(), outCanvas?.dispose?.()]);
|
|
218
|
+
}
|
|
187
219
|
}
|
|
188
220
|
__name(mirrorStaticImage, "mirrorStaticImage");
|
|
189
221
|
async function mirrorGifImage(canvas, buffer, mode) {
|
|
@@ -193,30 +225,55 @@ async function mirrorGifImage(canvas, buffer, mode) {
|
|
|
193
225
|
const width = gif.lsd?.width ?? frames[0].dims?.width;
|
|
194
226
|
const height = gif.lsd?.height ?? frames[0].dims?.height;
|
|
195
227
|
if (!width || !height) throw new Error("Unable to determine GIF size");
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
const outCanvas = canvas.createCanvas(width, height);
|
|
228
|
+
const screenCanvas = await canvas.createCanvas(width, height);
|
|
229
|
+
const screen = screenCanvas.getContext("2d");
|
|
230
|
+
const outCanvas = await canvas.createCanvas(width, height);
|
|
199
231
|
const out = outCanvas.getContext("2d");
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
232
|
+
try {
|
|
233
|
+
const encoder = new import_gif_encoder_2.default(width, height);
|
|
234
|
+
encoder.start();
|
|
235
|
+
encoder.setRepeat(0);
|
|
236
|
+
encoder.setQuality?.(10);
|
|
237
|
+
for (const frame of frames) {
|
|
238
|
+
const patch = frame.patch;
|
|
239
|
+
const dims = frame.dims || {};
|
|
240
|
+
const left = dims.left ?? 0;
|
|
241
|
+
const top = dims.top ?? 0;
|
|
242
|
+
const pw = dims.width ?? width;
|
|
243
|
+
const ph = dims.height ?? height;
|
|
244
|
+
if (!patch || patch.length !== pw * ph * 4) {
|
|
245
|
+
throw new Error("Unsupported GIF frame patch format");
|
|
246
|
+
}
|
|
247
|
+
const disposalType = frame.disposalType ?? 0;
|
|
248
|
+
const restore = disposalType === 3 ? screen.getImageData(0, 0, width, height) : null;
|
|
249
|
+
const region = screen.getImageData(left, top, pw, ph);
|
|
250
|
+
for (let i = 0; i < patch.length; i += 4) {
|
|
251
|
+
if (patch[i + 3] === 0) continue;
|
|
252
|
+
region.data[i] = patch[i];
|
|
253
|
+
region.data[i + 1] = patch[i + 1];
|
|
254
|
+
region.data[i + 2] = patch[i + 2];
|
|
255
|
+
region.data[i + 3] = patch[i + 3];
|
|
256
|
+
}
|
|
257
|
+
screen.putImageData(region, left, top);
|
|
258
|
+
const screenData = screen.getImageData(0, 0, width, height);
|
|
259
|
+
const outData = out.createImageData(width, height);
|
|
260
|
+
outData.data.set(mirrorPixels(screenData.data, width, height, mode));
|
|
261
|
+
out.putImageData(outData, 0, 0);
|
|
262
|
+
const delayMs = typeof frame.delay === "number" ? frame.delay : 100;
|
|
263
|
+
encoder.setDelay(Math.max(20, delayMs));
|
|
264
|
+
encoder.addFrame(out);
|
|
265
|
+
if (disposalType === 2) {
|
|
266
|
+
screen.clearRect(left, top, pw, ph);
|
|
267
|
+
} else if (disposalType === 3 && restore) {
|
|
268
|
+
screen.putImageData(restore, 0, 0);
|
|
269
|
+
}
|
|
208
270
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
encoder.setDelay(Math.max(0, delayCs * 10));
|
|
215
|
-
encoder.addFrame(out);
|
|
271
|
+
encoder.finish();
|
|
272
|
+
const outBuffer = Buffer.from(encoder.out.getData());
|
|
273
|
+
return { buffer: outBuffer, mime: "image/gif" };
|
|
274
|
+
} finally {
|
|
275
|
+
await Promise.allSettled([screenCanvas.dispose?.(), outCanvas.dispose?.()]);
|
|
216
276
|
}
|
|
217
|
-
encoder.finish();
|
|
218
|
-
const outBuffer = Buffer.from(encoder.out.getData());
|
|
219
|
-
return { buffer: outBuffer, mime: "image/gif" };
|
|
220
277
|
}
|
|
221
278
|
__name(mirrorGifImage, "mirrorGifImage");
|
|
222
279
|
async function handleMirror(ctx, session, mode) {
|