From d2aa2311968eaf39daeb2800a4940e633c436a5a Mon Sep 17 00:00:00 2001 From: YuraIz <7516890@gmail.com> Date: Mon, 27 Mar 2023 00:17:26 +0300 Subject: [PATCH] stickers: Add vector path --- data/resources/style-dark.css | 4 ++ data/resources/style.css | 4 ++ src/components/mod.rs | 2 + src/components/sticker.rs | 12 +++-- src/components/vector_path.rs | 95 +++++++++++++++++++++++++++++++++++ src/utils.rs | 11 +++- 6 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 src/components/vector_path.rs diff --git a/data/resources/style-dark.css b/data/resources/style-dark.css index cfe905a26..109cebb0d 100644 --- a/data/resources/style-dark.css +++ b/data/resources/style-dark.css @@ -7,6 +7,10 @@ messagesticker.needs-repainting > overlay > widget > widget { filter: invert(1); } +vectorpath { + color: alpha(#404040, 0.8); +} + .chat-list row .unread-count-muted { background-color: @dark_2; } diff --git a/data/resources/style.css b/data/resources/style.css index 76ad5de2f..02edc31f9 100644 --- a/data/resources/style.css +++ b/data/resources/style.css @@ -318,6 +318,10 @@ messagesticker { border-spacing: 6px; } +vectorpath { + color: alpha(black, 0.2); +} + .event-row { font-size: smaller; font-weight: bold; diff --git a/src/components/mod.rs b/src/components/mod.rs index f5efdd186..17fc12143 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -2,8 +2,10 @@ mod avatar; mod message_entry; mod snow; mod sticker; +mod vector_path; pub(crate) use self::avatar::Avatar; pub(crate) use self::message_entry::MessageEntry; pub(crate) use self::snow::Snow; pub(crate) use self::sticker::Sticker; +use self::vector_path::VectorPath; diff --git a/src/components/sticker.rs b/src/components/sticker.rs index a181c450d..6dac94ab8 100644 --- a/src/components/sticker.rs +++ b/src/components/sticker.rs @@ -6,6 +6,7 @@ use gtk::{gio, glib}; use tdlib::enums::StickerFormat; use tdlib::types::Sticker as TdSticker; +use super::VectorPath; use crate::session::Session; use crate::utils::{decode_image_from_path, spawn}; @@ -52,11 +53,15 @@ mod imp { } impl WidgetImpl for Sticker { - fn measure(&self, orientation: gtk::Orientation, _for_size: i32) -> (i32, i32, i32, i32) { + fn measure(&self, orientation: gtk::Orientation, for_size: i32) -> (i32, i32, i32, i32) { let size = self.longer_side_size.get(); let aspect_ratio = self.aspect_ratio.get(); - let min_size = 1; + let min_size = if let Some(child) = &*self.child.borrow_mut() { + child.measure(orientation, for_size).0 + } else { + 1 + }; let size = if let gtk::Orientation::Horizontal = orientation { if aspect_ratio >= 1.0 { @@ -103,8 +108,7 @@ impl Sticker { return; } - // TODO: draw sticker outline with cairo - self.set_child(None); + self.set_child(Some(VectorPath::new(&sticker.outline).upcast())); let aspect_ratio = sticker.width as f64 / sticker.height as f64; imp.aspect_ratio.set(aspect_ratio); diff --git a/src/components/vector_path.rs b/src/components/vector_path.rs new file mode 100644 index 000000000..9de0f3d9c --- /dev/null +++ b/src/components/vector_path.rs @@ -0,0 +1,95 @@ +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use gtk::{glib, graphene, gsk}; +use tdlib::types::ClosedVectorPath; + +use crate::utils::color_matrix_from_color; + +mod imp { + use super::*; + use std::cell::RefCell; + + #[derive(Default)] + pub(crate) struct VectorPath { + pub(super) node: RefCell>, + } + + #[glib::object_subclass] + impl ObjectSubclass for VectorPath { + const NAME: &'static str = "ComponentsVectorPath"; + type Type = super::VectorPath; + type ParentType = gtk::Widget; + + fn class_init(klass: &mut Self::Class) { + klass.set_css_name("vectorpath"); + } + } + + impl ObjectImpl for VectorPath {} + + impl WidgetImpl for VectorPath { + fn snapshot(&self, snapshot: >k::Snapshot) { + let widget = self.obj(); + + let factor = (widget.width() as f32).max(widget.height() as f32) / 512.0; + snapshot.scale(factor, factor); + + let (color_matrix, color_offset) = color_matrix_from_color(widget.color()); + snapshot.push_color_matrix(&color_matrix, &color_offset); + if let Some(node) = &*self.node.borrow() { + snapshot.append_node(node); + } + snapshot.pop(); + } + } +} + +glib::wrapper! { + pub(crate) struct VectorPath(ObjectSubclass) + @extends gtk::Widget; +} + +impl VectorPath { + pub fn new(outline: &[ClosedVectorPath]) -> Self { + let obj: Self = glib::Object::new(); + obj.imp().node.replace(path_node(outline)); + obj + } +} + +fn path_node(outline: &[ClosedVectorPath]) -> Option { + use tdlib::enums::VectorPathCommand::{CubicBezierCurve, Line}; + use tdlib::types::VectorPathCommandCubicBezierCurve as Curve; + + let snapshot = gtk::Snapshot::new(); + let context = snapshot.append_cairo(&graphene::Rect::new(0.0, 0.0, 512.0, 512.0)); + + for closed_path in outline { + let e = match closed_path.commands.iter().last().unwrap() { + Line(line) => &line.end_point, + CubicBezierCurve(curve) => &curve.end_point, + }; + context.move_to(e.x, e.y); + + for command in &closed_path.commands { + match command { + Line(line) => { + let e = &line.end_point; + context.line_to(e.x, e.y); + } + CubicBezierCurve(curve) => { + let Curve { + start_control_point: sc, + end_control_point: ec, + end_point: e, + } = curve; + + context.curve_to(sc.x, sc.y, ec.x, ec.y, e.x, e.y); + } + } + } + } + _ = context.fill(); + + snapshot.to_node() +} diff --git a/src/utils.rs b/src/utils.rs index 70ab1a4da..04c9b2140 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,5 @@ use gettextrs::gettext; -use gtk::{gdk, glib}; +use gtk::{gdk, glib, graphene}; use image::io::Reader as ImageReader; use locale_config::Locale; use once_cell::sync::Lazy; @@ -278,3 +278,12 @@ pub(crate) fn decode_image_from_path(path: &str) -> Result (graphene::Matrix, graphene::Vec4) { + let color_offset = graphene::Vec4::new(color.red(), color.green(), color.blue(), 0.0); + let mut matrix = [0.0; 16]; + matrix[15] = color.alpha(); + let color_matrix = graphene::Matrix::from_float(matrix); + + (color_matrix, color_offset) +}