portal: use dedicated text-rendering code
This commit is contained in:
parent
a9aad0c613
commit
d99444bd3c
3 changed files with 125 additions and 29 deletions
|
|
@ -2,6 +2,7 @@ mod ptl_display;
|
||||||
mod ptl_remote_desktop;
|
mod ptl_remote_desktop;
|
||||||
mod ptl_render_ctx;
|
mod ptl_render_ctx;
|
||||||
mod ptl_screencast;
|
mod ptl_screencast;
|
||||||
|
mod ptl_text;
|
||||||
mod ptr_gui;
|
mod ptr_gui;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
|
|
|
||||||
101
src/portal/ptl_text.rs
Normal file
101
src/portal/ptl_text.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
format::ARGB8888,
|
||||||
|
gfx_api::{GfxContext, GfxTexture},
|
||||||
|
pango::{
|
||||||
|
consts::{CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE},
|
||||||
|
CairoContext, CairoImageSurface, PangoCairoContext, PangoFontDescription, PangoLayout,
|
||||||
|
},
|
||||||
|
rect::Rect,
|
||||||
|
theme::Color,
|
||||||
|
},
|
||||||
|
std::{ops::Neg, rc::Rc, sync::Arc},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Data {
|
||||||
|
image: Rc<CairoImageSurface>,
|
||||||
|
cctx: Rc<CairoContext>,
|
||||||
|
_pctx: Rc<PangoCairoContext>,
|
||||||
|
_fd: PangoFontDescription,
|
||||||
|
layout: PangoLayout,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_data(font: &str, width: i32, height: i32, scale: Option<f64>) -> Option<Data> {
|
||||||
|
let image = CairoImageSurface::new_image_surface(CAIRO_FORMAT_ARGB32, width, height).ok()?;
|
||||||
|
let cctx = image.create_context().ok()?;
|
||||||
|
let pctx = cctx.create_pango_context().ok()?;
|
||||||
|
let mut fd = PangoFontDescription::from_string(font);
|
||||||
|
if let Some(scale) = scale {
|
||||||
|
fd.set_size((fd.size() as f64 * scale).round() as _);
|
||||||
|
}
|
||||||
|
let layout = pctx.create_layout().ok()?;
|
||||||
|
layout.set_font_description(&fd);
|
||||||
|
Some(Data {
|
||||||
|
image,
|
||||||
|
cctx,
|
||||||
|
_pctx: pctx,
|
||||||
|
_fd: fd,
|
||||||
|
layout,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn measure(font: &str, text: &str, scale: Option<f64>, full: bool) -> Option<TextMeasurement> {
|
||||||
|
let data = create_data(font, 1, 1, scale)?;
|
||||||
|
data.layout.set_text(text);
|
||||||
|
let mut res = TextMeasurement::default();
|
||||||
|
res.ink_rect = data.layout.inc_pixel_rect();
|
||||||
|
if full {
|
||||||
|
res.logical_rect = data.layout.logical_pixel_rect();
|
||||||
|
res.baseline = data.layout.pixel_baseline();
|
||||||
|
}
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
|
pub struct TextMeasurement {
|
||||||
|
pub ink_rect: Rect,
|
||||||
|
pub logical_rect: Rect,
|
||||||
|
pub baseline: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(
|
||||||
|
ctx: &Rc<dyn GfxContext>,
|
||||||
|
height: Option<i32>,
|
||||||
|
font: &Arc<String>,
|
||||||
|
text: &str,
|
||||||
|
color: Color,
|
||||||
|
scale: Option<f64>,
|
||||||
|
include_measurements: bool,
|
||||||
|
) -> Option<(Rc<dyn GfxTexture>, TextMeasurement)> {
|
||||||
|
let measurement = measure(font, text, scale, include_measurements)?;
|
||||||
|
let y = match height {
|
||||||
|
Some(_) => None,
|
||||||
|
_ => Some(measurement.ink_rect.y1().neg()),
|
||||||
|
};
|
||||||
|
let x = measurement.ink_rect.x1().neg();
|
||||||
|
let width = measurement.ink_rect.width();
|
||||||
|
let height = height.unwrap_or(measurement.ink_rect.height());
|
||||||
|
let data = create_data(font, width, height, scale)?;
|
||||||
|
data.layout.set_text(text);
|
||||||
|
let font_height = data.layout.pixel_size().1;
|
||||||
|
data.cctx.set_operator(CAIRO_OPERATOR_SOURCE);
|
||||||
|
data.cctx
|
||||||
|
.set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _);
|
||||||
|
let y = y.unwrap_or((height - font_height) / 2);
|
||||||
|
data.cctx.move_to(x as f64, y as f64);
|
||||||
|
data.layout.show_layout();
|
||||||
|
data.image.flush();
|
||||||
|
let bytes = data.image.data().ok()?;
|
||||||
|
ctx.clone()
|
||||||
|
.shmem_texture(
|
||||||
|
None,
|
||||||
|
bytes,
|
||||||
|
ARGB8888,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data.image.stride(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
.map(|t| (t.into_texture(), measurement))
|
||||||
|
}
|
||||||
|
|
@ -5,12 +5,16 @@ use {
|
||||||
cursor::KnownCursor,
|
cursor::KnownCursor,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
format::ARGB8888,
|
format::ARGB8888,
|
||||||
gfx_api::{needs_render_usage, AcquireSync, GfxContext, GfxFramebuffer, ReleaseSync},
|
gfx_api::{
|
||||||
|
needs_render_usage, AcquireSync, GfxContext, GfxFramebuffer, GfxTexture, ReleaseSync,
|
||||||
|
},
|
||||||
ifs::zwlr_layer_shell_v1::OVERLAY,
|
ifs::zwlr_layer_shell_v1::OVERLAY,
|
||||||
portal::ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
|
portal::{
|
||||||
|
ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
|
||||||
|
ptl_text::{self, TextMeasurement},
|
||||||
|
},
|
||||||
renderer::renderer_base::RendererBase,
|
renderer::renderer_base::RendererBase,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
text::{self, TextMeasurement, TextTexture},
|
|
||||||
theme::Color,
|
theme::Color,
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||||
|
|
@ -31,10 +35,10 @@ use {
|
||||||
},
|
},
|
||||||
ahash::AHashSet,
|
ahash::AHashSet,
|
||||||
std::{
|
std::{
|
||||||
borrow::Cow,
|
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
sync::Arc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -117,8 +121,8 @@ pub struct Button {
|
||||||
pub bg_color: Cell<Color>,
|
pub bg_color: Cell<Color>,
|
||||||
pub bg_hover_color: Cell<Color>,
|
pub bg_hover_color: Cell<Color>,
|
||||||
pub text: RefCell<String>,
|
pub text: RefCell<String>,
|
||||||
pub font: RefCell<Cow<'static, str>>,
|
pub font: Arc<String>,
|
||||||
pub tex: CloneCell<Option<TextTexture>>,
|
pub tex: CloneCell<Option<Rc<dyn GfxTexture>>>,
|
||||||
pub owner: CloneCell<Option<Rc<dyn ButtonOwner>>>,
|
pub owner: CloneCell<Option<Rc<dyn ButtonOwner>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,7 +143,7 @@ impl Default for Button {
|
||||||
bg_color: Cell::new(Color::from_gray(255)),
|
bg_color: Cell::new(Color::from_gray(255)),
|
||||||
bg_hover_color: Cell::new(Color::from_gray(255)),
|
bg_hover_color: Cell::new(Color::from_gray(255)),
|
||||||
text: Default::default(),
|
text: Default::default(),
|
||||||
font: RefCell::new(DEFAULT_FONT.into()),
|
font: Arc::new(DEFAULT_FONT.to_string()),
|
||||||
tex: Default::default(),
|
tex: Default::default(),
|
||||||
owner: Default::default(),
|
owner: Default::default(),
|
||||||
}
|
}
|
||||||
|
|
@ -162,21 +166,16 @@ impl GuiElement for Button {
|
||||||
_max_width: f32,
|
_max_width: f32,
|
||||||
_max_height: f32,
|
_max_height: f32,
|
||||||
) -> (f32, f32) {
|
) -> (f32, f32) {
|
||||||
let old_tex = self.tex.take();
|
|
||||||
let font = self.font.borrow_mut();
|
|
||||||
let text = self.text.borrow_mut();
|
let text = self.text.borrow_mut();
|
||||||
let tex = text::render_fitting2(
|
let tex = ptl_text::render(
|
||||||
ctx,
|
ctx,
|
||||||
old_tex,
|
|
||||||
None,
|
None,
|
||||||
&font,
|
&self.font,
|
||||||
&text,
|
&text,
|
||||||
Color::from_gray(0),
|
Color::from_gray(0),
|
||||||
false,
|
|
||||||
Some(scale as _),
|
Some(scale as _),
|
||||||
true,
|
true,
|
||||||
)
|
);
|
||||||
.ok();
|
|
||||||
let (tex, msmt) = match tex {
|
let (tex, msmt) = match tex {
|
||||||
Some((a, b)) => (Some(a), Some(b)),
|
Some((a, b)) => (Some(a), Some(b)),
|
||||||
_ => (None, None),
|
_ => (None, None),
|
||||||
|
|
@ -215,7 +214,7 @@ impl GuiElement for Button {
|
||||||
if let Some(tex) = self.tex.get() {
|
if let Some(tex) = self.tex.get() {
|
||||||
let (tx, ty) = r.scale_point_f(x1 + self.tex_off_x.get(), y1 + self.tex_off_y.get());
|
let (tx, ty) = r.scale_point_f(x1 + self.tex_off_x.get(), y1 + self.tex_off_y.get());
|
||||||
r.render_texture(
|
r.render_texture(
|
||||||
&tex.texture,
|
&tex,
|
||||||
None,
|
None,
|
||||||
tx.round() as _,
|
tx.round() as _,
|
||||||
ty.round() as _,
|
ty.round() as _,
|
||||||
|
|
@ -262,16 +261,16 @@ const DEFAULT_FONT: &str = "sans-serif 16";
|
||||||
|
|
||||||
pub struct Label {
|
pub struct Label {
|
||||||
pub data: GuiElementData,
|
pub data: GuiElementData,
|
||||||
pub font: RefCell<Cow<'static, str>>,
|
pub font: Arc<String>,
|
||||||
pub text: RefCell<String>,
|
pub text: RefCell<String>,
|
||||||
pub tex: CloneCell<Option<TextTexture>>,
|
pub tex: CloneCell<Option<Rc<dyn GfxTexture>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Label {
|
impl Default for Label {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: Default::default(),
|
data: Default::default(),
|
||||||
font: RefCell::new(DEFAULT_FONT.into()),
|
font: Arc::new(DEFAULT_FONT.into()),
|
||||||
text: RefCell::new("".to_string()),
|
text: RefCell::new("".to_string()),
|
||||||
tex: Default::default(),
|
tex: Default::default(),
|
||||||
}
|
}
|
||||||
|
|
@ -290,24 +289,19 @@ impl GuiElement for Label {
|
||||||
_max_width: f32,
|
_max_width: f32,
|
||||||
_max_height: f32,
|
_max_height: f32,
|
||||||
) -> (f32, f32) {
|
) -> (f32, f32) {
|
||||||
let old_tex = self.tex.take();
|
|
||||||
let text = self.text.borrow_mut();
|
let text = self.text.borrow_mut();
|
||||||
let font = self.font.borrow_mut();
|
let tex = ptl_text::render(
|
||||||
let tex = text::render_fitting2(
|
|
||||||
ctx,
|
ctx,
|
||||||
old_tex,
|
|
||||||
None,
|
None,
|
||||||
&font,
|
&self.font,
|
||||||
&text,
|
&text,
|
||||||
Color::from_gray(255),
|
Color::from_gray(255),
|
||||||
false,
|
|
||||||
Some(scale as _),
|
Some(scale as _),
|
||||||
false,
|
false,
|
||||||
)
|
);
|
||||||
.ok();
|
|
||||||
let (tex, width, height) = match tex {
|
let (tex, width, height) = match tex {
|
||||||
Some((t, _)) => {
|
Some((t, _)) => {
|
||||||
let (width, height) = t.texture.size();
|
let (width, height) = t.size();
|
||||||
(Some(t.clone()), width, height)
|
(Some(t.clone()), width, height)
|
||||||
}
|
}
|
||||||
_ => (None, 0, 0),
|
_ => (None, 0, 0),
|
||||||
|
|
@ -320,7 +314,7 @@ impl GuiElement for Label {
|
||||||
if let Some(tex) = self.tex.get() {
|
if let Some(tex) = self.tex.get() {
|
||||||
let (tx, ty) = r.scale_point_f(x, y);
|
let (tx, ty) = r.scale_point_f(x, y);
|
||||||
r.render_texture(
|
r.render_texture(
|
||||||
&tex.texture,
|
&tex,
|
||||||
None,
|
None,
|
||||||
tx.round() as _,
|
tx.round() as _,
|
||||||
ty.round() as _,
|
ty.round() as _,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue