seperate color
This commit is contained in:
@@ -109,6 +109,7 @@ struct ExportImageTask {
|
|||||||
manual_position: Option<ManualPosition>,
|
manual_position: Option<ManualPosition>,
|
||||||
scale: Option<f64>,
|
scale: Option<f64>,
|
||||||
opacity: Option<f64>,
|
opacity: Option<f64>,
|
||||||
|
color: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
@@ -141,9 +142,8 @@ fn parse_hex_color(hex: &str) -> image::Rgba<u8> {
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn export_batch(images: Vec<ExportImageTask>, watermark: WatermarkSettings, output_dir: String) -> Result<String, String> {
|
async fn export_batch(images: Vec<ExportImageTask>, watermark: WatermarkSettings, output_dir: String) -> Result<String, String> {
|
||||||
let font = FontRef::try_from_slice(FONT_DATA).map_err(|e| format!("Font error: {}", e))?;
|
let font = FontRef::try_from_slice(FONT_DATA).map_err(|e| format!("Font error: {}", e))?;
|
||||||
let base_color = parse_hex_color(&watermark.color);
|
|
||||||
|
|
||||||
// Note: opacity and final text color are now calculated per-task
|
// Note: Settings are now resolved per-task
|
||||||
|
|
||||||
let results: Vec<Result<(), String>> = images.par_iter().map(|task| {
|
let results: Vec<Result<(), String>> = images.par_iter().map(|task| {
|
||||||
let input_path = Path::new(&task.path);
|
let input_path = Path::new(&task.path);
|
||||||
@@ -156,8 +156,10 @@ async fn export_batch(images: Vec<ExportImageTask>, watermark: WatermarkSettings
|
|||||||
// Determine effective settings (Task > Global)
|
// Determine effective settings (Task > Global)
|
||||||
let eff_scale = task.scale.unwrap_or(watermark.scale);
|
let eff_scale = task.scale.unwrap_or(watermark.scale);
|
||||||
let eff_opacity = task.opacity.unwrap_or(watermark.opacity);
|
let eff_opacity = task.opacity.unwrap_or(watermark.opacity);
|
||||||
|
let eff_color_hex = task.color.as_ref().unwrap_or(&watermark.color);
|
||||||
|
|
||||||
// Calculate final color with effective opacity
|
// Calculate final color
|
||||||
|
let base_color = parse_hex_color(eff_color_hex);
|
||||||
let alpha = (eff_opacity * 255.0) as u8;
|
let alpha = (eff_opacity * 255.0) as u8;
|
||||||
let text_color = image::Rgba([base_color[0], base_color[1], base_color[2], alpha]);
|
let text_color = image::Rgba([base_color[0], base_color[1], base_color[2], alpha]);
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ async function exportBatch() {
|
|||||||
path: img.path,
|
path: img.path,
|
||||||
manual_position: img.manualPosition || null,
|
manual_position: img.manualPosition || null,
|
||||||
scale: img.scale || null,
|
scale: img.scale || null,
|
||||||
opacity: img.opacity || null
|
opacity: img.opacity || null,
|
||||||
|
color: img.color || null
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Pass dummy globals for rust struct compatibility
|
// Pass dummy globals for rust struct compatibility
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ const position = computed(() => {
|
|||||||
|
|
||||||
const effectiveScale = computed(() => store.selectedImage?.scale ?? store.watermarkSettings.scale);
|
const effectiveScale = computed(() => store.selectedImage?.scale ?? store.watermarkSettings.scale);
|
||||||
const effectiveOpacity = computed(() => store.selectedImage?.opacity ?? store.watermarkSettings.opacity);
|
const effectiveOpacity = computed(() => store.selectedImage?.opacity ?? store.watermarkSettings.opacity);
|
||||||
|
const effectiveColor = computed(() => store.selectedImage?.color ?? store.watermarkSettings.color);
|
||||||
|
|
||||||
const onMouseDown = (e: MouseEvent) => {
|
const onMouseDown = (e: MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -159,7 +160,7 @@ const onMouseLeave = () => {
|
|||||||
top: (position.y * 100) + '%',
|
top: (position.y * 100) + '%',
|
||||||
transform: 'translate(-50%, -50%)',
|
transform: 'translate(-50%, -50%)',
|
||||||
opacity: effectiveOpacity,
|
opacity: effectiveOpacity,
|
||||||
color: store.watermarkSettings.color,
|
color: effectiveColor,
|
||||||
/* Scale based on HEIGHT of the IMAGE */
|
/* Scale based on HEIGHT of the IMAGE */
|
||||||
fontSize: (imageRect.height * effectiveScale) + 'px',
|
fontSize: (imageRect.height * effectiveScale) + 'px',
|
||||||
height: '0px',
|
height: '0px',
|
||||||
|
|||||||
@@ -28,8 +28,19 @@ const currentOpacity = computed({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const currentColor = computed({
|
||||||
|
get: () => store.selectedImage?.color ?? store.watermarkSettings.color,
|
||||||
|
set: (val) => {
|
||||||
|
if (store.selectedIndex >= 0) {
|
||||||
|
store.setImageSetting(store.selectedIndex, 'color', val);
|
||||||
|
} else {
|
||||||
|
store.updateWatermarkSettings({ color: val });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const applyAll = () => {
|
const applyAll = () => {
|
||||||
if (confirm("Apply current size and opacity settings to ALL images? This will reset individual adjustments.")) {
|
if (confirm("Apply current settings (Size, Opacity, Color) to ALL images?")) {
|
||||||
store.applySettingsToAll();
|
store.applySettingsToAll();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -44,7 +55,7 @@ const applyAll = () => {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@click="applyAll"
|
@click="applyAll"
|
||||||
title="Apply Size & Opacity to All Images"
|
title="Apply Settings to All Images"
|
||||||
class="bg-gray-700 hover:bg-gray-600 p-1.5 rounded text-xs text-blue-300 transition-colors flex items-center gap-1"
|
class="bg-gray-700 hover:bg-gray-600 p-1.5 rounded text-xs text-blue-300 transition-colors flex items-center gap-1"
|
||||||
>
|
>
|
||||||
<Copy class="w-3 h-3" /> All
|
<Copy class="w-3 h-3" /> All
|
||||||
@@ -72,10 +83,10 @@ const applyAll = () => {
|
|||||||
<Palette class="w-4 h-4 text-gray-400" />
|
<Palette class="w-4 h-4 text-gray-400" />
|
||||||
<input
|
<input
|
||||||
type="color"
|
type="color"
|
||||||
v-model="store.watermarkSettings.color"
|
v-model="currentColor"
|
||||||
class="w-8 h-8 rounded cursor-pointer bg-transparent border-none p-0"
|
class="w-8 h-8 rounded cursor-pointer bg-transparent border-none p-0"
|
||||||
/>
|
/>
|
||||||
<span class="text-xs text-gray-300 font-mono">{{ store.watermarkSettings.color }}</span>
|
<span class="text-xs text-gray-300 font-mono">{{ currentColor }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export interface ImageItem {
|
|||||||
manualPosition?: { x: number; y: number };
|
manualPosition?: { x: number; y: number };
|
||||||
scale?: number;
|
scale?: number;
|
||||||
opacity?: number;
|
opacity?: number;
|
||||||
|
color?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WatermarkSettings {
|
export interface WatermarkSettings {
|
||||||
@@ -55,8 +56,9 @@ export const useGalleryStore = defineStore("gallery", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setImageSetting(index: number, setting: 'scale' | 'opacity', value: number) {
|
function setImageSetting(index: number, setting: 'scale' | 'opacity' | 'color', value: number | string) {
|
||||||
if (images.value[index]) {
|
if (images.value[index]) {
|
||||||
|
// @ts-ignore
|
||||||
images.value[index][setting] = value;
|
images.value[index][setting] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,16 +71,18 @@ export const useGalleryStore = defineStore("gallery", () => {
|
|||||||
|
|
||||||
const newScale = current.scale ?? watermarkSettings.value.scale;
|
const newScale = current.scale ?? watermarkSettings.value.scale;
|
||||||
const newOpacity = current.opacity ?? watermarkSettings.value.opacity;
|
const newOpacity = current.opacity ?? watermarkSettings.value.opacity;
|
||||||
|
const newColor = current.color ?? watermarkSettings.value.color;
|
||||||
|
|
||||||
// 1. Update Global
|
// 1. Update Global
|
||||||
watermarkSettings.value.scale = newScale;
|
watermarkSettings.value.scale = newScale;
|
||||||
watermarkSettings.value.opacity = newOpacity;
|
watermarkSettings.value.opacity = newOpacity;
|
||||||
|
watermarkSettings.value.color = newColor;
|
||||||
|
|
||||||
// 2. Clear overrides for Scale and Opacity on ALL images
|
// 2. Clear overrides on ALL images
|
||||||
// (We keep manualPosition because position is usually unique per image content)
|
|
||||||
images.value.forEach(img => {
|
images.value.forEach(img => {
|
||||||
img.scale = undefined;
|
img.scale = undefined;
|
||||||
img.opacity = undefined;
|
img.opacity = undefined;
|
||||||
|
img.color = undefined;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user