Skip to content

Instantly share code, notes, and snippets.

@sureshkumargondi
Forked from colecrouter/preview.png.ts
Created March 20, 2022 07:48
Show Gist options
  • Save sureshkumargondi/68c23b8edd2afcd94f8b1c5b6f5c0be4 to your computer and use it in GitHub Desktop.
Save sureshkumargondi/68c23b8edd2afcd94f8b1c5b6f5c0be4 to your computer and use it in GitHub Desktop.
ES6 janky png editing NO CANVAS Cloudflare Workers/Pages Functions
import { Inventory, Item, SharedKit } from '../types/kit';
import { decode, encode, toRGBA8 } from 'upng-js';
let MCTEXTURES: Textures;
const INV_WIDTH = 181;
const INV_HEIGHT = 115;
const ITEM_WIDTH = 32;
const ITEM_HEIGHT = 32;
export const onRequestGet = async ({ request, env, next, data }) => {
const url = new URL(request.url);
const param = url.search;
const subject = param.substring(1);
// Loading textures, get kit data at the same time
let kitData: SharedKit;
let texturePromise = fetch("[redacted]").then((res) => res.json());
let kitPromise = fetch(`[redacted]`, { headers: { "Authorization": `Bearer ${env.API_KEY}` } }).then(res => res.json());
await Promise.all([texturePromise, kitPromise]).then(([textures, kit]) => {
MCTEXTURES = textures;
kitData = kit;
});
// Do the thing
const encoded = await paintInventory(kitData.data.inventory);
return new Response(encoded, { status: 200, headers: { "content-type": "image/png", "cache-control": "public", "expires": new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toUTCString() /* One week */ } });
};
const paintInventory = async (inv: Inventory) => {
const imposeImage = async (bgLayer: ArrayBuffer, fg: Item | undefined, x: number, y: number): Promise<Uint8Array> => {
let bgView = new Uint8Array(bgLayer); // Convert our buffer to a view. We need to do this inside our function for some reason, otherwise it becomes undefined?
const fgBase64 = itemToBase64(fg);
if (fgBase64 === undefined) { return; }
const fgBuffer = await base64ToBuffer(fgBase64);
const fgDecoded = decode(fgBuffer); // Convert our item to a buffer
const fgLayer = toRGBA8(fgDecoded)[0]; // Decode our buffer to RGBA
const fgView = new Uint8Array(fgLayer); // Convert our buffer to a view
// Literally don't try to even understand this block of code, I have no idea how I did it or how I made it work.
x++, y--; // They don't come out aligned for some reason
for (let i = 0; i < 1024; i += 4) {
// For some reason, the icons are 32x32 instead of 16x16, so I'm doing my best to downscale them in one for loop. This would be 100x simpler if they were the right size.
const rowOffset = Math.floor((i) / (ITEM_WIDTH / 2 * 4)) /* Current row number */ * ((INV_WIDTH - (ITEM_WIDTH / 2)) * 4 /* How many pixels until new line*/) + (x * 4) /* X offset */ + (y * (INV_WIDTH * 4)); /* Y offset */
const columnOffset = Math.floor((i / 2) / ITEM_WIDTH) * (ITEM_WIDTH * 2) /* Current column number */;
// Transparency hack
if (fgView[((i + columnOffset) * 2) + 3] === 0) { continue; }
try {
// Set all four channels
bgView[(i) + rowOffset] = fgView[((i + columnOffset) * 2)];
bgView[(i + 1) + rowOffset] = fgView[((i + columnOffset) * 2) + 1];
bgView[(i + 2) + rowOffset] = fgView[((i + columnOffset) * 2) + 2];
bgView[(i + 3) + rowOffset] = fgView[((i + columnOffset) * 2) + 3];
} catch (err) { }
}
};
const base64ToBuffer = async (base64: string): Promise<ArrayBuffer> => {
const byteString = atob(base64.split(',')[1]); // Remove "data:image/png;base64,", then decode into binary string
const ab = new ArrayBuffer(byteString.length); // Create ArrayBuffer with the length of the decoded base64 string
let ia = new Uint8Array(ab); // Create a view of ab. For more info on views ArrayBuffers, see https://javascript.info/arraybuffer-binary-arrays
// Fill the ArrayBuffer with the decoded base64 string
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return ab; // Return the updated ArrayBuffer, since the view is linked to it, and it is therefore updated
};
// Helper to get the base64 texture of an Item
const itemToBase64 = (item: Item): string | undefined => {
return MCTEXTURES.items.find((el) => el.id === `minecraft:${item?.material.toLowerCase()}`)?.texture;
};
// Make inventory image
//const b64 = itemToBase64(json.data.inventory.head as Item);
const bgBase64 = "[redacted]";
const bgBuffer = await base64ToBuffer(bgBase64);
const bgDecoded = decode(bgBuffer); // Convert our item to a buffer
const bgLayer = toRGBA8(bgDecoded)[0]; // Decode our buffer to RGBA
// Add image
await imposeImage(bgLayer, inv.offhand, 9 + (5 * 18), 10);
// Return image
let newImg = encode([bgLayer], INV_WIDTH, INV_HEIGHT, 0); // We insert img instead of imgView because imgView is linked to img
return newImg;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment