fix wateramrk
This commit is contained in:
@@ -97,7 +97,7 @@ use image::Pixel;
|
|||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use imageproc::drawing::draw_text_mut;
|
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
|
// Embed the font to ensure it's always available without path issues
|
||||||
const FONT_DATA: &[u8] = include_bytes!("../assets/fonts/Roboto-Regular.ttf");
|
const FONT_DATA: &[u8] = include_bytes!("../assets/fonts/Roboto-Regular.ttf");
|
||||||
@@ -178,13 +178,13 @@ async fn export_batch(images: Vec<ExportImageTask>, watermark: WatermarkSettings
|
|||||||
let final_scale = PxScale::from(scale_px);
|
let final_scale = PxScale::from(scale_px);
|
||||||
let (final_t_width, final_t_height) = imageproc::drawing::text_size(final_scale, &font, &watermark.text);
|
let (final_t_width, final_t_height) = imageproc::drawing::text_size(final_scale, &font, &watermark.text);
|
||||||
|
|
||||||
// 4. Determine Position
|
// 4. Determine Position (Center based)
|
||||||
let (pos_x_pct, pos_y_pct) = if watermark.manual_override {
|
let (mut pos_x_pct, mut pos_y_pct) = if watermark.manual_override {
|
||||||
(watermark.manual_position.x, watermark.manual_position.y)
|
(watermark.manual_position.x, watermark.manual_position.y)
|
||||||
} else {
|
} else {
|
||||||
match calculate_zca_internal(&dynamic_img) {
|
match calculate_zca_internal(&dynamic_img) {
|
||||||
Ok(res) => (res.x, res.y),
|
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<ExportImageTask>, watermark: WatermarkSettings
|
|||||||
let center_x = width as f64 * pos_x_pct;
|
let center_x = width as f64 * pos_x_pct;
|
||||||
let center_y = height as f64 * pos_y_pct;
|
let center_y = height as f64 * pos_y_pct;
|
||||||
|
|
||||||
let x = (center_x - (final_t_width as f64 / 2.0)) as i32;
|
let mut 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 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(
|
draw_text_mut(
|
||||||
&mut base_img,
|
&mut base_img,
|
||||||
text_color,
|
text_color,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const store = useGalleryStore();
|
|||||||
const isDragging = ref(false);
|
const isDragging = ref(false);
|
||||||
const dragStart = ref({ x: 0, y: 0 });
|
const dragStart = ref({ x: 0, y: 0 });
|
||||||
const imgRef = ref<HTMLImageElement | null>(null);
|
const imgRef = ref<HTMLImageElement | null>(null);
|
||||||
|
const containerRef = ref<HTMLElement | null>(null);
|
||||||
const renderHeight = ref(0);
|
const renderHeight = ref(0);
|
||||||
|
|
||||||
let resizeObserver: ResizeObserver | null = null;
|
let resizeObserver: ResizeObserver | null = null;
|
||||||
@@ -46,19 +47,21 @@ const position = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onMouseDown = (e: MouseEvent) => {
|
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();
|
e.preventDefault();
|
||||||
isDragging.value = true;
|
isDragging.value = true;
|
||||||
dragStart.value = { x: e.clientX, y: e.clientY };
|
dragStart.value = { x: e.clientX, y: e.clientY };
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseMove = (e: MouseEvent) => {
|
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
|
// Calculate delta in percentage relative to the image container
|
||||||
const container = (e.currentTarget as HTMLElement).closest('.image-container');
|
const rect = containerRef.value.getBoundingClientRect();
|
||||||
if (!container) return;
|
if (rect.width === 0 || rect.height === 0) return;
|
||||||
|
|
||||||
const rect = container.getBoundingClientRect();
|
|
||||||
const deltaX = (e.clientX - dragStart.value.x) / rect.width;
|
const deltaX = (e.clientX - dragStart.value.x) / rect.width;
|
||||||
const deltaY = (e.clientY - dragStart.value.y) / rect.height;
|
const deltaY = (e.clientY - dragStart.value.y) / rect.height;
|
||||||
|
|
||||||
@@ -66,9 +69,14 @@ const onMouseMove = (e: MouseEvent) => {
|
|||||||
let newX = position.value.x + deltaX;
|
let newX = position.value.x + deltaX;
|
||||||
let newY = position.value.y + deltaY;
|
let newY = position.value.y + deltaY;
|
||||||
|
|
||||||
// Clamp
|
// Clamp logic (Simple 0-1 clamp for center point first)
|
||||||
newX = Math.max(0, Math.min(1, newX));
|
// Ideally we should clamp so the box stays inside, but we don't know the exact text width in px easily here without measurement.
|
||||||
newY = Math.max(0, Math.min(1, newY));
|
// 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
|
// Set store to manual mode immediately
|
||||||
store.updateWatermarkSettings({
|
store.updateWatermarkSettings({
|
||||||
@@ -97,6 +105,7 @@ const onMouseLeave = () => {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="store.selectedImage"
|
v-if="store.selectedImage"
|
||||||
|
ref="containerRef"
|
||||||
class="relative inline-flex justify-center items-center image-container"
|
class="relative inline-flex justify-center items-center image-container"
|
||||||
style="max-width: 100%; max-height: 100%;"
|
style="max-width: 100%; max-height: 100%;"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ export const useGalleryStore = defineStore("gallery", () => {
|
|||||||
text: 'Watermark',
|
text: 'Watermark',
|
||||||
color: '#FFFFFF',
|
color: '#FFFFFF',
|
||||||
opacity: 0.8,
|
opacity: 0.8,
|
||||||
scale: 0.05, // 5% of height default
|
scale: 0.025, // Reduced from 0.05
|
||||||
manual_override: false,
|
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(() => {
|
const selectedImage = computed(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user