🎉 Add text stroke fills

This commit is contained in:
Elena Torro 2025-05-08 13:28:04 +02:00
parent b71b9edee7
commit eddabc0d68
3 changed files with 54 additions and 15 deletions

View file

@ -1,4 +1,4 @@
use skia_safe::{self as skia, Matrix, RRect, Rect}; use skia_safe::{self as skia, image, Matrix, RRect, Rect};
use crate::uuid::Uuid; use crate::uuid::Uuid;
use std::collections::{HashMap, HashSet, VecDeque}; use std::collections::{HashMap, HashSet, VecDeque};
@ -20,7 +20,7 @@ mod surfaces;
mod text; mod text;
mod tiles; mod tiles;
use crate::shapes::{modified_children_ids, Corners, Shape, StructureEntry, Type}; use crate::shapes::{modified_children_ids, Corners, Fill, Shape, StructureEntry, Type};
use gpu_state::GpuState; use gpu_state::GpuState;
use options::RenderOptions; use options::RenderOptions;
use surfaces::{SurfaceId, Surfaces}; use surfaces::{SurfaceId, Surfaces};
@ -362,7 +362,11 @@ impl RenderState {
text::render(self, &shape, &paragraphs, None, None); text::render(self, &shape, &paragraphs, None, None);
for stroke in shape.strokes().rev() { for stroke in shape.strokes().rev() {
let stroke_paints = shape.get_text_stroke_paint(&stroke); let mut image: Option<image::Image> = None;
if let Fill::Image(image_fill) = &stroke.fill {
image = self.images.get(&image_fill.id()).cloned();
}
let stroke_paints = shape.get_text_stroke_paint(&stroke, image.as_ref());
let stroke_paragraphs = text_content let stroke_paragraphs = text_content
.get_skia_stroke_paragraphs(&self.fonts.font_collection(), &stroke_paints); .get_skia_stroke_paragraphs(&self.fonts.font_collection(), &stroke_paints);
shadows::render_text_drop_shadows(self, &shape, &stroke_paragraphs, antialias); shadows::render_text_drop_shadows(self, &shape, &stroke_paragraphs, antialias);

View file

@ -1,3 +1,4 @@
use skia_safe::Image;
use skia_safe::{self as skia, paint::Paint}; use skia_safe::{self as skia, paint::Paint};
use crate::render::BlendMode; use crate::render::BlendMode;
@ -812,7 +813,37 @@ impl Shape {
!self.fills.is_empty() !self.fills.is_empty()
} }
pub fn get_text_stroke_paint(&self, stroke: &Stroke) -> Vec<Paint> { fn set_paint_fill(&self, paint: &mut Paint, fill: &Fill, image: Option<Image>) {
match fill {
Fill::Solid(SolidColor(color)) => {
paint.set_color(*color);
}
Fill::LinearGradient(gradient) => {
paint.set_shader(gradient.to_linear_shader(&self.selrect()));
}
Fill::RadialGradient(gradient) => {
paint.set_shader(gradient.to_radial_shader(&self.selrect()));
}
Fill::Image(image_fill) => {
if let Some(image) = image {
let position = (self.selrect().x(), self.selrect().y());
let sampling_options = skia::SamplingOptions::new(
skia::FilterMode::Linear,
skia::MipmapMode::Nearest,
);
let tile_modes = (skia::TileMode::Clamp, skia::TileMode::Clamp);
let mut matrix = skia::Matrix::default();
matrix.set_translate(position);
let shader = image.to_shader(tile_modes, sampling_options, &matrix);
paint.set_shader(shader);
paint.set_alpha(image_fill.opacity());
}
}
}
}
pub fn get_text_stroke_paint(&self, stroke: &Stroke, image: Option<&Image>) -> Vec<Paint> {
let mut paints = Vec::new(); let mut paints = Vec::new();
match stroke.kind { match stroke.kind {
@ -827,9 +858,9 @@ impl Shape {
paint.set_blend_mode(skia::BlendMode::SrcATop); paint.set_blend_mode(skia::BlendMode::SrcATop);
paint.set_anti_alias(true); paint.set_anti_alias(true);
paint.set_stroke_width(stroke.width * 2.0); paint.set_stroke_width(stroke.width * 2.0);
if let Fill::Solid(SolidColor(color)) = stroke.fill {
paint.set_color(color); self.set_paint_fill(&mut paint, &stroke.fill, image.cloned());
}
paints.push(paint); paints.push(paint);
} }
StrokeKind::CenterStroke => { StrokeKind::CenterStroke => {
@ -837,9 +868,9 @@ impl Shape {
paint.set_style(skia::PaintStyle::Stroke); paint.set_style(skia::PaintStyle::Stroke);
paint.set_anti_alias(true); paint.set_anti_alias(true);
paint.set_stroke_width(stroke.width); paint.set_stroke_width(stroke.width);
if let Fill::Solid(SolidColor(color)) = stroke.fill {
paint.set_color(color); self.set_paint_fill(&mut paint, &stroke.fill, image.cloned());
}
paints.push(paint); paints.push(paint);
} }
StrokeKind::OuterStroke => { StrokeKind::OuterStroke => {
@ -848,9 +879,9 @@ impl Shape {
paint.set_blend_mode(skia::BlendMode::DstOver); paint.set_blend_mode(skia::BlendMode::DstOver);
paint.set_anti_alias(true); paint.set_anti_alias(true);
paint.set_stroke_width(stroke.width * 2.0); paint.set_stroke_width(stroke.width * 2.0);
if let Fill::Solid(SolidColor(color)) = stroke.fill {
paint.set_color(color); self.set_paint_fill(&mut paint, &stroke.fill, image.cloned());
}
paints.push(paint); paints.push(paint);
let mut paint = skia::Paint::default(); let mut paint = skia::Paint::default();

View file

@ -41,7 +41,7 @@ impl Gradient {
self.offsets.extend(offsets); self.offsets.extend(offsets);
} }
fn to_linear_shader(&self, rect: &Rect) -> Option<skia::Shader> { pub fn to_linear_shader(&self, rect: &Rect) -> Option<skia::Shader> {
let start = ( let start = (
rect.left + self.start.0 * rect.width(), rect.left + self.start.0 * rect.width(),
rect.top + self.start.1 * rect.height(), rect.top + self.start.1 * rect.height(),
@ -60,7 +60,7 @@ impl Gradient {
) )
} }
fn to_radial_shader(&self, rect: &Rect) -> Option<skia::Shader> { pub fn to_radial_shader(&self, rect: &Rect) -> Option<skia::Shader> {
let center = skia::Point::new( let center = skia::Point::new(
rect.left + self.start.0 * rect.width(), rect.left + self.start.0 * rect.width(),
rect.top + self.start.1 * rect.height(), rect.top + self.start.1 * rect.height(),
@ -119,6 +119,10 @@ impl ImageFill {
pub fn id(&self) -> Uuid { pub fn id(&self) -> Uuid {
self.id self.id
} }
pub fn opacity(&self) -> u8 {
self.opacity
}
} }
#[derive(Debug, Clone, PartialEq, Copy)] #[derive(Debug, Clone, PartialEq, Copy)]