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 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.parse(content);
48
- return import_koishi.h.select(nodes, "img") ?? import_koishi.h.select(nodes, "image");
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.content);
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 quoteContent = quote?.content;
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") ?? import_koishi.h.select(quoteElements, "image");
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 mirrorDraw(out, srcCanvas, mode) {
87
- const w = srcCanvas.width;
88
- const h2 = srcCanvas.height;
89
- out.clearRect(0, 0, w, h2);
90
- if (mode === "left") {
91
- const rightWidth = Math.floor(w / 2);
92
- const leftKeepWidth = w - rightWidth;
93
- out.drawImage(srcCanvas, 0, 0, leftKeepWidth, h2, 0, 0, leftKeepWidth, h2);
94
- out.save();
95
- out.translate(w, 0);
96
- out.scale(-1, 1);
97
- out.drawImage(srcCanvas, 0, 0, rightWidth, h2, 0, 0, rightWidth, h2);
98
- out.restore();
99
- return;
100
- }
101
- if (mode === "right") {
102
- const leftWidth = Math.floor(w / 2);
103
- const rightKeepWidth = w - leftWidth;
104
- out.drawImage(
105
- srcCanvas,
106
- leftWidth,
107
- 0,
108
- rightKeepWidth,
109
- h2,
110
- leftWidth,
111
- 0,
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
- const topHeight = Math.floor(h2 / 2);
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(mirrorDraw, "mirrorDraw");
114
+ __name(mirrorPixels, "mirrorPixels");
173
115
  async function mirrorStaticImage(canvas, buffer, mode) {
174
- const img = await canvas.loadImage(buffer);
175
- const w = img.width;
176
- const h2 = img.height;
177
- const srcCanvas = canvas.createCanvas(w, h2);
178
- const src = srcCanvas.getContext("2d");
179
- src.drawImage(img, 0, 0, w, h2);
180
- const outCanvas = canvas.createCanvas(w, h2);
181
- const out = outCanvas.getContext("2d");
182
- mirrorDraw(out, srcCanvas, mode);
183
- return {
184
- buffer: outCanvas.toBuffer("image/png"),
185
- mime: "image/png"
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 srcCanvas = canvas.createCanvas(width, height);
197
- const src = srcCanvas.getContext("2d");
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
- const encoder = new import_gif_encoder_2.default(width, height);
201
- encoder.start();
202
- encoder.setRepeat(0);
203
- encoder.setQuality?.(10);
204
- for (const frame of frames) {
205
- const patch = frame.patch;
206
- if (!patch || patch.length !== width * height * 4) {
207
- throw new Error("Unsupported GIF frame patch format");
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
- const imageData = src.createImageData(width, height);
210
- imageData.data.set(patch);
211
- src.putImageData(imageData, 0, 0);
212
- mirrorDraw(out, srcCanvas, mode);
213
- const delayCs = typeof frame.delay === "number" ? frame.delay : 0;
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) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-img-tool",
3
3
  "description": "modify img",
4
- "version": "0.0.2",
4
+ "version": "1.0.2",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [