diff --git a/packages/greenbox-script/.prettierignore b/packages/greenbox-script/.prettierignore new file mode 100644 index 0000000..cba51b3 --- /dev/null +++ b/packages/greenbox-script/.prettierignore @@ -0,0 +1 @@ +__fixtures__ \ No newline at end of file diff --git a/packages/greenbox-web/src/components/AlphaImage.tsx b/packages/greenbox-web/src/components/AlphaImage.tsx index 5aa62f6..113101f 100644 --- a/packages/greenbox-web/src/components/AlphaImage.tsx +++ b/packages/greenbox-web/src/components/AlphaImage.tsx @@ -4,7 +4,315 @@ import { useEffect, useMemo, useState } from "react"; import Image from "./Image"; -function createAlphaMask(data: Uint8ClampedArray, width: number) { +function getEraserStartPoints( + src: string, + numberOfPixels: number, + width: number, +) { + const corners = [ + 0, + width * 4 - 8, + numberOfPixels - width * 4, + numberOfPixels - 8, + ]; + switch (src) { + case "itemimages/al_ant.gif": + corners.push(2468, 908); + break; + case "itemimages/animskel.gif": + corners.push(2816, 1872); + break; + case "itemimages/catburglar.gif": + corners.push(808, 3180, 32); + break; + case "itemimages/crab.gif": + corners.push(1148); + break; + case "itemimages/crayongoth.gif": + corners.push(3308); + break; + case "itemimages/cuddlefish.gif": + corners.push(2948); + break; + case "itemimages/familiar20.gif": + corners.push(2344); + break; + case "itemimages/familiar33.gif": + corners.push(1980); + break; + case "itemimages/familiar38.gif": + corners.push(1980); + break; + case "itemimages/familiar39.gif": + corners.push(52); + break; + case "itemimages/frankengnome.gif": + corners.push(3544); + break; + case "itemimages/hatrack.gif": + corners.push(444, 160, 3292); + break; + case "itemimages/hobosheep.gif": + corners.push(3068); + break; + case "itemimages/kobold.gif": + corners.push(3304); + break; + case "itemimages/medium_0.gif": + corners.push(3184); + break; + case "itemimages/partymouse.gif": + corners.push(3328, 1784); + break; + case "itemimages/roboreindeer.gif": + corners.push(2928, 92); + break; + case "itemimages/shortchef.gif": + corners.push(3424); + break; + case "itemimages/vampvint.gif": + corners.push(2696); + break; + case "otherimages/sigils/bugbear.gif": + corners.push(492); + break; + case "otherimages/sigils/c20cheer.gif": + corners.push(6560); + break; + case "otherimages/sigils/chalktat.gif": + corners.push(2940, 3936); + break; + case "otherimages/sigils/class11hc.gif": + corners.push(4288); + break; + case "otherimages/sigils/class20hc.gif": + corners.push(2712, 6888); + break; + case "otherimages/sigils/cliptat.gif": + corners.push(2144, 7856); + break; + case "otherimages/sigils/debbietat.gif": + corners.push(2636); + break; + case "otherimages/sigils/dvinntat.gif": + corners.push(2256, 7056); + break; + case "otherimages/sigils/dvotat5.gif": + corners.push(5484); + break; + case "otherimages/sigils/elbereth.gif": + corners.push(2852, 2704, 1544, 7552, 7700, 1336, 7896); + break; + case "otherimages/sigils/elvtat.gif": + corners.push(3904); + break; + case "otherimages/sigils/eternaltat.gif": + corners.push(8308, 5904, 3504, 1100, 3852, 6260, 5076, 4728, 5548, 3544); + break; + case "otherimages/sigils/fibertat.gif": + corners.push(7144, 3544); + break; + case "otherimages/sigils/frtat.gif": + corners.push(5904); + break; + case "otherimages/sigils/gabtat.gif": + corners.push(3940, 6736); + break; + case "otherimages/sigils/hippy.gif": + corners.push(3468, 3544, 7540, 7480); + break; + case "otherimages/sigils/hothobotat.gif": + corners.push(7104); + break; + case "otherimages/sigils/hourtat.gif": + corners.push(4740, 4880); + break; + case "otherimages/sigils/mosstat.gif": + corners.push(5368); + break; + case "otherimages/sigils/nobeer.gif": + corners.push(5056, 4352, 7900, 1908); + break; + case "otherimages/sigils/nofood.gif": + corners.push(5652, 2700, 3744); + break; + case "otherimages/sigils/nosealtat.gif": + corners.push(3348, 7312, 1904, 5640); + break; + case "otherimages/sigils/oxy.gif": + corners.push(5052, 4952); + break; + case "otherimages/sigils/para_tat.gif": + corners.push(4556, 5292); + break; + case "otherimages/sigils/prealmtat.gif": + corners.push(1724, 1876); + break; + case "otherimages/sigils/pressietat.gif": + corners.push(2256, 1900); + break; + case "otherimages/sigils/sailortat.gif": + corners.push(1308); + break; + case "otherimages/sigils/sgtat.gif": + corners.push(7704, 1712); + break; + case "otherimages/sigils/skelepiratetat.gif": + corners.push(9308, 6508); + break; + case "otherimages/sigils/slimetat.gif": + corners.push(6940, 3476, 3336); + break; + case "otherimages/sigils/smoochtat.gif": + corners.push(3120, 3084); + break; + case "otherimages/sigils/spqktat.gif": + corners.push(1528, 4488); + break; + case "otherimages/sigils/sspdfi5if.gif": + corners.push(3548); + break; + case "otherimages/sigils/tc_tat.gif": + corners.push(5356, 5492); + break; + case "otherimages/sigils/vwgovttat.gif": + corners.push(4080, 5108, 5136); + break; + case "otherimages/sigils/vwsnaketat.gif": + corners.push(5912); + break; + case "otherimages/sigils/wickertat.gif": + corners.push(6956); + break; + case "otherimages/sigils/workouttat.gif": + corners.push(7840); + break; + case "otherimages/sigils/wreathtat.gif": + corners.push(5504); + break; + case "otherimages/sigils/wroughttat.gif": + corners.push(2716); + break; + case "otherimages/sigils/zaptat.gif": + corners.push(6952, 7896); + break; + case "otherimages/sigils/zompirtat.gif": + corners.push(6300); + break; + case "otherimages/trophy/anger_management_about_schmidt.gif": + corners.push(5424); + break; + case "otherimages/trophy/angst_with_extra_cheese.gif": + corners.push(17664, 18336); + break; + case "otherimages/trophy/big_head_todd.gif": + corners.push(20860, 20744); + break; + case "otherimages/trophy/but_it_doesnt_love_me_back.gif": + corners.push(14472, 15148); + break; + case "otherimages/trophy/cuppa_cuppa_burning_goo.gif": + corners.push(24928, 25072); + break; + case "otherimages/trophy/garble_varble_zous.gif": + corners.push(17560); + break; + case "otherimages/trophy/garble_varble_zous.gif": + corners.push(17652, 17968); + break; + case "otherimages/trophy/get_oot_eh.gif": + corners.push(15552, 15660); + break; + case "otherimages/trophy/ghuolishly_good.gif": + corners.push(15288, 15112); + break; + case "otherimages/trophy/haggis_is_as_haggis_does.gif": + corners.push(15392); + break; + case "otherimages/trophy/i_had_to_drink_from_the_liquid_cup.gif": + corners.push(17672, 15932); + break; + case "otherimages/trophy/ilovewesley.gif": + corners.push(18060, 18752); + break; + case "otherimages/trophy/in_a_little_toy_shop.gif": + corners.push(14864, 15540); + break; + case "otherimages/trophy/in_deep_end_ents.gif": + corners.push(14084, 15520); + break; + case "otherimages/trophy/jeremiah_was_a_bullfrog.gif": + corners.push(14864, 15948); + break; + case "otherimages/trophy/ladies_and_gentlemen.gif": + corners.push(17656, 18344); + break; + case "otherimages/trophy/look_what_i_can_do.gif": + corners.push(15944, 15268); + break; + case "otherimages/trophy/my_shrimps_was_dead_and_gone.gif": + corners.push(17648, 19144); + break; + case "otherimages/trophy/no_well_ten_beers.gif": + corners.push(15660, 15544); + break; + case "otherimages/trophy/not_wearing_any_pants.gif": + corners.push(16064, 15148); + break; + case "otherimages/trophy/not_worth_the_wait.gif": + corners.push(15660, 16352); + break; + case "otherimages/trophy/papier_i_hardly_know_her.gif": + corners.push(9708, 7924, 25080); + break; + case "otherimages/trophy/run_over_by_grandma.gif": + corners.push(15652, 15544); + break; + case "otherimages/trophy/the_dude_abides.gif": + corners.push(16452, 17556); + break; + case "otherimages/trophy/the_nastiest_cocktail.gif": + corners.push(15264, 15948); + break; + case "otherimages/trophy/this_this_lemonade.gif": + corners.push(12280); + break; + case "otherimages/trophy/yule_be_happy.gif": + corners.push(23408, 15848); + break; + case "itemimages/movfeast.gif": + corners.push(2816); + break; + case "itemimages/sanehatrack.gif": + corners.push(288, 440, 3288); + break; + case "itemimages/medium_small.gif": + corners.push(3180); + break; + case "itemimages/acuteangel.gif": + corners.push(3416); + break; + case "itemimages/buddybjorn.gif": + corners.push(2196, 1044); + break; + case "itemimages/odetobooze.gif": + corners.push(1900); + break; + case "itemimages/prelude.gif": + corners.push(2328); + break; + case "itemimages/garbagenova.gif": + corners.push(1860); + break; + } + return corners; +} + +function createAlphaMask( + data: Uint8ClampedArray, + width: number, + startingPoints: number[], +) { const mask = new Uint8ClampedArray(data.length).fill(255); const visited = new Set(); @@ -36,7 +344,7 @@ function createAlphaMask(data: Uint8ClampedArray, width: number) { } // Start an erase process from each corner - [0, width * 4 - 8, data.length - width * 4, data.length - 8].forEach(eraser); + startingPoints.forEach(eraser); return mask; } @@ -87,7 +395,16 @@ export default function AlphaImage({ const imageBitmap = await createImageBitmap(blob); ctx.drawImage(imageBitmap, 0, 0); const imageData = ctx.getImageData(0, 0, sourceWidth, sourceHeight); - const maskData = createAlphaMask(imageData.data, sourceWidth); + const startingPoints = getEraserStartPoints( + src, + imageData.data.length, + sourceWidth, + ); + const maskData = createAlphaMask( + imageData.data, + sourceWidth, + startingPoints, + ); const d = new ImageData(maskData, sourceWidth, sourceHeight); ctx.putImageData(d, 0, 0); const data = canvas.toDataURL(); diff --git a/packages/greenbox-web/src/components/Thing.tsx b/packages/greenbox-web/src/components/Thing.tsx index 581cb76..1d2aa4e 100644 --- a/packages/greenbox-web/src/components/Thing.tsx +++ b/packages/greenbox-web/src/components/Thing.tsx @@ -1,6 +1,6 @@ import { Box, LinkBox, LinkOverlay, useToken } from "@chakra-ui/react"; import he from "he"; -import { forwardRef } from "react"; +import { forwardRef, useEffect, useState } from "react"; import { useAppSelector } from "../hooks"; @@ -34,7 +34,7 @@ function styleFromStatus(state: StateType, bg: string) { } default: { return { - backgroundColor: bg, + backgroundColor: "complete", }; } } @@ -70,11 +70,34 @@ export default forwardRef(function Thing( ref, ) { const [bg] = useToken("colors", ["accent"]); + const [eraserTags, setEraserTags] = useState([]); const style = styleFromStatus(status, bg); const clashes = useAppSelector((state) => state.wikiClashes); const wikiLink = guessWikiLink(link, name, type, clashes); + useEffect(() => { + const handle = (event: KeyboardEvent) => { + if (event.key !== "Shift") return; + if (event.type === "keydown") { + setEraserTags([]); + } + if (event.type === "keyup") { + if (!eraserTags.length) return; + console.log( + `case "${image}": corners.push(${eraserTags.join(", ")}); break;`, + ); + } + }; + + window.addEventListener("keydown", handle); + window.addEventListener("keyup", handle); + return () => { + window.removeEventListener("keydown", handle); + window.removeEventListener("keyup", handle); + }; + }, [eraserTags, image]); + return ( (function Thing( filter: style.backgroundColor ? "brightness(90%)" : undefined, backgroundColor: style.backgroundColor || "blackAlpha.50", }} + onClick={(event) => { + if (!event.shiftKey) return; + const img = event.currentTarget + .querySelector("img") + ?.getBoundingClientRect(); + if (!img) return; + event.preventDefault(); + localStorage.removeItem(`alphamask-${image}`); + const x = Math.floor(event.clientX - img.left); + const y = Math.floor(event.clientY - img.top); + setEraserTags([...eraserTags, (y * img.width + x) * 4]); + return false; + }} {...rest} > {badges && (