From bacb9e6848608d8493ad72bbaa4811ba8b2890b9 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 11 Oct 2023 15:52:46 -0600 Subject: [PATCH] Improve performance of text_box renderer --- src/main.rs | 26 +++++----- src/text_box.rs | 125 ++++++++++++++++++++++++++++++------------------ 2 files changed, 92 insertions(+), 59 deletions(-) diff --git a/src/main.rs b/src/main.rs index 611496a..bdb9451 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,20 +1,16 @@ // SPDX-License-Identifier: GPL-3.0-only use cosmic::{ - app::{self, Command, Core, Settings}, + app::{Command, Core, Settings}, executor, iced::{ - self, - widget::{column, container, horizontal_space, pick_list, row, text}, - Alignment, Color, Length, + widget::{column, row, text}, + Alignment, Length, }, - theme::{self, Theme, ThemeType}, - widget::{button, icon, segmented_button, toggler, view_switcher}, + widget::{icon, segmented_button, view_switcher}, ApplicationExt, Element, }; -use cosmic_text::{ - Attrs, AttrsList, Buffer, Edit, FontSystem, Metrics, SyntaxEditor, SyntaxSystem, Wrap, -}; +use cosmic_text::{Attrs, Buffer, Edit, FontSystem, Metrics, SyntaxEditor, SyntaxSystem}; use std::{env, fs, path::PathBuf, sync::Mutex}; use self::menu_list::MenuList; @@ -204,14 +200,20 @@ impl cosmic::Application for App { /// Creates the application, and optionally emits command on initialize. fn init(core: Core, _flags: Self::Flags) -> (Self, Command) { - let mut tab_model = segmented_button::Model::builder().build(); - - let mut app = App { core, tab_model }; + let mut app = App { + core, + tab_model: segmented_button::Model::builder().build(), + }; for path in env::args().skip(1) { app.open_tab(Some(PathBuf::from(path))); } + // Open an empty file if no arguments provided + if app.tab_model.iter().next().is_none() { + app.open_tab(None); + } + let command = app.update_title(); (app, command) } diff --git a/src/text_box.rs b/src/text_box.rs index af3a0ec..dee05f6 100644 --- a/src/text_box.rs +++ b/src/text_box.rs @@ -74,20 +74,7 @@ pub fn text_box<'a, Editor>(editor: &'a Mutex) -> TextBox<'a, Editor> { TextBox::new(editor) } -fn draw_pixel( - buffer: &mut [u8], - width: i32, - height: i32, - x: i32, - y: i32, - color: cosmic_text::Color, -) { - let alpha = (color.0 >> 24) & 0xFF; - if alpha == 0 { - // Do not draw if alpha is zero - return; - } - +fn draw_pixel(buffer: &mut [u32], width: i32, height: i32, x: i32, y: i32, color: u32) { if y < 0 || y >= height { // Skip if y out of bounds return; @@ -98,29 +85,72 @@ fn draw_pixel( return; } - let offset = (y as usize * width as usize + x as usize) * 4; - - let mut current = buffer[offset + 2] as u32 - | (buffer[offset + 1] as u32) << 8 - | (buffer[offset + 0] as u32) << 16 - | (buffer[offset + 3] as u32) << 24; - - if alpha >= 255 || current == 0 { - // Alpha is 100% or current is null, replace with no blending - current = color.0; - } else { - // Alpha blend with current value - let n_alpha = 255 - alpha; - let rb = ((n_alpha * (current & 0x00FF00FF)) + (alpha * (color.0 & 0x00FF00FF))) >> 8; - let ag = (n_alpha * ((current & 0xFF00FF00) >> 8)) - + (alpha * (0x01000000 | ((color.0 & 0x0000FF00) >> 8))); - current = (rb & 0x00FF00FF) | (ag & 0xFF00FF00); + let alpha = (color >> 24) & 0xFF; + if alpha == 0 { + // Do not draw if alpha is zero + return; } +} - buffer[offset + 2] = current as u8; - buffer[offset + 1] = (current >> 8) as u8; - buffer[offset + 0] = (current >> 16) as u8; - buffer[offset + 3] = (current >> 24) as u8; +//TODO: improve performance +fn draw_rect( + buffer: &mut [u32], + image_w: i32, + image_h: i32, + start_x: i32, + start_y: i32, + w: i32, + h: i32, + color: u32, +) { + let alpha = (color >> 24) & 0xFF; + if alpha == 0 { + // Do not draw if alpha is zero + return; + } else if alpha >= 255 { + // Handle overwrite + for y in start_y..start_y + h { + if y < 0 || y >= image_h { + // Skip if y out of bounds + continue; + } + + let line_offset = y as usize * image_w as usize; + for x in start_x..start_x + w { + if x < 0 || x >= image_w { + // Skip if x out of bounds + continue; + } + + let offset = line_offset + x as usize; + buffer[offset] = color; + } + } + } else { + let n_alpha = 255 - alpha; + for y in start_y..start_y + h { + if y < 0 || y >= image_h { + // Skip if y out of bounds + continue; + } + + let line_offset = y as usize * image_w as usize; + for x in start_x..start_x + w { + if x < 0 || x >= image_w { + // Skip if x out of bounds + continue; + } + + // Alpha blend with current value + let offset = line_offset + x as usize; + let current = buffer[offset]; + let rb = ((n_alpha * (current & 0x00FF00FF)) + (alpha * (color & 0x00FF00FF))) >> 8; + let ag = (n_alpha * ((current & 0xFF00FF00) >> 8)) + + (alpha * (0x01000000 | ((color & 0x0000FF00) >> 8))); + buffer[offset] = (rb & 0x00FF00FF) | (ag & 0xFF00FF00); + } + } + } } impl<'a, 'editor, Editor, Message, Renderer> Widget for TextBox<'a, Editor> @@ -250,18 +280,19 @@ where // Draw to pixel buffer let mut pixels = vec![0; image_w as usize * image_h as usize * 4]; - editor.draw( - &mut state.cache.lock().unwrap(), - text_color, - |x, y, w, h, color| { - //TODO: improve performance - for row in 0..h as i32 { - for col in 0..w as i32 { - draw_pixel(&mut pixels, image_w, image_h, x + col, y + row, color); - } - } - }, - ); + { + let buffer = unsafe { + std::slice::from_raw_parts_mut(pixels.as_mut_ptr() as *mut u32, pixels.len() / 4) + }; + + editor.draw( + &mut state.cache.lock().unwrap(), + text_color, + |x, y, w, h, color| { + draw_rect(buffer, image_w, image_h, x, y, w as i32, h as i32, color.0); + }, + ); + } // Restore original metrics editor.buffer_mut().set_metrics(metrics);