From 9f29892c2434f54f07ffe1a6508dec46d8688029 Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Sat, 24 Aug 2024 08:54:20 +1000
Subject: [PATCH] feat(ui): colored mask preview image

---
 .../common/CanvasEntityPreviewImage.tsx       | 46 +++++++++++++++++--
 1 file changed, 43 insertions(+), 3 deletions(-)

diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityPreviewImage.tsx b/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityPreviewImage.tsx
index 161eed1fda..142f2cdacc 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityPreviewImage.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityPreviewImage.tsx
@@ -1,13 +1,36 @@
 import { Box, chakra, Flex } from '@invoke-ai/ui-library';
 import { useStore } from '@nanostores/react';
+import { createSelector } from '@reduxjs/toolkit';
+import { rgbColorToString } from 'common/util/colorCodeTransformers';
 import { useEntityAdapter } from 'features/controlLayers/contexts/EntityAdapterContext';
+import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
 import { TRANSPARENCY_CHECKER_PATTERN } from 'features/controlLayers/konva/constants';
-import { memo, useEffect, useRef } from 'react';
+import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
+import { memo, useEffect, useMemo, useRef } from 'react';
+import { useSelector } from 'react-redux';
 
 const ChakraCanvas = chakra.canvas;
 
+const PADDING = 4;
+
 export const CanvasEntityPreviewImage = memo(() => {
+  const entityIdentifier = useEntityIdentifierContext();
   const adapter = useEntityAdapter();
+  const selectMaskColor = useMemo(
+    () =>
+      createSelector(selectCanvasV2Slice, (state) => {
+        const entity = selectEntity(state, entityIdentifier);
+        if (!entity) {
+          return null;
+        }
+        if (entity.type === 'inpaint_mask' || entity.type === 'regional_guidance') {
+          return rgbColorToString(entity.fill.color);
+        }
+        return null;
+      }),
+    [entityIdentifier]
+  );
+  const maskColor = useSelector(selectMaskColor);
   const containerRef = useRef<HTMLDivElement>(null);
   const canvasRef = useRef<HTMLCanvasElement>(null);
   const cache = useStore(adapter.renderer.$canvasCache);
@@ -26,8 +49,25 @@ export const CanvasEntityPreviewImage = memo(() => {
     canvasRef.current.width = rect.width;
     canvasRef.current.height = rect.height;
 
-    ctx.drawImage(canvas, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height);
-  }, [adapter.transformer, adapter.transformer.nodeRect, adapter.transformer.pixelRect, cache]);
+    const scale = containerRef.current.offsetWidth / rect.width;
+
+    const sx = rect.x;
+    const sy = rect.y;
+    const sWidth = rect.width;
+    const sHeight = rect.height;
+    const dx = PADDING / scale;
+    const dy = PADDING / scale;
+    const dWidth = rect.width - (PADDING * 2) / scale;
+    const dHeight = rect.height - (PADDING * 2) / scale;
+
+    ctx.drawImage(canvas, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
+
+    if (maskColor) {
+      ctx.fillStyle = maskColor;
+      ctx.globalCompositeOperation = 'source-in';
+      ctx.fillRect(0, 0, rect.width, rect.height);
+    }
+  }, [adapter.transformer, adapter.transformer.nodeRect, adapter.transformer.pixelRect, cache, maskColor]);
 
   return (
     <Flex