From 1eff2cd4f7c15457fdf08dddc0dfddd6fff7ddf3 Mon Sep 17 00:00:00 2001 From: chlorospingus Date: Wed, 28 May 2025 14:34:21 -0700 Subject: [PATCH] Properly use premultiplied alpha --- src/graphics/circle.rs | 13 ++++---- src/graphics/drawable.rs | 65 ++++++++++++++++++++++++--------------- src/graphics/rectangle.rs | 14 ++++----- src/wayland/shm.rs | 21 ++++++------- src/wayland/surface.rs | 2 +- src/wayland/wl_client.rs | 8 ++--- 6 files changed, 69 insertions(+), 54 deletions(-) diff --git a/src/graphics/circle.rs b/src/graphics/circle.rs index 93e8207..fd8ed5a 100644 --- a/src/graphics/circle.rs +++ b/src/graphics/circle.rs @@ -24,19 +24,18 @@ impl Drawable for Circle { for l_row in 1..self.radius { let inner_diff = (((self.radius-1).pow(2) - l_row.pow(2)) as f64).sqrt(); let outer_diff = ((self.radius.pow(2) - l_row.pow(2)) as f64).sqrt(); - let row: Vec = vec![self.color; 2*(inner_diff.floor() as usize)]; - shm_pool.write(&row, (self.y-l_row)*buffer.width + self.x - inner_diff.floor() as usize + buffer.offset); - shm_pool.write(&row, (self.y+l_row-1)*buffer.width + self.x - inner_diff.floor() as usize + buffer.offset); + shm_pool.write(self.color, (self.y-l_row)*buffer.width + self.x - inner_diff.floor() as usize + buffer.offset, 2*(inner_diff.floor() as usize)); + shm_pool.write(self.color, (self.y+l_row-1)*buffer.width + self.x - inner_diff.floor() as usize + buffer.offset, 2*(inner_diff.floor() as usize)); for l_col in (inner_diff.floor() as usize+1)..(outer_diff.ceil() as usize) { let distance = ((l_row.pow(2) + l_col.pow(2)) as f64).sqrt(); let offset = (self.y-l_row)*buffer.width + self.x - l_col + buffer.offset; - shm_pool.write_pixel(color_blend(self.color, shm_pool.read_pixel(offset).unwrap(), distance.fract()), offset); + shm_pool.write_pixel(color_blend(self.color, 0, distance.fract()), offset); let offset = (self.y-l_row)*buffer.width + self.x + l_col-1 + buffer.offset; - shm_pool.write_pixel(color_blend(self.color, shm_pool.read_pixel(offset).unwrap(), distance.fract()), offset); + shm_pool.write_pixel(color_blend(self.color, 0, distance.fract()), offset); let offset = (self.y+l_row-1)*buffer.width + self.x - l_col + buffer.offset; - shm_pool.write_pixel(color_blend(self.color, shm_pool.read_pixel(offset).unwrap(), distance.fract()), offset); + shm_pool.write_pixel(color_blend(self.color, 0, distance.fract()), offset); let offset = (self.y+l_row-1)*buffer.width + self.x + l_col-1 + buffer.offset; - shm_pool.write_pixel(color_blend(self.color, shm_pool.read_pixel(offset).unwrap(), distance.fract()), offset); + shm_pool.write_pixel(color_blend(self.color, 0, distance.fract()), offset); } } } diff --git a/src/graphics/drawable.rs b/src/graphics/drawable.rs index e6315c7..9a3cd00 100644 --- a/src/graphics/drawable.rs +++ b/src/graphics/drawable.rs @@ -1,34 +1,51 @@ use crate::wayland::{shm::ShmPool, wl_shm::wl_buffer}; -pub fn rgb_blend(col1: u32, col2: u32, diff: f64) -> u32 { - let r1 = (col1 & 0x00ff0000) >> 16; - let g1 = (col1 & 0x0000ff00) >> 8; - let b1 = col1 & 0x000000ff; - let r2 = (col2 & 0x00ff0000) >> 16; - let g2 = (col2 & 0x0000ff00) >> 8; - let b2 = col2 & 0x000000ff; - let r3 = if r1 < r2 { - r1 + ((r2 - r1) as f64 * diff) as u32 - } else { - r1 - ((r1 - r2) as f64 * diff) as u32 - }; - let g3 = if g1 < g2 { - g1 + ((g2 - g1) as f64 * diff) as u32 - } else { - g1 - ((g1 - g2) as f64 * diff) as u32 - }; - let b3 = if b1 < b2 { - b1 + ((b2 - b1) as f64 * diff) as u32 - } else { - b1 - ((b1 - b2) as f64 * diff) as u32 - }; - (r3 << 16) + (g3 << 8) + b3 +pub fn premultiply(color: u32) -> u32 { + let a = (color & 0xff000000) >> 24; + let r = (color & 0x00ff0000) >> 16; + let g = (color & 0x0000ff00) >> 8; + let b = (color & 0x000000ff) >> 0; + let alpha = (a as f64 / 0xff as f64); + (a << 24) + + (((r as f64*alpha) as u32) << 16) + + (((g as f64*alpha) as u32) << 8) + + (((b as f64*alpha) as u32) << 0) } pub fn color_blend(col1: u32, col2: u32, diff: f64) -> u32 { let a1 = (col1 & 0xff000000) >> 24; + let r1 = (col1 & 0x00ff0000) >> 16; + let g1 = (col1 & 0x0000ff00) >> 8; + let b1 = (col1 & 0x000000ff) >> 0; let a2 = (col2 & 0xff000000) >> 24; - ((a2 + ((a1 as f64/0xff as f64)*(0xff - a2) as f64*(1.0-diff)) as u32) << 24) + rgb_blend(col1, col2, diff * (a1 as f64 / 0xff as f64)) + let r2 = (col2 & 0x00ff0000) >> 16; + let g2 = (col2 & 0x0000ff00) >> 8; + let b2 = (col2 & 0x000000ff) >> 0; + (((a1 as f64 + (a2 as f64 - a1 as f64) * diff).round() as u32) << 24) + + (((r1 as f64 + (r2 as f64 - r1 as f64) * diff).round() as u32) << 16) + + (((g1 as f64 + (b2 as f64 - g1 as f64) * diff).round() as u32) << 8 ) + + (((b1 as f64 + (g2 as f64 - b1 as f64) * diff).round() as u32) << 0 ) +} + +pub fn color_over(over: u32, under: u32) -> u32 { + let a_over = (over & 0xff000000) >> 24; + let a_under = (under & 0xff000000) >> 24; + if a_over == 0xff || a_under == 0 { + return over; + } + if a_over == 0 { + return under; + } + let r_over = (over & 0x00ff0000) >> 16; + let r_under = (under & 0x00ff0000) >> 16; + let g_over = (over & 0x0000ff00) >> 8; + let g_under = (under & 0x0000ff00) >> 8; + let b_over = (over & 0x000000ff) >> 0; + let b_under = (under & 0x000000ff) >> 0; + ((a_over + (a_under as f64 * (0xff - a_over) as f64 / 0xff as f64) as u32).min(0xff) << 24).min(0xff) + + ((r_over + (r_under as f64 * (0xff - a_over) as f64 / 0xff as f64) as u32).min(0xff) << 16).min(0xff) + + ((g_over + (g_under as f64 * (0xff - a_over) as f64 / 0xff as f64) as u32).min(0xff) << 8) + + ((b_over + (b_under as f64 * (0xff - a_over) as f64 / 0xff as f64) as u32).min(0xff) << 0) } pub trait Drawable : Send { diff --git a/src/graphics/rectangle.rs b/src/graphics/rectangle.rs index b8994cb..8f2e178 100644 --- a/src/graphics/rectangle.rs +++ b/src/graphics/rectangle.rs @@ -24,24 +24,24 @@ impl Drawable for Rectangle { fn draw(&self, buffer: &wl_buffer, shm_pool: &mut ShmPool) { for g_row in self.y+self.radius..self.y+self.height-self.radius+1 { - shm_pool.write(&vec![self.color; self.width], g_row*buffer.width as usize+self.x + buffer.offset); + shm_pool.write(self.color, g_row*buffer.width as usize+self.x + buffer.offset, self.width); } for l_row in 1..self.radius { let inner_diff = (((self.radius-1).pow(2) - l_row.pow(2)) as f64).sqrt(); let outer_diff = ((self.radius.pow(2) - l_row.pow(2)) as f64).sqrt(); - shm_pool.write(&vec![self.color; self.width - (2*(self.radius - inner_diff.floor() as usize-1))], (self.y+self.radius-l_row)*buffer.width as usize + self.x + self.radius - inner_diff.floor() as usize-1 + buffer.offset); - shm_pool.write(&vec![self.color; self.width - (2*(self.radius - inner_diff.floor() as usize-1))], (self.y+self.height-self.radius+l_row)*buffer.width as usize + self.x + self.radius - inner_diff.floor() as usize-1 + buffer.offset); + shm_pool.write(self.color, (self.y+self.radius-l_row)*buffer.width as usize + self.x + self.radius - inner_diff.floor() as usize-1 + buffer.offset, self.width - (2*(self.radius - inner_diff.floor() as usize-1))); + shm_pool.write(self.color, (self.y+self.height-self.radius+l_row)*buffer.width as usize + self.x + self.radius - inner_diff.floor() as usize-1 + buffer.offset, self.width - (2*(self.radius - inner_diff.floor() as usize-1))); for l_col in inner_diff.floor() as usize+1..outer_diff.ceil() as usize { // TODO: handle error from read_pixel let distance = ((l_row.pow(2) + l_col.pow(2)) as f64).sqrt(); let offset = (self.y+self.radius-l_row)*buffer.width as usize + self.x + self.radius - l_col - 1 + buffer.offset; - shm_pool.write_pixel(color_blend(self.color, shm_pool.read_pixel(offset).unwrap(), distance.fract()), offset); + shm_pool.write_pixel(color_blend(self.color, 0, distance.fract()), offset); let offset = (self.y+self.radius-l_row)*buffer.width as usize + self.x + self.width - self.radius + l_col + buffer.offset; - shm_pool.write_pixel(color_blend(self.color, shm_pool.read_pixel(offset).unwrap(), distance.fract()), offset); + shm_pool.write_pixel(color_blend(self.color, 0, distance.fract()), offset); let offset = (self.y+self.height-self.radius+l_row)*buffer.width as usize + self.x + self.radius - l_col - 1 + buffer.offset; - shm_pool.write_pixel(color_blend(self.color, shm_pool.read_pixel(offset).unwrap(), distance.fract()), offset); + shm_pool.write_pixel(color_blend(self.color, 0, distance.fract()), offset); let offset = (self.y+self.height-self.radius+l_row)*buffer.width as usize + self.x + self.width - self.radius + l_col + buffer.offset; - shm_pool.write_pixel(color_blend(self.color, shm_pool.read_pixel(offset).unwrap(), distance.fract()), offset); + shm_pool.write_pixel(color_blend(self.color, 0, distance.fract()), offset); } } } diff --git a/src/wayland/shm.rs b/src/wayland/shm.rs index 91ab670..5c51480 100644 --- a/src/wayland/shm.rs +++ b/src/wayland/shm.rs @@ -1,5 +1,7 @@ use libc::{c_void, ftruncate, mmap, munmap, shm_open, shm_unlink, MAP_FAILED, MAP_PRIVATE, MAP_SHARED, O_CREAT, O_EXCL, O_RDWR, PROT_READ, PROT_WRITE}; +use crate::graphics::drawable::color_over; + #[derive(Clone)] pub struct ShmPool { pub fd: i32, @@ -69,25 +71,22 @@ impl ShmPool { Ok(()) } - pub fn write(&mut self, data: &Vec, offset: usize) { - if offset + data.len() >= self.size { + pub fn write(&mut self, color: u32, offset: usize, len: usize) { + if offset + len/4 >= self.size { return; } - unsafe { - std::ptr::copy_nonoverlapping( - data.as_ptr() as *const u32, // src: data as *const u32 - self.addr.offset(4*offset as isize) as *mut u32, // dst: ShmPool address as *mut u32 - data.len() - ); - } + for i in offset..offset+len { unsafe { + *((self.addr as *mut u32).offset(i as isize)) + = color_over(color, self.read_pixel(i).unwrap()); + }} } - pub fn write_pixel(&mut self, data: u32, offset: usize) { + pub fn write_pixel(&mut self, color: u32, offset: usize) { // TODO: Return error if out of bounds if offset + 3 > self.size { return; } - unsafe {*(self.addr.offset(offset as isize*4) as *mut u32) = data;} + unsafe {*((self.addr as *mut u32).offset(offset as isize)) = color;} } pub fn read_pixel(&self, offset: usize) -> Option { diff --git a/src/wayland/surface.rs b/src/wayland/surface.rs index bc4b64d..522f869 100644 --- a/src/wayland/surface.rs +++ b/src/wayland/surface.rs @@ -121,7 +121,7 @@ impl WlClient { self.wl_surface_attach(buffer)?; let mut drawables = self.drawables.lock()?; let mut shm_pool = self.shm_pool.lock()?; - shm_pool.write(&vec![0x00000000; 800 * 800 * 2], 0); + shm_pool.write(0, 0, 800 * 800 * 2); for drawable in &mut *drawables { drawable.update(); drawable.draw(buffer, &mut *shm_pool); diff --git a/src/wayland/wl_client.rs b/src/wayland/wl_client.rs index 94dcf42..6fa02e9 100644 --- a/src/wayland/wl_client.rs +++ b/src/wayland/wl_client.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, env::var, error::Error, fmt::Debug, io::{IoSliceMut, Write}, os::unix::net::{AncillaryData, SocketAncillary, UnixStream}, sync::{atomic::{AtomicBool, AtomicU32, Ordering}, mpsc, Arc, Mutex, RwLock}, thread::{self}, u32}; -use crate::{graphics::{circle::Circle, drawable::Drawable, rectangle::Rectangle}, wayland::{shm, surface::UnsetErr, vec_utils::WlMessage, wl_shm::wl_buffer}}; +use crate::{graphics::{circle::Circle, drawable::{premultiply, Drawable}, rectangle::Rectangle}, wayland::{shm, surface::UnsetErr, vec_utils::WlMessage, wl_shm::wl_buffer}}; struct WlHeader { object: u32, @@ -68,9 +68,9 @@ impl WlClient { if let Ok(mut drawables) = arc_wl_client.drawables.lock() { drawables.push(Rectangle::new(50, 50, 300, 300, 16, 0xffff8800).into()); drawables.push(Rectangle::new(350, 50, 300, 300, 16, 0xffaa22aa).into()); - drawables.push(Circle::new(350, 80, 25, 0xff00ffff).into()); - drawables.push(Circle::new(350, 160, 25, 0x8800ffff).into()); - drawables.push(Circle::new(350, 240, 25, 0x0000ffff).into()); + drawables.push(Circle::new(350, 80, 25, premultiply(0xff00ffff)).into()); + drawables.push(Circle::new(350, 160, 25, premultiply(0x8800ffff)).into()); + drawables.push(Circle::new(350, 240, 25, premultiply(0x0000ffff)).into()); } arc_wl_client.running.store(true, Ordering::Relaxed);