mirror of
https://github.com/System-End/Vencord.git
synced 2026-04-19 19:45:09 +00:00
PetPet: fix dark colours being transparent in the output (#3571)
Co-authored-by: V <vendicated@riseup.net>
This commit is contained in:
parent
3e036d1612
commit
58ef6585af
2 changed files with 53 additions and 10 deletions
|
|
@ -22,7 +22,7 @@ import { makeLazy } from "@utils/lazy";
|
|||
import definePlugin from "@utils/types";
|
||||
import { CommandArgument, CommandContext } from "@vencord/discord-types";
|
||||
import { DraftType, UploadAttachmentStore, UploadHandler, UploadManager, UserUtils } from "@webpack/common";
|
||||
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||
import { GIFEncoder, nearestColorIndex, quantize } from "gifenc";
|
||||
|
||||
const DEFAULT_DELAY = 20;
|
||||
const DEFAULT_RESOLUTION = 128;
|
||||
|
|
@ -46,7 +46,7 @@ function loadImage(source: File | string) {
|
|||
URL.revokeObjectURL(url);
|
||||
resolve(img);
|
||||
};
|
||||
img.onerror = (event, _source, _lineno, _colno, err) => reject(err || event);
|
||||
img.onerror = _event => reject(Error(`An error occurred while loading ${url}. Check the console for more info.`));
|
||||
img.crossOrigin = "Anonymous";
|
||||
img.src = url;
|
||||
});
|
||||
|
|
@ -82,10 +82,33 @@ async function resolveImage(options: CommandArgument[], ctx: CommandContext, noS
|
|||
return null;
|
||||
}
|
||||
|
||||
function rgb888_to_rgb565(r: number, g: number, b: number): number {
|
||||
return ((r << 8) & 0xf800) | ((g << 3) & 0x07e0) | (b >> 3);
|
||||
}
|
||||
|
||||
function applyPaletteTransparent(data: Uint8Array | Uint8ClampedArray, palette: number[][], cache: number[], threshold: number): Uint8Array {
|
||||
const index = new Uint8Array(Math.floor(data.length / 4));
|
||||
|
||||
for (let i = 0; i < index.length; i += 1) {
|
||||
const r = data[4 * i];
|
||||
const g = data[4 * i + 1];
|
||||
const b = data[4 * i + 2];
|
||||
const a = data[4 * i + 3];
|
||||
|
||||
if (a < threshold) {
|
||||
index[i] = 255;
|
||||
} else {
|
||||
const key = rgb888_to_rgb565(r, g, b);
|
||||
index[i] = key in cache ? cache[key] : (cache[key] = nearestColorIndex(palette, [r, g, b]));
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "petpet",
|
||||
description: "Adds a /petpet slash command to create headpet gifs from any image",
|
||||
authors: [Devs.Ven],
|
||||
authors: [Devs.Ven, Devs.u32],
|
||||
commands: [
|
||||
{
|
||||
inputType: ApplicationCommandInputType.BUILT_IN,
|
||||
|
|
@ -94,7 +117,7 @@ export default definePlugin({
|
|||
options: [
|
||||
{
|
||||
name: "delay",
|
||||
description: "The delay between each frame. Defaults to 20.",
|
||||
description: "The delay between each frame in ms. Rounded to nearest 10ms. Defaults to the minimum value of 20.",
|
||||
type: ApplicationCommandOptionType.INTEGER
|
||||
},
|
||||
{
|
||||
|
|
@ -141,16 +164,32 @@ export default definePlugin({
|
|||
const avatar = await loadImage(url);
|
||||
|
||||
const delay = findOption(opts, "delay", DEFAULT_DELAY);
|
||||
// Frame delays < 20ms don't function correctly on chromium and firefox
|
||||
if (delay < 20) return sendBotMessage(cmdCtx.channel.id, { content: "Delay must be at least 20." });
|
||||
|
||||
const resolution = findOption(opts, "resolution", DEFAULT_RESOLUTION);
|
||||
|
||||
const gif = GIFEncoder();
|
||||
|
||||
const paletteImageSize = Math.min(120, resolution);
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = canvas.height = resolution;
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
canvas.width = resolution;
|
||||
// Ensure there is sufficient space for the palette generation image
|
||||
canvas.height = Math.max(resolution, 2 * paletteImageSize);
|
||||
|
||||
const ctx = canvas.getContext("2d", { willReadFrequently: true })!;
|
||||
|
||||
UploadManager.clearAll(cmdCtx.channel.id, DraftType.SlashCommand);
|
||||
|
||||
// Generate palette from an image where hand and avatar are fully visible
|
||||
ctx.drawImage(avatar, 0, paletteImageSize, 0.8 * paletteImageSize, 0.8 * paletteImageSize);
|
||||
ctx.drawImage(frames[0], 0, 0, paletteImageSize, paletteImageSize);
|
||||
const { data } = ctx.getImageData(0, 0, paletteImageSize, 2 * paletteImageSize);
|
||||
const palette = quantize(data, 255);
|
||||
|
||||
const cache = new Array(2 ** 16);
|
||||
|
||||
for (let i = 0; i < FRAMES; i++) {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
|
|
@ -164,13 +203,13 @@ export default definePlugin({
|
|||
ctx.drawImage(frames[i], 0, 0, resolution, resolution);
|
||||
|
||||
const { data } = ctx.getImageData(0, 0, resolution, resolution);
|
||||
const palette = quantize(data, 256);
|
||||
const index = applyPalette(data, palette);
|
||||
const index = applyPaletteTransparent(data, palette, cache, 1);
|
||||
|
||||
gif.writeFrame(index, resolution, resolution, {
|
||||
transparent: true,
|
||||
palette,
|
||||
transparentIndex: 255,
|
||||
delay,
|
||||
palette: i === 0 ? palette : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -620,7 +620,11 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||
vv: {
|
||||
name: "VV",
|
||||
id: 254866377087778816n
|
||||
}
|
||||
},
|
||||
u32: {
|
||||
name: "u32",
|
||||
id: 1063237286818488351n,
|
||||
},
|
||||
} satisfies Record<string, Dev>);
|
||||
|
||||
// iife so #__PURE__ works correctly
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue