///|
/// 随机点阵水印:按 `DotConfig` 的归一化网格 + PRNG 布局,在全图散布深色点。
///
/// - 落点网格使用**归一化坐标**(单元边长 = 图宽 8%),点位置按比例跟随
/// 图像尺寸——因此缩放(resize)后提取仍能命中,鲁棒性矩阵实测通过。
/// - 每个单元由 `seed` 驱动的确定性 PRNG 决定是否落点(`density` 为落点
/// 单元概率)及点在单元内的偏移比例(叠加 `grid_offset_x / grid_offset_y`
/// 像素偏移,非零偏移在缩放场景会有轻微漂移,建议默认 0)。
/// - 点颜色固定为深色(`0x0000_00FF`),半透明时由 `opacity` 控制;
/// 同一配置(尤其同一 `seed`)嵌入结果完全一致,可被 `extract_dots` 复现。
pub fn RgbaImage::embed_dots(self : RgbaImage, cfg : DotConfig) -> RgbaImage {
let cell_ratio = 0.08 // 单元边长占图宽的比例(≈ 原 8px @100px 的观感)
let alpha = clamp_opacity(cfg.opacity)
let threshold = (cfg.density * 1000.0).to_int()
let mut st = seed_to_state(cfg.seed)
let cols = (1.0 / cell_ratio).ceil().to_int()
let rows = cols
for cy in 0.. Double {
let thr = match brightness_threshold {
Some(v) => v
None => 128
}
let cell_ratio = 0.08 // 与 embed_dots 保持一致
let mut st = seed_to_state(cfg.seed)
let cols = (1.0 / cell_ratio).ceil().to_int()
let rows = cols
let threshold = (cfg.density * 1000.0).to_int()
let mut hits = 0
let mut total = 0
for cy in 0..> 24) & 0xFF
let g2 = (p >> 16) & 0xFF
let b2 = (p >> 8) & 0xFF
if (r2 + g2 + b2) / 3 < thr {
any = true
}
}
}
}
// 与 embed 对齐:embed 对越界格不画点,提取时也跳过(避免分母虚增)
if checked {
total += 1
if any {
hits += 1
}
}
}
}
}
if total == 0 {
0.0
} else {
(hits * 1000 / total).to_double() / 1000.0
}
}