From a5f1b165fdb3edb3cf253aa39e9d2136c10b5402 Mon Sep 17 00:00:00 2001 From: Julian Freeman Date: Sun, 18 Jan 2026 23:55:14 -0400 Subject: [PATCH] fix wateramrk --- src-tauri/src/lib.rs | 22 ++++++++++++++-------- src/components/HeroView.vue | 23 ++++++++++++++++------- src/stores/gallery.ts | 4 ++-- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 394fa2a..9decacb 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -97,7 +97,7 @@ use image::Pixel; use rayon::prelude::*; use std::path::Path; use imageproc::drawing::draw_text_mut; -use ab_glyph::{FontRef, PxScale, Font}; +use ab_glyph::{FontRef, PxScale}; // Embed the font to ensure it's always available without path issues const FONT_DATA: &[u8] = include_bytes!("../assets/fonts/Roboto-Regular.ttf"); @@ -178,13 +178,13 @@ async fn export_batch(images: Vec, watermark: WatermarkSettings let final_scale = PxScale::from(scale_px); let (final_t_width, final_t_height) = imageproc::drawing::text_size(final_scale, &font, &watermark.text); - // 4. Determine Position - let (pos_x_pct, pos_y_pct) = if watermark.manual_override { + // 4. Determine Position (Center based) + let (mut pos_x_pct, mut pos_y_pct) = if watermark.manual_override { (watermark.manual_position.x, watermark.manual_position.y) } else { match calculate_zca_internal(&dynamic_img) { Ok(res) => (res.x, res.y), - Err(_) => (0.5, 0.95), + Err(_) => (0.5, 0.96), // Default fallback lowered } }; @@ -192,12 +192,18 @@ async fn export_batch(images: Vec, watermark: WatermarkSettings let center_x = width as f64 * pos_x_pct; let center_y = height as f64 * pos_y_pct; - let x = (center_x - (final_t_width as f64 / 2.0)) as i32; - let y = (center_y - (final_t_height as f64 / 2.0)) as i32; + let mut x = (center_x - (final_t_width as f64 / 2.0)) as i32; + let mut y = (center_y - (final_t_height as f64 / 2.0)) as i32; - // Clamp to be visible? imageproc handles out of bounds by clipping. + // 5. Boundary Clamping (Ensure text stays inside image with padding) + let padding = (height as f64 * 0.01).max(5.0) as i32; // 1% padding + let max_x = (width as i32 - final_t_width as i32 - padding).max(padding); + let max_y = (height as i32 - final_t_height as i32 - padding).max(padding); - // 5. Draw + x = x.clamp(padding, max_x); + y = y.clamp(padding, max_y); + + // 6. Draw draw_text_mut( &mut base_img, text_color, diff --git a/src/components/HeroView.vue b/src/components/HeroView.vue index 5f975ce..42e3cbb 100644 --- a/src/components/HeroView.vue +++ b/src/components/HeroView.vue @@ -7,6 +7,7 @@ const store = useGalleryStore(); const isDragging = ref(false); const dragStart = ref({ x: 0, y: 0 }); const imgRef = ref(null); +const containerRef = ref(null); const renderHeight = ref(0); let resizeObserver: ResizeObserver | null = null; @@ -46,19 +47,21 @@ const position = computed(() => { }); const onMouseDown = (e: MouseEvent) => { + // Only allow dragging if target is the watermark itself (or its child) + // Actually, user should be able to click watermark to start drag. + // e.preventDefault to stop image drag e.preventDefault(); isDragging.value = true; dragStart.value = { x: e.clientX, y: e.clientY }; }; const onMouseMove = (e: MouseEvent) => { - if (!isDragging.value || !store.selectedImage) return; + if (!isDragging.value || !store.selectedImage || !containerRef.value) return; // Calculate delta in percentage relative to the image container - const container = (e.currentTarget as HTMLElement).closest('.image-container'); - if (!container) return; + const rect = containerRef.value.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; - const rect = container.getBoundingClientRect(); const deltaX = (e.clientX - dragStart.value.x) / rect.width; const deltaY = (e.clientY - dragStart.value.y) / rect.height; @@ -66,9 +69,14 @@ const onMouseMove = (e: MouseEvent) => { let newX = position.value.x + deltaX; let newY = position.value.y + deltaY; - // Clamp - newX = Math.max(0, Math.min(1, newX)); - newY = Math.max(0, Math.min(1, newY)); + // Clamp logic (Simple 0-1 clamp for center point first) + // Ideally we should clamp so the box stays inside, but we don't know the exact text width in px easily here without measurement. + // For MVP, clamping center to [0, 1] is "safe enough" to prevent losing it, + // but user asked for "not exceeding boundary". + // Visual clamping: slightly inside 0-1. + const padding = 0.01; + newX = Math.max(padding, Math.min(1 - padding, newX)); + newY = Math.max(padding, Math.min(1 - padding, newY)); // Set store to manual mode immediately store.updateWatermarkSettings({ @@ -97,6 +105,7 @@ const onMouseLeave = () => { >
diff --git a/src/stores/gallery.ts b/src/stores/gallery.ts index 5df597a..ef4d924 100644 --- a/src/stores/gallery.ts +++ b/src/stores/gallery.ts @@ -29,9 +29,9 @@ export const useGalleryStore = defineStore("gallery", () => { text: 'Watermark', color: '#FFFFFF', opacity: 0.8, - scale: 0.05, // 5% of height default + scale: 0.025, // Reduced from 0.05 manual_override: false, - manual_position: { x: 0.5, y: 0.9 } + manual_position: { x: 0.5, y: 0.96 } // Lowered from 0.9 }); const selectedImage = computed(() => {