use crate::components::rich_text::batch::BatchManager;
pub use crate::components::rich_text::batch::{
Command,
DisplayList,
Rect,
Vertex,
};
use crate::components::rich_text::image_cache::glyph::GlyphCacheSession;
use crate::components::rich_text::image_cache::ImageCache;
pub use crate::components::rich_text::image_cache::{
AddImage,
ImageId,
ImageLocation,
TextureEvent,
TextureId,
};
use crate::components::rich_text::text::*;
use crate::SugarCursor;
use std::borrow::Borrow;
pub struct ComposedRect {
rect: Rect,
coords: [f32; 4],
color: [f32; 4],
has_alpha: bool,
image: TextureId,
}
pub enum CachedRect {
Image(ComposedRect),
Mask(ComposedRect),
Standard((Rect, [f32; 4])),
}
pub struct Compositor {
batches: BatchManager,
intercepts: Vec<(f32, f32)>,
}
impl Compositor {
pub fn new() -> Self {
Self {
batches: BatchManager::new(),
intercepts: Vec::new(),
}
}
#[inline]
pub fn begin(&mut self) {
self.batches.reset();
}
pub fn finish(
&mut self,
list: &mut DisplayList,
images: &mut ImageCache,
events: impl FnMut(TextureEvent),
) {
images.drain_events(events);
self.batches.build_display_list(list);
}
}
impl Compositor {
#[allow(unused)]
pub fn add_image(
&mut self,
images: &mut ImageCache,
request: AddImage,
) -> Option<ImageId> {
images.allocate(request)
}
#[allow(unused)]
pub fn get_image(
&mut self,
images: &mut ImageCache,
image: ImageId,
) -> Option<ImageLocation> {
images.get(image)
}
#[allow(unused)]
pub fn remove_image(&mut self, images: &mut ImageCache, image: ImageId) -> bool {
images.deallocate(image).is_some()
}
}
impl Compositor {
#[allow(unused)]
pub fn draw_rect(&mut self, rect: impl Into<Rect>, depth: f32, color: &[f32; 4]) {
self.batches.add_rect(&rect.into(), depth, color);
}
#[allow(unused)]
pub fn draw_image(
&mut self,
images: &mut ImageCache,
rect: impl Into<Rect>,
depth: f32,
color: &[f32; 4],
image: ImageId,
) {
if let Some(img) = images.get(image) {
self.batches.add_image_rect(
&rect.into(),
depth,
color,
&[img.min.0, img.min.1, img.max.0, img.max.1],
img.texture_id,
image.has_alpha(),
);
}
}
pub fn draw_glyphs_from_cache(&mut self, cache: &Vec<CachedRect>, depth: f32) {
for val in cache {
match val {
CachedRect::Image(data) => {
self.batches.add_image_rect(
&data.rect,
depth,
&data.color,
&data.coords,
data.image,
data.has_alpha,
);
}
CachedRect::Mask(data) => {
self.batches.add_mask_rect(
&data.rect,
depth,
&data.color,
&data.coords,
data.image,
data.has_alpha,
);
}
CachedRect::Standard((rect, bg_color)) => {
self.batches.add_rect(rect, depth, bg_color);
}
}
}
}
pub fn draw_glyphs<I>(
&mut self,
session: &mut GlyphCacheSession,
rect: impl Into<Rect>,
depth: f32,
style: &TextRunStyle,
glyphs: I,
) -> Vec<CachedRect>
where
I: Iterator,
I::Item: Borrow<Glyph>,
{
let rect = rect.into();
let (underline, underline_offset, underline_size, underline_color) =
match style.underline {
Some(underline) => (
true,
underline.offset.round() as i32,
underline.size.round().max(1.),
underline.color,
),
_ => (false, 0, 0., [0.0, 0.0, 0.0, 0.0]),
};
if underline {
self.intercepts.clear();
}
let mut result = Vec::new();
let subpx_bias = (0.125, 0.);
let color = style.color;
let x = rect.x;
for g in glyphs {
let glyph = g.borrow();
let entry = session.get(glyph.id);
if let Some(entry) = entry {
if let Some(img) = session.get_image(entry.image) {
let gx = (glyph.x + subpx_bias.0).floor() + entry.left as f32;
let gy = (glyph.y + subpx_bias.1).floor() - entry.top as f32;
if entry.is_bitmap {
let rect =
Rect::new(gx, gy, entry.width as f32, entry.height as f32);
let color = [1.0, 1.0, 1.0, 1.0];
let coords = [img.min.0, img.min.1, img.max.0, img.max.1];
self.batches.add_image_rect(
&rect,
depth,
&color,
&coords,
img.texture_id,
entry.image.has_alpha(),
);
result.push(CachedRect::Image(ComposedRect {
rect,
color,
coords,
image: img.texture_id,
has_alpha: entry.image.has_alpha(),
}));
} else {
let rect =
Rect::new(gx, gy, entry.width as f32, entry.height as f32);
let coords = [img.min.0, img.min.1, img.max.0, img.max.1];
self.batches.add_mask_rect(
&rect,
depth,
&color,
&coords,
img.texture_id,
true,
);
result.push(CachedRect::Mask(ComposedRect {
rect,
color,
coords,
image: img.texture_id,
has_alpha: true,
}));
}
if let Some(bg_color) = style.background_color {
let rect = Rect::new(
rect.x,
style.topline,
rect.width,
style.line_height,
);
self.batches.add_rect(&rect, depth, &bg_color);
result.push(CachedRect::Standard((rect, bg_color)));
}
match style.cursor {
SugarCursor::Block(cursor_color) => {
let rect = Rect::new(
rect.x,
style.topline,
rect.width,
style.line_height,
);
self.batches.add_rect(&rect, depth, &cursor_color);
result.push(CachedRect::Standard((rect, cursor_color)));
}
SugarCursor::Caret(cursor_color) => {
let rect =
Rect::new(rect.x, style.topline, 3.0, style.line_height);
self.batches.add_rect(&rect, depth, &cursor_color);
result.push(CachedRect::Standard((rect, cursor_color)));
}
_ => {}
}
if underline && entry.top - underline_offset < entry.height as i32 {
if let Some(mut desc_ink) = entry.desc.range() {
desc_ink.0 += gx;
desc_ink.1 += gx;
self.intercepts.push(desc_ink);
}
}
}
}
}
if underline {
for range in self.intercepts.iter_mut() {
range.0 -= 1.;
range.1 += 1.;
}
let mut ux = x;
let uy = style.baseline - underline_offset as f32;
for range in self.intercepts.iter() {
if ux < range.0 {
let rect = Rect::new(ux, uy, range.0 - ux, underline_size);
self.batches.add_rect(&rect, depth, &underline_color);
result.push(CachedRect::Standard((rect, underline_color)));
}
ux = range.1;
}
let end = x + rect.width;
if ux < end {
let rect = Rect::new(ux, uy, end - ux, underline_size);
self.batches.add_rect(&rect, depth, &underline_color);
result.push(CachedRect::Standard((rect, underline_color)));
}
}
result
}
}