use std::marker::PhantomData;
use std::sync::Arc;
use std::time::Instant;
use super::held;
use super::source::{StateGuard, validate_size};
use super::stats::LiveImageSourceStats;
use super::{LiveImageError, LiveImageSource, LivePixelFormat, PixelRect, RejectedFrame};
use crate::sync::Ordering;
mod diff;
pub use diff::LiveImageDiffWriter;
#[derive(Clone)]
pub struct LiveImageWriter {
token: Arc<WriterToken>,
}
pub(crate) struct WriterToken {
source: LiveImageSource,
session: u64,
}
impl WriterToken {
pub(crate) fn new(source: LiveImageSource, session: u64) -> Self {
Self { source, session }
}
}
impl Drop for WriterToken {
fn drop(&mut self) {
self.source.shared.release_session(self.session);
}
}
struct NotSend(PhantomData<*const ()>);
unsafe impl Sync for NotSend {}
#[clippy::has_significant_drop]
#[must_use = "a transaction publishes only on `commit()`"]
pub struct LiveImageWriteGuard<'a> {
guard: Option<StateGuard<'a>>,
format: LivePixelFormat,
counted: bool,
_not_send: NotSend,
}
impl LiveImageWriter {
pub(crate) fn new(token: WriterToken) -> Self {
Self {
token: Arc::new(token),
}
}
fn source_ref(&self) -> &LiveImageSource {
&self.token.source
}
pub fn lock(&self) -> Result<LiveImageWriteGuard<'_>, LiveImageError> {
let shared = &*self.source_ref().shared;
shared.assert_not_held("LiveImageWriter::lock");
self.begin(shared.lock_state())
}
pub fn try_lock(&self) -> Result<LiveImageWriteGuard<'_>, LiveImageError> {
let shared = &*self.source_ref().shared;
let guard = shared.try_lock_state().ok_or(LiveImageError::WouldBlock)?;
self.begin(guard)
}
fn begin<'a>(
&'a self,
guard: StateGuard<'a>,
) -> Result<LiveImageWriteGuard<'a>, LiveImageError> {
if guard.state().session != self.token.session {
return Err(LiveImageError::Revoked);
}
held::enter(guard.shared().id);
Ok(LiveImageWriteGuard {
guard: Some(guard),
format: self.source_ref().format(),
counted: true,
_not_send: NotSend(PhantomData),
})
}
pub fn write_frame(
&self,
width: u32,
height: u32,
src: &[u8],
src_stride: usize,
) -> Result<u64, LiveImageError> {
validate_size(width, height)?;
let bpp = self.source_ref().format().bytes_per_pixel();
check_source(width, height, src, src_stride, bpp)?;
let mut guard = self.lock()?;
if let Err(error) = guard.resize(width, height) {
guard.counted = false;
return Err(error);
}
if let Err(error) = guard.write_rect(PixelRect::full(width, height), src, src_stride) {
guard.counted = false;
return Err(error);
}
Ok(guard.commit())
}
pub fn swap_frame(
&self,
width: u32,
height: u32,
stride: usize,
pixels: Vec<u8>,
) -> Result<Vec<u8>, RejectedFrame> {
let bpp = self.source_ref().format().bytes_per_pixel();
let check = validate_size(width, height)
.and_then(|()| check_source(width, height, &pixels, stride, bpp));
if let Err(error) = check {
return Err(RejectedFrame { error, pixels });
}
let mut guard = match self.lock() {
Ok(guard) => guard,
Err(error) => return Err(RejectedFrame { error, pixels }),
};
let written = (width as usize * bpp * height as usize) as u64;
let state = guard.state_mut();
let previous = std::mem::replace(&mut state.pixels, pixels);
if state.size != Some((width, height)) {
state.pending.resized = true;
}
state.size = Some((width, height));
state.stride = stride;
state.pending.force_full = true;
state.pending.wrote = true;
state.pending.damage.add(PixelRect::full(width, height));
guard.count_written(written);
guard.commit();
Ok(previous)
}
pub fn write_rect(
&self,
rect: PixelRect,
src: &[u8],
src_stride: usize,
) -> Result<u64, LiveImageError> {
let mut guard = self.lock()?;
if let Err(error) = guard.write_rect(rect, src, src_stride) {
guard.counted = false;
return Err(error);
}
Ok(guard.commit())
}
pub fn resize(&self, width: u32, height: u32) -> Result<u64, LiveImageError> {
let mut guard = self.lock()?;
if let Err(error) = guard.resize(width, height) {
guard.counted = false;
return Err(error);
}
Ok(guard.commit())
}
pub fn clear(&self) -> Result<(), LiveImageError> {
let mut guard = self.lock()?;
guard.counted = false;
let state = guard.state_mut();
state.free();
state.status = super::LiveImageStatus::Waiting;
Ok(())
}
pub fn is_observed(&self) -> bool {
self.source_ref().is_observed()
}
pub fn is_displayed(&self) -> bool {
self.source_ref().is_displayed()
}
pub fn source(&self) -> LiveImageSource {
self.source_ref().clone()
}
pub fn stats(&self) -> LiveImageSourceStats {
self.source_ref().stats()
}
}
impl std::fmt::Debug for LiveImageWriter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"LiveImageWriter(session {} of {:?})",
self.token.session,
self.source_ref()
)
}
}
fn check_source(
width: u32,
height: u32,
src: &[u8],
stride: usize,
bpp: usize,
) -> Result<(), LiveImageError> {
let row = width as usize * bpp;
if stride < row {
return Err(LiveImageError::StrideTooSmall { stride, min: row });
}
if height == 0 || width == 0 {
return Ok(());
}
let needed = (height as usize - 1)
.checked_mul(stride)
.and_then(|n| n.checked_add(row))
.unwrap_or(usize::MAX);
if src.len() < needed {
return Err(LiveImageError::BufferTooSmall {
len: src.len(),
needed,
});
}
Ok(())
}
impl LiveImageWriteGuard<'_> {
fn state(&self) -> &super::source::State {
self.guard
.as_ref()
.expect("open until commit or drop")
.state()
}
fn state_mut(&mut self) -> &mut super::source::State {
self.guard
.as_mut()
.expect("open until commit or drop")
.state_mut()
}
fn bpp(&self) -> usize {
self.format.bytes_per_pixel()
}
fn count_written(&self, bytes: u64) {
if let Some(guard) = &self.guard {
guard
.shared()
.counters
.bytes_written
.fetch_add(bytes, Ordering::Relaxed);
}
}
fn frame_for(&self, rect: PixelRect) -> Result<(u32, u32), LiveImageError> {
let (width, height) = self.state().size.ok_or(LiveImageError::NoFrame)?;
if !rect.fits_in(width, height) {
return Err(LiveImageError::OutOfBounds {
rect,
width,
height,
});
}
Ok((width, height))
}
fn mark(&mut self, rect: PixelRect) {
if rect.is_empty() {
return;
}
let pending = &mut self.state_mut().pending;
pending.damage.add(rect);
pending.wrote = true;
}
pub fn size(&self) -> Option<(u32, u32)> {
self.state().size
}
pub fn stride(&self) -> usize {
self.state().stride
}
pub fn format(&self) -> LivePixelFormat {
self.format
}
pub fn resize(&mut self, width: u32, height: u32) -> Result<(), LiveImageError> {
let bpp = self.bpp();
self.state_mut().resize(width, height, bpp)
}
pub fn write_rect(
&mut self,
rect: PixelRect,
src: &[u8],
src_stride: usize,
) -> Result<(), LiveImageError> {
self.frame_for(rect)?;
let bpp = self.bpp();
check_source(rect.width, rect.height, src, src_stride, bpp)?;
if rect.is_empty() {
return Ok(());
}
let row = rect.width as usize * bpp;
let state = self.state_mut();
let stride = state.stride;
if src_stride == stride && rect.x == 0 && row == stride {
let start = rect.y as usize * stride;
let len = rect.height as usize * stride;
state.pixels[start..start + len].copy_from_slice(&src[..len]);
} else {
for y in 0..rect.height as usize {
let dst = (rect.y as usize + y) * stride + rect.x as usize * bpp;
let from = y * src_stride;
state.pixels[dst..dst + row].copy_from_slice(&src[from..from + row]);
}
}
self.count_written((row * rect.height as usize) as u64);
self.mark(rect);
Ok(())
}
pub fn copy_within(
&mut self,
from: PixelRect,
to_x: u32,
to_y: u32,
) -> Result<(), LiveImageError> {
self.frame_for(from)?;
let to = PixelRect::new(to_x, to_y, from.width, from.height);
self.frame_for(to)?;
if from.is_empty() {
return Ok(());
}
let bpp = self.bpp();
let row = from.width as usize * bpp;
let state = self.state_mut();
let stride = state.stride;
let copy_row = |pixels: &mut Vec<u8>, y: usize| {
let src = (from.y as usize + y) * stride + from.x as usize * bpp;
let dst = (to.y as usize + y) * stride + to.x as usize * bpp;
pixels.copy_within(src..src + row, dst);
};
if to.y > from.y {
for y in (0..from.height as usize).rev() {
copy_row(&mut state.pixels, y);
}
} else {
for y in 0..from.height as usize {
copy_row(&mut state.pixels, y);
}
}
self.count_written((row * from.height as usize) as u64);
self.mark(to);
Ok(())
}
pub fn fill_rect(&mut self, rect: PixelRect, pixel: [u8; 4]) -> Result<(), LiveImageError> {
self.frame_for(rect)?;
if rect.is_empty() {
return Ok(());
}
let bpp = self.bpp();
let state = self.state_mut();
let stride = state.stride;
for y in 0..rect.height as usize {
let start = (rect.y as usize + y) * stride + rect.x as usize * bpp;
for px in state.pixels[start..start + rect.width as usize * bpp].chunks_exact_mut(bpp) {
px.copy_from_slice(&pixel[..bpp]);
}
}
self.count_written(rect.area() * bpp as u64);
self.mark(rect);
Ok(())
}
pub fn rows_mut(&mut self, rect: PixelRect) -> Result<RowsMut<'_>, LiveImageError> {
self.frame_for(rect)?;
self.mark(rect);
let bpp = self.bpp();
let state = self.state_mut();
let stride = state.stride;
Ok(RowsMut {
pixels: &mut state.pixels,
stride,
rect,
bpp,
})
}
pub fn pixels_mut(&mut self) -> &mut [u8] {
&mut self.state_mut().pixels
}
pub fn mark_dirty(&mut self, rect: PixelRect) {
let Some((width, height)) = self.state().size else {
return;
};
if let Some(clamped) = rect.intersect(&PixelRect::full(width, height)) {
self.mark(clamped);
}
}
pub fn mark_all_dirty(&mut self) {
if let Some((width, height)) = self.state().size {
self.mark(PixelRect::full(width, height));
self.state_mut().pending.force_full = true;
}
}
pub fn commit(mut self) -> u64 {
self.finish(true)
}
fn finish(&mut self, commit: bool) -> u64 {
let Some(mut guard) = self.guard.take() else {
return 0;
};
let shared = guard.shared();
let state = guard.state_mut();
let published = if commit {
state.publish(Instant::now())
} else {
if std::thread::panicking() {
state.pending.force_full = true;
}
if self.counted {
shared.counters.abandoned.fetch_add(1, Ordering::Relaxed);
}
None
};
for session in held::leave(shared.id) {
shared.apply_release(guard.state_mut(), session);
}
if let Some(generation) = published {
shared.publish_generation(generation);
}
let meta = shared.publish_meta(guard.state());
let generation = guard.state().generation;
drop(guard);
shared.notify(published.is_some(), meta);
generation
}
}
impl Drop for LiveImageWriteGuard<'_> {
fn drop(&mut self) {
self.finish(false);
}
}
impl std::fmt::Debug for LiveImageWriteGuard<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LiveImageWriteGuard")
.field("size", &self.size())
.field("format", &self.format)
.finish_non_exhaustive()
}
}
pub struct RowsMut<'a> {
pixels: &'a mut [u8],
stride: usize,
rect: PixelRect,
bpp: usize,
}
impl RowsMut<'_> {
pub fn width(&self) -> u32 {
self.rect.width
}
pub fn height(&self) -> u32 {
self.rect.height
}
pub fn row_mut(&mut self, y: u32) -> &mut [u8] {
assert!(
y < self.rect.height,
"row {y} of a rect {} rows tall",
self.rect.height
);
let start = (self.rect.y + y) as usize * self.stride + self.rect.x as usize * self.bpp;
&mut self.pixels[start..start + self.rect.width as usize * self.bpp]
}
}