Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vector path for stickers #470

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions data/resources/style-dark.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 4 additions & 0 deletions data/resources/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ messagesticker {
border-spacing: 6px;
}

vectorpath {
color: alpha(black, 0.2);
}

.event-row {
font-size: smaller;
font-weight: bold;
Expand Down
2 changes: 2 additions & 0 deletions src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
12 changes: 8 additions & 4 deletions src/components/sticker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
95 changes: 95 additions & 0 deletions src/components/vector_path.rs
Original file line number Diff line number Diff line change
@@ -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<Option<gsk::RenderNode>>,
}

#[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: &gtk::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<imp::VectorPath>)
@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<gsk::RenderNode> {
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()
}
11 changes: 10 additions & 1 deletion src/utils.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -278,3 +278,12 @@ pub(crate) fn decode_image_from_path(path: &str) -> Result<gdk::MemoryTexture, D

Ok(texture)
}

pub fn color_matrix_from_color(color: gdk::RGBA) -> (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)
}