diff --git a/packages/client/src/components/CanvasWrapper.tsx b/packages/client/src/components/CanvasWrapper.tsx index ca1c1f5..d1d9471 100644 --- a/packages/client/src/components/CanvasWrapper.tsx +++ b/packages/client/src/components/CanvasWrapper.tsx @@ -7,10 +7,12 @@ import { ViewportMoveEvent } from "@sc07-canvas/lib/src/renderer/PanZoom"; import throttle from "lodash.throttle"; import { IPosition } from "@sc07-canvas/lib/src/net"; import { Template } from "./Templating/Template"; +import { Template as TemplateCl } from "../lib/template"; import { IRouterData, Router } from "../lib/router"; import { KeybindManager } from "../lib/keybinds"; import { BlankOverlay } from "./Overlay/BlankOverlay"; import { HeatmapOverlay } from "./Overlay/HeatmapOverlay"; +import { useTemplateContext } from "../contexts/TemplateContext"; export const CanvasWrapper = () => { const { config } = useAppContext(); @@ -65,6 +67,11 @@ const CanvasInner = () => { const canvas = useRef(); const { config, setCanvasPosition, setCursor, setPixelWhois } = useAppContext(); + const { + x: templateX, + y: templateY, + enable: templateEnable, + } = useTemplateContext(); const PanZoom = useContext(RendererContext); /** @@ -129,6 +136,19 @@ const CanvasInner = () => { [canvas.current] ); + const getTemplatePixel = useCallback( + (x: number, y: number) => { + if (!templateEnable) return; + if (x < templateX || y < templateY) return; + + x -= templateX; + y -= templateY; + + return TemplateCl.instance.getPixel(x, y); + }, + [templateX, templateY] + ); + const handlePickPixel = useCallback( ({ clientX, clientY }: { clientX: number; clientY: number }) => { if (!canvas.current) { @@ -141,13 +161,27 @@ const CanvasInner = () => { const [x, y] = canvas.current.screenToPos(clientX, clientY); if (!isCoordInCanvas(x, y)) return; // out of bounds - const pixel = canvas.current.getPixel(x, y); - if (!pixel) return; + let pixelColor = -1; + + const templatePixel = getTemplatePixel(x, y); + if (templatePixel) { + pixelColor = + canvas.current.Pallete.getColorFromHex(templatePixel.slice(1))?.id || + -1; + } + + if (pixelColor === -1) { + pixelColor = canvas.current.getPixel(x, y)?.color || -1; + } + + if (pixelColor === -1) { + return; + } // no need to use canvas#setCursor as Palette.tsx already does that setCursor((v) => ({ ...v, - color: pixel.color, + color: pixelColor, })); }, [canvas.current] diff --git a/packages/client/src/lib/template.ts b/packages/client/src/lib/template.ts index deabade..3dd392e 100644 --- a/packages/client/src/lib/template.ts +++ b/packages/client/src/lib/template.ts @@ -1,6 +1,7 @@ import EventEmitter from "eventemitter3"; import { WebGLUtils } from "./webgl"; import { ClientConfig } from "@sc07-canvas/lib/src/net"; +import { rgbToHex } from "./utils"; interface TemplateEvents { updateImageURL(url: string | undefined): void; @@ -28,6 +29,8 @@ enum TemplateStyle { } export class Template extends EventEmitter { + static instance: Template; + config: ClientConfig; $wrapper: HTMLDivElement; @@ -43,9 +46,10 @@ export class Template extends EventEmitter { constructor(config: ClientConfig, templateHolder: HTMLDivElement) { super(); + Template.instance = this; this.config = config; - console.log("template init", config, templateHolder); + console.log("[Template] Initialize", config, templateHolder); this.$wrapper = templateHolder; @@ -53,7 +57,7 @@ export class Template extends EventEmitter { this.$imageLoader.style.setProperty("display", "none"); this.$imageLoader.setAttribute("crossorigin", ""); this.$imageLoader.addEventListener("load", () => { - console.log("imageLoader loaded image"); + console.log("[Template] Image loaded"); if (!this.options.width) { this.setOption("width", this.$imageLoader.naturalWidth); this.emit("autoDetectWidth", this.$imageLoader.naturalWidth); @@ -115,6 +119,57 @@ export class Template extends EventEmitter { } } + getPixel(x: number, y: number): string | undefined { + if (!this.context) { + console.warn("[Template#getPixel] No context is available"); + return undefined; + } + + const width = this.context.drawingBufferWidth; + const height = this.context.drawingBufferHeight; + + const arr = new Uint8Array(4 * width * height); + this.context.bindFramebuffer( + this.context.FRAMEBUFFER, + this.framebuffers.intermediate + ); + + if (x < 0 || y < 0 || x > width || y > height) { + return undefined; + } + + this.context.readPixels( + 0, + 0, + width, + height, + this.context.RGBA, + this.context.UNSIGNED_BYTE, + arr + ); + this.context.bindFramebuffer( + this.context.FRAMEBUFFER, + this.framebuffers.main + ); + + const pixels = new Uint8Array(4 * width * height); + const length = width * height * 4; + const row = width * 4; + const end = (height - 1) * row; + for (let i = 0; i < length; i += row) { + pixels.set(arr.subarray(i, i + row), end - i); + } + + const [r, g, b, a] = pixels.slice( + 4 * (y * this.context.drawingBufferWidth + x), + 4 * (y * this.context.drawingBufferWidth + x) + 4 + ); + + if (a === 254) return undefined; + + return rgbToHex(r, g, b); + } + rasterizeTemplate() { this.downscaleTemplate(); this.stylizeTemplate(); diff --git a/packages/client/src/lib/utils.ts b/packages/client/src/lib/utils.ts index e2891e4..0c579a1 100644 --- a/packages/client/src/lib/utils.ts +++ b/packages/client/src/lib/utils.ts @@ -17,6 +17,20 @@ export const getRenderer = (): Renderer => { Debug._getRenderer = getRenderer; +export const rgbToHex = (r: number, g: number, b: number) => { + function componentToHex(c: number) { + var hex = c.toString(16); + return hex.length == 1 ? "0" + hex : hex; + } + + return ( + "#" + + componentToHex(r) + + componentToHex(g) + + componentToHex(b) + ).toUpperCase(); +}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any export const api = async ( endpoint: string,