mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Save and restore graphics state for every frame (#4496)
Co-authored-by: Laurenz <laurmaedje@gmail.com>
This commit is contained in:
parent
17ee3df1ba
commit
ac322e342b
@ -92,7 +92,7 @@ pub struct Builder<'a, R = ()> {
|
|||||||
state: State,
|
state: State,
|
||||||
/// Stack of saved graphic states.
|
/// Stack of saved graphic states.
|
||||||
saves: Vec<State>,
|
saves: Vec<State>,
|
||||||
/// Wheter any stroke or fill was not totally opaque.
|
/// Whether any stroke or fill was not totally opaque.
|
||||||
uses_opacities: bool,
|
uses_opacities: bool,
|
||||||
/// All clickable links that are present in this content.
|
/// All clickable links that are present in this content.
|
||||||
links: Vec<(Destination, Rect)>,
|
links: Vec<(Destination, Rect)>,
|
||||||
@ -129,7 +129,7 @@ struct State {
|
|||||||
/// The color space of the current fill paint.
|
/// The color space of the current fill paint.
|
||||||
fill_space: Option<Name<'static>>,
|
fill_space: Option<Name<'static>>,
|
||||||
/// The current external graphic state.
|
/// The current external graphic state.
|
||||||
external_graphics_state: Option<ExtGState>,
|
external_graphics_state: ExtGState,
|
||||||
/// The current stroke paint.
|
/// The current stroke paint.
|
||||||
stroke: Option<FixedStroke>,
|
stroke: Option<FixedStroke>,
|
||||||
/// The color space of the current stroke paint.
|
/// The color space of the current stroke paint.
|
||||||
@ -148,7 +148,7 @@ impl State {
|
|||||||
font: None,
|
font: None,
|
||||||
fill: None,
|
fill: None,
|
||||||
fill_space: None,
|
fill_space: None,
|
||||||
external_graphics_state: None,
|
external_graphics_state: ExtGState::default(),
|
||||||
stroke: None,
|
stroke: None,
|
||||||
stroke_space: None,
|
stroke_space: None,
|
||||||
text_rendering_mode: TextRenderingMode::Fill,
|
text_rendering_mode: TextRenderingMode::Fill,
|
||||||
@ -191,12 +191,13 @@ impl Builder<'_, ()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_external_graphics_state(&mut self, graphics_state: &ExtGState) {
|
fn set_external_graphics_state(&mut self, graphics_state: &ExtGState) {
|
||||||
let current_state = self.state.external_graphics_state.as_ref();
|
let current_state = &self.state.external_graphics_state;
|
||||||
if current_state != Some(graphics_state) {
|
if current_state != graphics_state {
|
||||||
let index = self.resources.ext_gs.insert(*graphics_state);
|
let index = self.resources.ext_gs.insert(*graphics_state);
|
||||||
let name = eco_format!("Gs{index}");
|
let name = eco_format!("Gs{index}");
|
||||||
self.content.set_parameters(Name(name.as_bytes()));
|
self.content.set_parameters(Name(name.as_bytes()));
|
||||||
|
|
||||||
|
self.state.external_graphics_state = *graphics_state;
|
||||||
if graphics_state.uses_opacities() {
|
if graphics_state.uses_opacities() {
|
||||||
self.uses_opacities = true;
|
self.uses_opacities = true;
|
||||||
}
|
}
|
||||||
@ -204,29 +205,27 @@ impl Builder<'_, ()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_opacities(&mut self, stroke: Option<&FixedStroke>, fill: Option<&Paint>) {
|
fn set_opacities(&mut self, stroke: Option<&FixedStroke>, fill: Option<&Paint>) {
|
||||||
let stroke_opacity = stroke
|
let get_opacity = |paint: &Paint| {
|
||||||
.map(|stroke| {
|
let color = match paint {
|
||||||
let color = match &stroke.paint {
|
Paint::Solid(color) => *color,
|
||||||
Paint::Solid(color) => *color,
|
Paint::Gradient(_) | Paint::Pattern(_) => return 255,
|
||||||
Paint::Gradient(_) | Paint::Pattern(_) => return 255,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
color.alpha().map_or(255, |v| (v * 255.0).round() as u8)
|
color.alpha().map_or(255, |v| (v * 255.0).round() as u8)
|
||||||
})
|
};
|
||||||
.unwrap_or(255);
|
|
||||||
let fill_opacity = fill
|
|
||||||
.map(|paint| {
|
|
||||||
let color = match paint {
|
|
||||||
Paint::Solid(color) => *color,
|
|
||||||
Paint::Gradient(_) | Paint::Pattern(_) => return 255,
|
|
||||||
};
|
|
||||||
|
|
||||||
color.alpha().map_or(255, |v| (v * 255.0).round() as u8)
|
let stroke_opacity = stroke.map_or(255, |stroke| get_opacity(&stroke.paint));
|
||||||
})
|
let fill_opacity = fill.map_or(255, get_opacity);
|
||||||
.unwrap_or(255);
|
|
||||||
self.set_external_graphics_state(&ExtGState { stroke_opacity, fill_opacity });
|
self.set_external_graphics_state(&ExtGState { stroke_opacity, fill_opacity });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reset_opacities(&mut self) {
|
||||||
|
self.set_external_graphics_state(&ExtGState {
|
||||||
|
stroke_opacity: 255,
|
||||||
|
fill_opacity: 255,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn transform(&mut self, transform: Transform) {
|
pub fn transform(&mut self, transform: Transform) {
|
||||||
let Transform { sx, ky, kx, sy, tx, ty } = transform;
|
let Transform { sx, ky, kx, sy, tx, ty } = transform;
|
||||||
self.state.transform = self.state.transform.pre_concat(transform);
|
self.state.transform = self.state.transform.pre_concat(transform);
|
||||||
@ -542,6 +541,8 @@ fn write_color_glyphs(ctx: &mut Builder, pos: Point, text: TextItemView) {
|
|||||||
|
|
||||||
let mut last_font = None;
|
let mut last_font = None;
|
||||||
|
|
||||||
|
ctx.reset_opacities();
|
||||||
|
|
||||||
ctx.content.begin_text();
|
ctx.content.begin_text();
|
||||||
ctx.content.set_text_matrix([1.0, 0.0, 0.0, -1.0, x, y]);
|
ctx.content.set_text_matrix([1.0, 0.0, 0.0, -1.0, x, y]);
|
||||||
// So that the next call to ctx.set_font() will change the font to one that
|
// So that the next call to ctx.set_font() will change the font to one that
|
||||||
@ -671,6 +672,8 @@ fn write_image(ctx: &mut Builder, x: f32, y: f32, image: &Image, size: Size) {
|
|||||||
image
|
image
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ctx.reset_opacities();
|
||||||
|
|
||||||
let name = eco_format!("Im{index}");
|
let name = eco_format!("Im{index}");
|
||||||
let w = size.x.to_f32();
|
let w = size.x.to_f32();
|
||||||
let h = size.y.to_f32();
|
let h = size.y.to_f32();
|
||||||
|
BIN
tests/ref/issue-4361-transparency-leak.png
Normal file
BIN
tests/ref/issue-4361-transparency-leak.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
@ -333,3 +333,10 @@
|
|||||||
--- issue-color-mix-luma ---
|
--- issue-color-mix-luma ---
|
||||||
// When mixing luma colors, we accidentally used the wrong component.
|
// When mixing luma colors, we accidentally used the wrong component.
|
||||||
#rect(fill: gradient.linear(black, silver, space: luma))
|
#rect(fill: gradient.linear(black, silver, space: luma))
|
||||||
|
|
||||||
|
--- issue-4361-transparency-leak ---
|
||||||
|
// Ensure that transparency doesn't leak from shapes to images in PDF. The PNG
|
||||||
|
// test doesn't validate it, but at least we can discover regressions on the PDF
|
||||||
|
// output with a PDF comparison script.
|
||||||
|
#rect(fill: red.transparentize(50%))
|
||||||
|
#image("/assets/images/tiger.jpg", width: 45pt)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user