Compare commits
1 Commits
97b284e81e
...
23c5abe5a9
| Author | SHA1 | Date | |
|---|---|---|---|
| 23c5abe5a9 |
@@ -33,9 +33,7 @@ impl TextEdit {
|
||||
|
||||
impl Widget for TextEdit {
|
||||
fn draw(&mut self, painter: &mut Painter) {
|
||||
let tex = self.view.draw(&mut painter.size_ctx());
|
||||
let region = text_region(&tex, self.align);
|
||||
painter.texture_within(&tex.handle, region);
|
||||
let region = self.view.draw(painter);
|
||||
|
||||
if let Some(cursor) = &self.cursor
|
||||
&& let Some(offset) = cursor_pos(cursor, &self.buf)
|
||||
@@ -49,11 +47,11 @@ impl Widget for TextEdit {
|
||||
}
|
||||
|
||||
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||
Len::abs(self.view.draw(ctx).size.x)
|
||||
Len::abs(self.view.render(ctx).size.x)
|
||||
}
|
||||
|
||||
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||
Len::abs(self.view.draw(ctx).size.y)
|
||||
Len::abs(self.view.render(ctx).size.y)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ pub struct TextView {
|
||||
pub attrs: MutDetect<TextAttrs>,
|
||||
pub buf: MutDetect<TextBuffer>,
|
||||
// cache
|
||||
tex: Option<TextTexture>,
|
||||
tex: Option<RenderedText>,
|
||||
width: Option<f32>,
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ impl TextView {
|
||||
width: None,
|
||||
}
|
||||
}
|
||||
pub fn draw(&mut self, ctx: &mut SizeCtx) -> TextTexture {
|
||||
pub fn render(&mut self, ctx: &mut SizeCtx) -> RenderedText {
|
||||
let width = if self.attrs.wrap {
|
||||
Some(ctx.px_size().x)
|
||||
} else {
|
||||
@@ -55,9 +55,25 @@ impl TextView {
|
||||
self.buf.changed = false;
|
||||
tex
|
||||
}
|
||||
pub fn tex(&self) -> Option<&TextTexture> {
|
||||
pub fn tex(&self) -> Option<&RenderedText> {
|
||||
self.tex.as_ref()
|
||||
}
|
||||
pub fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||
Len::abs(self.render(ctx).size.x)
|
||||
}
|
||||
pub fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||
Len::abs(self.render(ctx).size.y)
|
||||
}
|
||||
pub fn draw(&mut self, painter: &mut Painter) -> UiRegion {
|
||||
let tex = self.render(&mut painter.size_ctx());
|
||||
let region = tex.size.align(self.align);
|
||||
let dims = tex.handle.size();
|
||||
let mut region = region.offset(tex.top_left_offset);
|
||||
region.x.end = region.x.start + UiScalar::abs(dims.x);
|
||||
region.y.end = region.y.start + UiScalar::abs(dims.y);
|
||||
painter.texture_within(&tex.handle, region);
|
||||
region
|
||||
}
|
||||
}
|
||||
|
||||
impl Text {
|
||||
@@ -69,7 +85,7 @@ impl Text {
|
||||
view: TextView::new(buf, attrs),
|
||||
}
|
||||
}
|
||||
fn update_buf(&mut self, ctx: &mut SizeCtx) -> TextTexture {
|
||||
fn update_buf(&mut self, ctx: &mut SizeCtx) {
|
||||
if self.content.changed {
|
||||
self.content.changed = false;
|
||||
self.view.buf.set_text(
|
||||
@@ -80,36 +96,26 @@ impl Text {
|
||||
None,
|
||||
);
|
||||
}
|
||||
self.view.draw(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Text {
|
||||
fn draw(&mut self, painter: &mut Painter) {
|
||||
let tex = self.update_buf(&mut painter.size_ctx());
|
||||
let region = text_region(&tex, self.align);
|
||||
painter.texture_within(&tex.handle, region);
|
||||
self.update_buf(&mut painter.size_ctx());
|
||||
self.view.draw(painter);
|
||||
}
|
||||
|
||||
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||
Len::abs(self.update_buf(ctx).size.x)
|
||||
self.update_buf(ctx);
|
||||
self.view.desired_width(ctx)
|
||||
}
|
||||
|
||||
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||
Len::abs(self.update_buf(ctx).size.y)
|
||||
self.update_buf(ctx);
|
||||
self.view.desired_height(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text_region(tex: &TextTexture, align: RegionAlign) -> UiRegion {
|
||||
let tex_dims = tex.handle.size();
|
||||
let mut region = tex.size.align(align);
|
||||
region.x.start.abs += tex.top_left_offset.x;
|
||||
region.y.start.abs += tex.top_left_offset.y;
|
||||
region.x.end.abs = region.x.start.abs + tex_dims.x;
|
||||
region.y.end.abs = region.y.start.abs + tex_dims.y;
|
||||
region
|
||||
}
|
||||
|
||||
impl Deref for Text {
|
||||
type Target = TextAttrs;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/// stored in linear for sane manipulation
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, bytemuck::Zeroable, Debug)]
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq, bytemuck::Zeroable, Debug)]
|
||||
pub struct Color<T: ColorNum> {
|
||||
pub r: T,
|
||||
pub g: T,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
layout::{
|
||||
Axis, Layers, Len, Modules, Size, TextAttrs, TextBuffer, TextData, TextTexture,
|
||||
Axis, Layers, Len, Modules, Size, TextAttrs, TextBuffer, TextData, RenderedText,
|
||||
TextureHandle, Textures, UiRegion, UiVec2, Vec2, WidgetId, Widgets,
|
||||
},
|
||||
render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst},
|
||||
@@ -422,7 +422,7 @@ impl<'a, 'c> Painter<'a, 'c> {
|
||||
}
|
||||
|
||||
/// returns (handle, offset from top left)
|
||||
pub fn render_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> TextTexture {
|
||||
pub fn render_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> RenderedText {
|
||||
self.ctx.text.draw(buffer, attrs, self.ctx.textures)
|
||||
}
|
||||
|
||||
@@ -539,7 +539,6 @@ impl SizeCtx<'_> {
|
||||
// if let Some(&(outer, len)) = self.cache_height.get(&id)
|
||||
// && outer == self.outer
|
||||
// {
|
||||
// prntln!("FAIL {id:?}");
|
||||
// self.checked_height.insert(id, (self.outer, len));
|
||||
// return len;
|
||||
// }
|
||||
@@ -584,7 +583,7 @@ impl SizeCtx<'_> {
|
||||
self.output_size
|
||||
}
|
||||
|
||||
pub fn draw_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> TextTexture {
|
||||
pub fn draw_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> RenderedText {
|
||||
self.text.draw(buffer, attrs, self.textures)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use cosmic_text::{
|
||||
Attrs, AttrsList, Buffer, Family, FontSystem, Metrics, SwashCache, SwashContent,
|
||||
};
|
||||
use image::{Rgba, RgbaImage};
|
||||
use std::simd::{Simd, num::SimdUint};
|
||||
|
||||
use crate::{
|
||||
layout::{Align, RegionAlign, TextureHandle, Textures, UiColor, Vec2},
|
||||
util::HashMap,
|
||||
use crate::layout::{Align, RegionAlign, TextureHandle, Textures, UiColor, Vec2};
|
||||
use cosmic_text::{
|
||||
Attrs, AttrsList, Buffer, CacheKey, Color, Family, FontSystem, Metrics, Placement, SwashCache,
|
||||
SwashContent,
|
||||
};
|
||||
use image::{GenericImageView, RgbaImage};
|
||||
|
||||
/// TODO: properly wrap this
|
||||
pub mod text_lib {
|
||||
@@ -16,6 +15,7 @@ pub mod text_lib {
|
||||
pub struct TextData {
|
||||
pub font_system: FontSystem,
|
||||
pub swash_cache: SwashCache,
|
||||
glyph_cache: Vec<(Placement, CacheKey, Color)>,
|
||||
}
|
||||
|
||||
impl Default for TextData {
|
||||
@@ -23,6 +23,7 @@ impl Default for TextData {
|
||||
Self {
|
||||
font_system: FontSystem::new(),
|
||||
swash_cache: SwashCache::new(),
|
||||
glyph_cache: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,29 +77,30 @@ impl TextData {
|
||||
buffer: &mut TextBuffer,
|
||||
attrs: &TextAttrs,
|
||||
textures: &mut Textures,
|
||||
) -> TextTexture {
|
||||
) -> RenderedText {
|
||||
// TODO: either this or the layout stuff (or both) is super slow,
|
||||
// should probably do texture packing and things if possible.
|
||||
// very visible if you add just a couple of wrapping texts and resize window
|
||||
// should also be timed to figure out exactly what points need to be sped up
|
||||
let mut pixels = HashMap::<_, [u8; 4]>::default();
|
||||
// let mut pixels = HashMap::<_, [u8; 4]>::default();
|
||||
let mut min_x = 0;
|
||||
let mut min_y = 0;
|
||||
let mut max_x = 0;
|
||||
let mut max_y = 0;
|
||||
let cosmic_color = {
|
||||
let text_color = {
|
||||
let c = attrs.color;
|
||||
cosmic_text::Color::rgba(c.r, c.g, c.b, c.a)
|
||||
};
|
||||
let mut max_width = 0.0f32;
|
||||
let mut height = 0.0;
|
||||
|
||||
for run in buffer.layout_runs() {
|
||||
for glyph in run.glyphs.iter() {
|
||||
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
||||
|
||||
let glyph_color = match glyph.color_opt {
|
||||
Some(some) => some,
|
||||
None => cosmic_color,
|
||||
None => text_color,
|
||||
};
|
||||
|
||||
if let Some(img) = self
|
||||
@@ -112,35 +114,8 @@ impl TextData {
|
||||
min_y = min_y.min(pos.top);
|
||||
max_x = max_x.max(pos.left + pos.width as i32);
|
||||
max_y = max_y.max(pos.top + pos.height as i32);
|
||||
let mut merge = |i, color: [u8; 4]| {
|
||||
let x = i % pos.width as usize;
|
||||
let y = i / pos.width as usize;
|
||||
let pos = (x as i32 + pos.left, y as i32 + pos.top);
|
||||
if let Some(pixel) = pixels.get_mut(&pos) {
|
||||
for i in 0..4 {
|
||||
// TODO: no clue if proper alpha blending should be done
|
||||
pixel[i] = color[i].saturating_add(pixel[i]);
|
||||
}
|
||||
} else {
|
||||
pixels.insert(pos, color);
|
||||
}
|
||||
};
|
||||
match img.content {
|
||||
SwashContent::Mask => {
|
||||
for (i, a) in img.data.iter().enumerate() {
|
||||
let mut color = glyph_color.as_rgba();
|
||||
color[3] = ((color[3] as u32 * *a as u32) / u8::MAX as u32) as u8;
|
||||
merge(i, color);
|
||||
}
|
||||
}
|
||||
SwashContent::SubpixelMask => todo!(),
|
||||
SwashContent::Color => {
|
||||
let (colors, _) = img.data.as_chunks::<4>();
|
||||
for (i, color) in colors.iter().enumerate() {
|
||||
merge(i, *color);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.glyph_cache
|
||||
.push((pos, physical_glyph.cache_key, glyph_color));
|
||||
}
|
||||
}
|
||||
max_width = max_width.max(run.line_w);
|
||||
@@ -150,12 +125,52 @@ impl TextData {
|
||||
let img_height = (max_y - min_y + 1) as u32;
|
||||
let mut image = RgbaImage::new(img_width, img_height);
|
||||
|
||||
for ((x, y), color) in pixels {
|
||||
let x = (x - min_x) as u32;
|
||||
let y = (y - min_y) as u32;
|
||||
image.put_pixel(x, y, Rgba(color));
|
||||
for (pos, key, color) in self.glyph_cache.drain(..) {
|
||||
let img = self
|
||||
.swash_cache
|
||||
.get_image(&mut self.font_system, key)
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let mut merge = |i, color: [u8; 4]| {
|
||||
let i = i as i32;
|
||||
let x = (i % pos.width as i32 + pos.left - min_x) as u32;
|
||||
let y = (i / pos.width as i32 + pos.top - min_y) as u32;
|
||||
let pixel = &mut image[(x, y)].0;
|
||||
// TODO: no clue if proper alpha blending should be done
|
||||
*pixel = Simd::from(color).saturating_add(Simd::from(*pixel)).into();
|
||||
};
|
||||
|
||||
match img.content {
|
||||
SwashContent::Mask => {
|
||||
for (i, a) in img.data.iter().enumerate() {
|
||||
let mut color = color.as_rgba();
|
||||
color[3] = ((color[3] as u32 * *a as u32) / u8::MAX as u32) as u8;
|
||||
merge(i, color);
|
||||
}
|
||||
TextTexture {
|
||||
}
|
||||
SwashContent::SubpixelMask => todo!("subpixel mask text rendering"),
|
||||
SwashContent::Color => {
|
||||
let (colors, _) = img.data.as_chunks::<4>();
|
||||
for (i, color) in colors.iter().enumerate() {
|
||||
merge(i, *color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let max_dim = 8192;
|
||||
if image.width() > max_dim || image.height() > max_dim {
|
||||
let width = image.width().min(max_dim);
|
||||
let height = image.height().min(max_dim);
|
||||
eprintln!(
|
||||
"WARNING: image of size {:?} cropped to {:?} (texture too big)",
|
||||
image.dimensions(),
|
||||
(width, height)
|
||||
);
|
||||
image = image.view(0, 0, width, height).to_image();
|
||||
}
|
||||
|
||||
RenderedText {
|
||||
handle: textures.add(image),
|
||||
top_left_offset: Vec2::new(min_x as f32, min_y as f32),
|
||||
size: Vec2::new(max_width, height),
|
||||
@@ -164,9 +179,8 @@ impl TextData {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TextTexture {
|
||||
pub struct RenderedText {
|
||||
pub handle: TextureHandle,
|
||||
pub top_left_offset: Vec2,
|
||||
pub size: Vec2,
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#![feature(fn_traits)]
|
||||
#![feature(const_cmp)]
|
||||
#![feature(const_destruct)]
|
||||
#![feature(portable_simd)]
|
||||
|
||||
pub mod core;
|
||||
pub mod layout;
|
||||
|
||||
@@ -48,7 +48,7 @@ macro_rules! impl_op {
|
||||
}
|
||||
impl const $opa for $T {
|
||||
fn $fna(&mut self, rhs: Self) {
|
||||
$(self.$field.$fna(rhs.$field);)*
|
||||
*self = self.$fn(rhs);
|
||||
}
|
||||
}
|
||||
impl const $op<f32> for $T {
|
||||
@@ -71,7 +71,7 @@ macro_rules! impl_op {
|
||||
}
|
||||
impl const $opa<f32> for $T {
|
||||
fn $fna(&mut self, rhs: f32) {
|
||||
$(self.$field.$fna(rhs);)*
|
||||
*self = self.$fn(rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user