Pixel pick on template (fixes #70)

This commit is contained in:
Grant 2024-07-07 17:51:51 -06:00
parent 924815e629
commit 46214c56cf
3 changed files with 108 additions and 5 deletions

View File

@ -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<Canvas>();
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]

View File

@ -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<TemplateEvents> {
static instance: Template;
config: ClientConfig;
$wrapper: HTMLDivElement;
@ -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;
@ -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);
@ -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();

View File

@ -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,