add black border
This commit is contained in:
@@ -35,15 +35,9 @@ fn generate_thumbnail(original_path: &Path) -> Option<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate
|
// Generate
|
||||||
// Use image reader to be faster? open is fine.
|
|
||||||
if let Ok(img) = image::open(original_path) {
|
if let Ok(img) = image::open(original_path) {
|
||||||
// Resize to height 200, preserve ratio
|
|
||||||
let thumb = img.thumbnail(u32::MAX, 200);
|
let thumb = img.thumbnail(u32::MAX, 200);
|
||||||
// Save as jpeg with quality 80
|
let _file = fs::File::create(&thumb_path).ok()?;
|
||||||
let mut file = fs::File::create(&thumb_path).ok()?;
|
|
||||||
// thumb.write_to(&mut file, image::ImageOutputFormat::Jpeg(80)).ok()?;
|
|
||||||
// write_to might be slow due to encoding, but parallel execution helps.
|
|
||||||
// save directly
|
|
||||||
thumb.save_with_format(&thumb_path, image::ImageFormat::Jpeg).ok()?;
|
thumb.save_with_format(&thumb_path, image::ImageFormat::Jpeg).ok()?;
|
||||||
|
|
||||||
return Some(thumb_path.to_string_lossy().to_string());
|
return Some(thumb_path.to_string_lossy().to_string());
|
||||||
@@ -165,30 +159,28 @@ async fn export_batch(images: Vec<ExportImageTask>, watermark: WatermarkSettings
|
|||||||
|
|
||||||
// 2. Measure Text
|
// 2. Measure Text
|
||||||
let scaled_font = PxScale::from(scale_px);
|
let scaled_font = PxScale::from(scale_px);
|
||||||
let (t_width, t_height) = imageproc::drawing::text_size(scaled_font, &font, &watermark.text);
|
let (t_width, _t_height) = imageproc::drawing::text_size(scaled_font, &font, &watermark.text);
|
||||||
|
|
||||||
// 3. Ensure it fits width (Padding 5%)
|
// 3. Ensure it fits width (Padding 5%)
|
||||||
let max_width = (width as f32 * 0.95) as u32;
|
let max_width = (width as f32 * 0.95) as u32;
|
||||||
if t_width > max_width {
|
if t_width > max_width {
|
||||||
let ratio = max_width as f32 / t_width as f32;
|
let ratio = max_width as f32 / t_width as f32;
|
||||||
scale_px *= ratio;
|
scale_px *= ratio;
|
||||||
// Re-measure isn't strictly necessary for center calc if we assume linear scaling,
|
|
||||||
// but let's be safe for variable width fonts? Actually text_size scales linearly.
|
|
||||||
}
|
}
|
||||||
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 (Center based)
|
// 4. Determine Position (Center based)
|
||||||
let (mut pos_x_pct, mut pos_y_pct) = if watermark.manual_override {
|
let (pos_x_pct, 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.96), // Default fallback lowered
|
Err(_) => (0.5, 0.96),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate top-left coordinate for draw_text_mut (it expects top-left, not center)
|
// Calculate top-left coordinate for draw_text_mut
|
||||||
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;
|
||||||
|
|
||||||
@@ -203,7 +195,21 @@ async fn export_batch(images: Vec<ExportImageTask>, watermark: WatermarkSettings
|
|||||||
x = x.clamp(padding, max_x);
|
x = x.clamp(padding, max_x);
|
||||||
y = y.clamp(padding, max_y);
|
y = y.clamp(padding, max_y);
|
||||||
|
|
||||||
// 6. Draw
|
// 6. Draw Stroke (Simple 4-direction offset for black outline)
|
||||||
|
let stroke_color = image::Rgba([0, 0, 0, text_color[3]]);
|
||||||
|
for offset in [(-1, -1), (-1, 1), (1, -1), (1, 1)] {
|
||||||
|
draw_text_mut(
|
||||||
|
&mut base_img,
|
||||||
|
stroke_color,
|
||||||
|
x + offset.0,
|
||||||
|
y + offset.1,
|
||||||
|
final_scale,
|
||||||
|
&font,
|
||||||
|
&watermark.text,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Draw Main Text
|
||||||
draw_text_mut(
|
draw_text_mut(
|
||||||
&mut base_img,
|
&mut base_img,
|
||||||
text_color,
|
text_color,
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
"title": "watermark-wizard",
|
"title": "watermark-wizard",
|
||||||
"width": 800,
|
"width": 1650,
|
||||||
"height": 600
|
"height": 1000
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
|
|||||||
@@ -160,6 +160,10 @@ const onMouseLeave = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
|
text-shadow:
|
||||||
|
-1px -1px 0 #000,
|
||||||
|
1px -1px 0 #000,
|
||||||
|
-1px 1px 0 #000,
|
||||||
|
1px 1px 0 #000;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user