Text: transparent text fills are faded once, not twice

A run's solid fill with a:alpha was resolved into the glyph colour's alpha and also kept as the
run's fill opacity, and the renderer multiplied both: text at 60 % transparency drew at 16 %
opacity instead of 40 %. The colour now carries the transparency alone. PowerPoint's export of
"Transparent 60% text" (red) is a light red that now matches.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Brandon ThomasandClaude Opus 5.5 committed 2026-10-13 06:48:43 -04:00
1 parent 9e2f7b96dc
commit ef313b91b0
2 files changed
+17 -2

No files matched your search

+3 -2
View File
@@ -54,7 +54,7 @@ pub struct GlyphRun {
pub para: usize,
pub chars: (usize, usize),
pub link: bool,
/// Alpha of the fill (0–1).
/// Opacity of the fill on top of `color`'s own alpha (0 for no fill).
pub alpha: f64,
pub gradient: Option<deckcraft_model::style::Gradient>,
/// Shadow and glow behind the glyphs (`a:rPr/a:effectLst`).
@@ -269,7 +269,8 @@ fn make_style(ctx: &Ctx, r: &RunProps, scale: f64, prompt: Option<Rgba>) -> Styl
let fake_italic = italic && !face.italic;
let size = (r.size.unwrap_or(18.0) * scale).clamp(0.5, 4000.0);
let (mut color, alpha, gradient) = match &r.fill {
Some(Fill::Solid { color }) => (ctx.color(color, None), color.alpha(), None),
// The resolved colour carries the fill's transparency (`a:alpha`): it is applied once.
Some(Fill::Solid { color }) => (ctx.color(color, None), 1.0, None),
Some(Fill::Gradient(g)) => {
// Colours resolved here (theme, colour map), so the renderer needs no colour scheme.
let mut g = g.clone();
+14
View File
@@ -871,3 +871,17 @@ fn capitals_keep_characters_whose_capital_is_a_sequence() {
assert_eq!(r.glyphs[1].0, face.glyph_for('ß'), "ß is not drawn as S");
}
}
#[test]
fn transparent_text_is_faded_once() {
let (p, mut sh) = setup("Faded", 2000.0, 300.0);
let run = &mut sh.text.as_mut().unwrap().paragraphs[0].runs[0];
run.props.fill = Some(deckcraft_model::Fill::solid(
deckcraft_model::ColorRef::rgb(deckcraft_color::Rgba::rgb(255, 0, 0)).with(deckcraft_color::ColorTransform::Alpha(40000)),
));
let l = lay(&p, &sh, 2000.0, 300.0);
let r = l.runs.iter().find(|r| !r.cells.is_empty()).unwrap();
// 60 % transparency: 40 % opaque in all, not 40 % of 40 %.
let opacity = r.color.a as f64 / 255.0 * r.alpha;
assert!((opacity - 0.4).abs() < 0.01, "{opacity}");
}