Properly use premultiplied alpha

This commit is contained in:
2025-05-28 14:34:21 -07:00
parent 3bcccb2e87
commit 1eff2cd4f7
6 changed files with 69 additions and 54 deletions

View File

@@ -24,19 +24,18 @@ impl Drawable for Circle {
for l_row in 1..self.radius { for l_row in 1..self.radius {
let inner_diff = (((self.radius-1).pow(2) - l_row.pow(2)) as f64).sqrt(); 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 outer_diff = ((self.radius.pow(2) - l_row.pow(2)) as f64).sqrt();
let row: Vec<u32> = vec![self.color; 2*(inner_diff.floor() as usize)]; 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(&row, (self.y-l_row)*buffer.width + self.x - inner_diff.floor() as usize + buffer.offset); 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));
shm_pool.write(&row, (self.y+l_row-1)*buffer.width + self.x - inner_diff.floor() as usize + buffer.offset);
for l_col in (inner_diff.floor() as usize+1)..(outer_diff.ceil() 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 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; 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; 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; 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; 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);
} }
} }
} }

View File

@@ -1,34 +1,51 @@
use crate::wayland::{shm::ShmPool, wl_shm::wl_buffer}; use crate::wayland::{shm::ShmPool, wl_shm::wl_buffer};
pub fn rgb_blend(col1: u32, col2: u32, diff: f64) -> u32 { pub fn premultiply(color: u32) -> u32 {
let r1 = (col1 & 0x00ff0000) >> 16; let a = (color & 0xff000000) >> 24;
let g1 = (col1 & 0x0000ff00) >> 8; let r = (color & 0x00ff0000) >> 16;
let b1 = col1 & 0x000000ff; let g = (color & 0x0000ff00) >> 8;
let r2 = (col2 & 0x00ff0000) >> 16; let b = (color & 0x000000ff) >> 0;
let g2 = (col2 & 0x0000ff00) >> 8; let alpha = (a as f64 / 0xff as f64);
let b2 = col2 & 0x000000ff; (a << 24) +
let r3 = if r1 < r2 { (((r as f64*alpha) as u32) << 16) +
r1 + ((r2 - r1) as f64 * diff) as u32 (((g as f64*alpha) as u32) << 8) +
} else { (((b as f64*alpha) as u32) << 0)
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 color_blend(col1: u32, col2: u32, diff: f64) -> u32 { pub fn color_blend(col1: u32, col2: u32, diff: f64) -> u32 {
let a1 = (col1 & 0xff000000) >> 24; 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; 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 { pub trait Drawable : Send {

View File

@@ -24,24 +24,24 @@ impl Drawable for Rectangle {
fn draw(&self, buffer: &wl_buffer, shm_pool: &mut ShmPool) { 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 { 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 { for l_row in 1..self.radius {
let inner_diff = (((self.radius-1).pow(2) - l_row.pow(2)) as f64).sqrt(); 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 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(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(&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.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 { for l_col in inner_diff.floor() as usize+1..outer_diff.ceil() as usize {
// TODO: handle error from read_pixel // TODO: handle error from read_pixel
let distance = ((l_row.pow(2) + l_col.pow(2)) as f64).sqrt(); 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; 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; 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; 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; 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);
} }
} }
} }

View File

@@ -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 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)] #[derive(Clone)]
pub struct ShmPool { pub struct ShmPool {
pub fd: i32, pub fd: i32,
@@ -69,25 +71,22 @@ impl ShmPool {
Ok(()) Ok(())
} }
pub fn write(&mut self, data: &Vec<u32>, offset: usize) { pub fn write(&mut self, color: u32, offset: usize, len: usize) {
if offset + data.len() >= self.size { if offset + len/4 >= self.size {
return; return;
} }
unsafe { for i in offset..offset+len { unsafe {
std::ptr::copy_nonoverlapping( *((self.addr as *mut u32).offset(i as isize))
data.as_ptr() as *const u32, // src: data as *const u32 = color_over(color, self.read_pixel(i).unwrap());
self.addr.offset(4*offset as isize) as *mut u32, // dst: ShmPool address as *mut u32 }}
data.len()
);
}
} }
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 // TODO: Return error if out of bounds
if offset + 3 > self.size { if offset + 3 > self.size {
return; 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<u32> { pub fn read_pixel(&self, offset: usize) -> Option<u32> {

View File

@@ -121,7 +121,7 @@ impl WlClient {
self.wl_surface_attach(buffer)?; self.wl_surface_attach(buffer)?;
let mut drawables = self.drawables.lock()?; let mut drawables = self.drawables.lock()?;
let mut shm_pool = self.shm_pool.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 { for drawable in &mut *drawables {
drawable.update(); drawable.update();
drawable.draw(buffer, &mut *shm_pool); drawable.draw(buffer, &mut *shm_pool);

View File

@@ -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 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 { struct WlHeader {
object: u32, object: u32,
@@ -68,9 +68,9 @@ impl WlClient {
if let Ok(mut drawables) = arc_wl_client.drawables.lock() { 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(50, 50, 300, 300, 16, 0xffff8800).into());
drawables.push(Rectangle::new(350, 50, 300, 300, 16, 0xffaa22aa).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, 80, 25, premultiply(0xff00ffff)).into());
drawables.push(Circle::new(350, 160, 25, 0x8800ffff).into()); drawables.push(Circle::new(350, 160, 25, premultiply(0x8800ffff)).into());
drawables.push(Circle::new(350, 240, 25, 0x0000ffff).into()); drawables.push(Circle::new(350, 240, 25, premultiply(0x0000ffff)).into());
} }
arc_wl_client.running.store(true, Ordering::Relaxed); arc_wl_client.running.store(true, Ordering::Relaxed);