Loading packages/client/src/components/CanvasWrapper.tsx +37 −3 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -65,6 +67,11 @@ const CanvasInner = () => { const canvas = useRef<Canvas>(); const { config, setCanvasPosition, setCursor, setPixelWhois } = useAppContext(); const { x: templateX, y: templateY, enable: templateEnable, } = useTemplateContext(); const PanZoom = useContext(RendererContext); /** Loading Loading @@ -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) { Loading @@ -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] Loading packages/client/src/lib/template.ts +57 −2 Original line number Diff line number Diff line 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; Loading Loading @@ -28,6 +29,8 @@ enum TemplateStyle { } export class Template extends EventEmitter<TemplateEvents> { static instance: Template; config: ClientConfig; $wrapper: HTMLDivElement; Loading @@ -43,9 +46,10 @@ export class Template extends EventEmitter<TemplateEvents> { 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; Loading @@ -53,7 +57,7 @@ export class Template extends EventEmitter<TemplateEvents> { 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); Loading Loading @@ -115,6 +119,57 @@ export class Template extends EventEmitter<TemplateEvents> { } } 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(); Loading packages/client/src/lib/utils.ts +14 −0 Original line number Diff line number Diff line Loading @@ -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 <T = unknown, Error = string>( endpoint: string, Loading Loading
packages/client/src/components/CanvasWrapper.tsx +37 −3 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -65,6 +67,11 @@ const CanvasInner = () => { const canvas = useRef<Canvas>(); const { config, setCanvasPosition, setCursor, setPixelWhois } = useAppContext(); const { x: templateX, y: templateY, enable: templateEnable, } = useTemplateContext(); const PanZoom = useContext(RendererContext); /** Loading Loading @@ -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) { Loading @@ -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] Loading
packages/client/src/lib/template.ts +57 −2 Original line number Diff line number Diff line 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; Loading Loading @@ -28,6 +29,8 @@ enum TemplateStyle { } export class Template extends EventEmitter<TemplateEvents> { static instance: Template; config: ClientConfig; $wrapper: HTMLDivElement; Loading @@ -43,9 +46,10 @@ export class Template extends EventEmitter<TemplateEvents> { 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; Loading @@ -53,7 +57,7 @@ export class Template extends EventEmitter<TemplateEvents> { 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); Loading Loading @@ -115,6 +119,57 @@ export class Template extends EventEmitter<TemplateEvents> { } } 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(); Loading
packages/client/src/lib/utils.ts +14 −0 Original line number Diff line number Diff line Loading @@ -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 <T = unknown, Error = string>( endpoint: string, Loading