From a6fd3f0939920482f633478be5aefa70d0b23cd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Sz=C3=BCcs?= Date: Wed, 10 Dec 2025 11:17:45 +0100 Subject: [PATCH] feat(image-uploader): improve background removal handling and streamline image data processing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Balázs Szücs --- .../annotation/shared/ImageUploader.tsx | 24 ++++++++++--------- .../components/tools/sign/SignSettings.tsx | 17 +++---------- frontend/src/core/utils/imageTransparency.ts | 8 +++---- 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/frontend/src/core/components/annotation/shared/ImageUploader.tsx b/frontend/src/core/components/annotation/shared/ImageUploader.tsx index b9576f181..f565914b7 100644 --- a/frontend/src/core/components/annotation/shared/ImageUploader.tsx +++ b/frontend/src/core/components/annotation/shared/ImageUploader.tsx @@ -12,7 +12,6 @@ interface ImageUploaderProps { hint?: string; allowBackgroundRemoval?: boolean; onProcessedImageData?: (dataUrl: string | null) => void; - currentImageData?: string; } export const ImageUploader: React.FC = ({ @@ -22,8 +21,7 @@ export const ImageUploader: React.FC = ({ placeholder, hint, allowBackgroundRemoval = false, - onProcessedImageData, - currentImageData + onProcessedImageData }) => { const { t } = useTranslation(); const [removeBackground, setRemoveBackground] = useState(false); @@ -31,7 +29,7 @@ export const ImageUploader: React.FC = ({ const [originalImageData, setOriginalImageData] = useState(null); const [isProcessing, setIsProcessing] = useState(false); - const processImage = async (imageSource: File | string, shouldRemoveBackground: boolean) => { + const processImage = async (imageSource: File | string, shouldRemoveBackground: boolean): Promise => { if (shouldRemoveBackground && allowBackgroundRemoval) { setIsProcessing(true); try { @@ -40,7 +38,6 @@ export const ImageUploader: React.FC = ({ tolerance: 15 }); onProcessedImageData?.(transparentImageDataUrl); - return transparentImageDataUrl; } catch (error) { console.error('Error removing background:', error); onProcessedImageData?.(null); @@ -48,12 +45,18 @@ export const ImageUploader: React.FC = ({ setIsProcessing(false); } } else { - if (originalImageData) { - onProcessedImageData?.(originalImageData); + // When background removal is disabled, return the original image data + if (typeof imageSource === 'string') { + onProcessedImageData?.(imageSource); + } else { + // Convert File to data URL if needed + const reader = new FileReader(); + reader.onload = (e) => { + onProcessedImageData?.(e.target?.result as string); + }; + reader.readAsDataURL(imageSource); } - setIsProcessing(false); } - return null; }; const handleImageChange = async (file: File | null) => { @@ -90,11 +93,10 @@ export const ImageUploader: React.FC = ({ }; const handleBackgroundRemovalChange = async (checked: boolean) => { + if (isProcessing) return; // Prevent race conditions setRemoveBackground(checked); if (originalImageData) { await processImage(originalImageData, checked); - } else if (currentFile) { - await processImage(currentFile, checked); } }; diff --git a/frontend/src/core/components/tools/sign/SignSettings.tsx b/frontend/src/core/components/tools/sign/SignSettings.tsx index 0726a3963..c27adf31a 100644 --- a/frontend/src/core/components/tools/sign/SignSettings.tsx +++ b/frontend/src/core/components/tools/sign/SignSettings.tsx @@ -461,24 +461,13 @@ const SignSettings = ({ const handleImageChange = async (file: File | null) => { if (file && !disabled) { try { - const result = await new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = (e) => { - if (e.target?.result) { - resolve(e.target.result as string); - } else { - reject(new Error('Failed to read file')); - } - }; - reader.onerror = () => reject(reader.error); - reader.readAsDataURL(file); - }); - // Reset pause state and directly activate placement setPlacementManuallyPaused(false); lastAppliedPlacementKey.current = null; - setImageSignatureData(result); + // Image data will be set by onProcessedImageData callback in ImageUploader + // This avoids the race condition where both handleImageChange and onProcessedImageData + // try to set the image data, potentially with the wrong version // Directly activate placement on image upload if (typeof window !== 'undefined') { diff --git a/frontend/src/core/utils/imageTransparency.ts b/frontend/src/core/utils/imageTransparency.ts index d6e2eb160..17fe2f42a 100644 --- a/frontend/src/core/utils/imageTransparency.ts +++ b/frontend/src/core/utils/imageTransparency.ts @@ -105,10 +105,10 @@ function detectCornerColor(imageData: ImageData): { r: number; g: number; b: num const sampleSize = 5; const corners = [ - { x: sampleSize, y: sampleSize }, // top-left - { x: width - sampleSize - 1, y: sampleSize }, // top-right - { x: sampleSize, y: height - sampleSize - 1 }, // bottom-left - { x: width - sampleSize - 1, y: height - sampleSize - 1 } // bottom-right + { x: 0, y: 0 }, // top-left + { x: width - sampleSize, y: 0 }, // top-right + { x: 0, y: height - sampleSize }, // bottom-left + { x: width - sampleSize, y: height - sampleSize } // bottom-right ]; let totalR = 0, totalG = 0, totalB = 0;