1
0
Fork 0
forked from wry/wry
wry/src/text.rs
Julian Orth e52a60b3b6 wayland: implement scaling
This involves many subsystems:

- config:
    - allow setting the connector scale
    - allow setting the cursor size
- cursors:
    - load server cursors for all requested sizes and scales
- wl_surface:
    - track the output the surface belongs to
    - send wl_surface.enter/leave
- wl_output:
    - implement wl_output.scale
- text:
    - pre-render texts for all used scales
- renderer:
    - properly align scale textures and rectangles
- wp_fractional_scale:
    - new interface for fractional scaling
2022-05-30 17:00:25 +02:00

168 lines
4.4 KiB
Rust

use {
crate::{
format::ARGB8888,
pango::{
consts::{
CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE, PANGO_ELLIPSIZE_END, PANGO_SCALE,
},
CairoContext, CairoImageSurface, PangoCairoContext, PangoError, PangoFontDescription,
PangoLayout,
},
rect::Rect,
render::{RenderContext, RenderError, Texture},
theme::Color,
},
std::{ops::Neg, rc::Rc},
thiserror::Error,
};
#[derive(Debug, Error)]
pub enum TextError {
#[error("Could not create a cairo image")]
CreateImage(#[source] PangoError),
#[error("Could not create a cairo context")]
CairoContext(#[source] PangoError),
#[error("Could not create a pango context")]
PangoContext(#[source] PangoError),
#[error("Could not create a pango layout")]
CreateLayout(#[source] PangoError),
#[error("Could not import the rendered text")]
RenderError(#[source] RenderError),
#[error("Could not access the cairo image data")]
ImageData(#[source] PangoError),
}
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>) -> Result<Data, TextError> {
let image = match CairoImageSurface::new_image_surface(CAIRO_FORMAT_ARGB32, width, height) {
Ok(s) => s,
Err(e) => return Err(TextError::CreateImage(e)),
};
let cctx = match image.create_context() {
Ok(c) => c,
Err(e) => return Err(TextError::CairoContext(e)),
};
let pctx = match cctx.create_pango_context() {
Ok(c) => c,
Err(e) => return Err(TextError::PangoContext(e)),
};
let mut fd = PangoFontDescription::from_string(font);
if let Some(scale) = scale {
fd.set_size((fd.size() as f64 * scale).round() as _);
}
let layout = match pctx.create_layout() {
Ok(l) => l,
Err(e) => return Err(TextError::CreateLayout(e)),
};
layout.set_font_description(&fd);
Ok(Data {
image,
cctx,
_pctx: pctx,
_fd: fd,
layout,
})
}
pub fn measure(
font: &str,
text: &str,
markup: bool,
scale: Option<f64>,
) -> Result<Rect, TextError> {
let data = create_data(font, 1, 1, scale)?;
if markup {
data.layout.set_markup(text);
} else {
data.layout.set_text(text);
}
Ok(data.layout.inc_pixel_rect())
}
pub fn render(
ctx: &Rc<RenderContext>,
width: i32,
height: i32,
font: &str,
text: &str,
color: Color,
scale: Option<f64>,
) -> Result<Rc<Texture>, TextError> {
render2(
ctx, 1, width, height, 1, font, text, color, true, false, scale,
)
}
fn render2(
ctx: &Rc<RenderContext>,
x: i32,
width: i32,
height: i32,
padding: i32,
font: &str,
text: &str,
color: Color,
ellipsize: bool,
markup: bool,
scale: Option<f64>,
) -> Result<Rc<Texture>, TextError> {
let data = create_data(font, width, height, scale)?;
if ellipsize {
data.layout
.set_width((width - 2 * padding).max(0) * PANGO_SCALE);
data.layout.set_ellipsize(PANGO_ELLIPSIZE_END);
}
if markup {
data.layout.set_markup(text);
} else {
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 _);
data.cctx
.move_to(x as f64, ((height - font_height) / 2) as f64);
data.layout.show_layout();
data.image.flush();
let bytes = match data.image.data() {
Ok(d) => d,
Err(e) => return Err(TextError::ImageData(e)),
};
match ctx.shmem_texture(bytes, ARGB8888, width, height, data.image.stride()) {
Ok(t) => Ok(t),
Err(e) => Err(TextError::RenderError(e)),
}
}
pub fn render_fitting(
ctx: &Rc<RenderContext>,
height: i32,
font: &str,
text: &str,
color: Color,
markup: bool,
scale: Option<f64>,
) -> Result<Rc<Texture>, TextError> {
let rect = measure(font, text, markup, scale)?;
render2(
ctx,
rect.x1().neg(),
rect.width(),
height,
0,
font,
text,
color,
false,
markup,
scale,
)
}