ruffle/render/src/bitmap.rs

304 lines
8.4 KiB
Rust
Raw Normal View History

use gc_arena::Collect;
use std::fmt::Debug;
use std::sync::Arc;
use downcast_rs::{impl_downcast, Downcast};
use swf::{Rectangle, Twips};
use crate::backend::RenderBackend;
#[derive(Clone, Debug, Collect)]
#[collect(require_static)]
pub struct BitmapHandle(pub Arc<dyn BitmapHandleImpl>);
pub trait BitmapHandleImpl: Downcast + Debug {}
impl_downcast!(BitmapHandleImpl);
/// Info returned by the `register_bitmap` methods.
#[derive(Clone, Debug, Collect)]
#[collect(require_static)]
pub struct BitmapInfo {
pub handle: BitmapHandle,
pub width: u16,
pub height: u16,
}
#[derive(Copy, Clone, Debug)]
pub struct BitmapSize {
pub width: u16,
pub height: u16,
}
/// An object that returns a bitmap given an ID.
///
/// This is used by render backends to get the bitmap used in a bitmap fill.
/// For movie libraries, this will return the bitmap with the given character ID.
pub trait BitmapSource {
fn bitmap_size(&self, id: u16) -> Option<BitmapSize>;
fn bitmap_handle(&self, id: u16, renderer: &mut dyn RenderBackend) -> Option<BitmapHandle>;
}
pub type RgbaBufRead<'a> = Box<dyn FnOnce(&[u8], u32) + 'a>;
pub trait SyncHandle: Downcast + Debug {
/// Retrieves the rendered pixels from a previous `render_offscreen` call
fn retrieve_offscreen_texture(
self: Box<Self>,
with_rgba: RgbaBufRead,
) -> Result<(), crate::error::Error>;
}
impl_downcast!(SyncHandle);
impl Clone for Box<dyn SyncHandle> {
fn clone(&self) -> Box<dyn SyncHandle> {
panic!("SyncHandle should have been consumed before clone() is called!")
}
}
/// Decoded bitmap data from an SWF tag.
#[derive(Clone, Debug, Collect)]
#[collect(require_static)]
pub struct Bitmap {
width: u32,
height: u32,
format: BitmapFormat,
data: Vec<u8>,
}
impl Bitmap {
/// Ensures that `data` is the correct size for the given `width` and `height`.
pub fn new(width: u32, height: u32, format: BitmapFormat, mut data: Vec<u8>) -> Self {
// If the size is incorrect, either we screwed up or the decoder screwed up.
let expected_len = width as usize * height as usize * format.bytes_per_pixel();
if data.len() != expected_len {
2023-02-02 14:47:20 +00:00
tracing::warn!(
"Incorrect bitmap data size, expected {} bytes, got {}",
data.len(),
expected_len
);
// Truncate or zero pad to the expected size.
data.resize(expected_len, 0);
}
Self {
width,
height,
format,
data,
}
}
pub fn to_rgba(mut self) -> Self {
// Converts this bitmap to RGBA, if it is not already.
if self.format == BitmapFormat::Rgb {
self.data = self
.data
.chunks_exact(3)
.flat_map(|rgb| [rgb[0], rgb[1], rgb[2], 255])
.collect();
self.format = BitmapFormat::Rgba;
}
self
}
#[inline]
pub fn width(&self) -> u32 {
self.width
}
#[inline]
pub fn height(&self) -> u32 {
self.height
}
#[inline]
pub fn format(&self) -> BitmapFormat {
self.format
}
#[inline]
pub fn data(&self) -> &[u8] {
&self.data
}
#[inline]
pub fn data_mut(&mut self) -> &mut [u8] {
&mut self.data
}
pub fn as_colors(&self) -> impl Iterator<Item = i32> + '_ {
let chunks = match self.format {
BitmapFormat::Rgb => self.data.chunks_exact(3),
BitmapFormat::Rgba => self.data.chunks_exact(4),
};
chunks.map(|chunk| {
let red = chunk[0];
let green = chunk[1];
let blue = chunk[2];
let alpha = chunk.get(3).copied().unwrap_or(0xFF);
i32::from_le_bytes([blue, green, red, alpha])
})
}
}
/// The pixel format of the bitmap data.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BitmapFormat {
/// 24-bit RGB.
Rgb,
/// 32-bit RGBA with premultiplied alpha.
Rgba,
}
impl BitmapFormat {
#[inline]
pub fn bytes_per_pixel(self) -> usize {
match self {
BitmapFormat::Rgb => 3,
BitmapFormat::Rgba => 4,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct PixelRegion {
pub min_x: u32,
pub min_y: u32,
pub max_x: u32,
pub max_y: u32,
}
impl PixelRegion {
pub fn encompassing_twips(a: (Twips, Twips), b: (Twips, Twips)) -> Self {
// Figure out what our two ranges are
let (min, max) = ((a.0.min(b.0), a.1.min(b.1)), (a.0.max(b.0), a.1.max(b.1)));
// Increase max by one pixel as we've calculated the *encompassed* max
let max = (
max.0 + Twips::from_pixels_i32(1),
max.1 + Twips::from_pixels_i32(1),
);
// Make sure we're never going below 0
Self {
min_x: min.0.to_pixels().floor().max(0.0) as u32,
min_y: min.1.to_pixels().floor().max(0.0) as u32,
max_x: max.0.to_pixels().ceil().max(0.0) as u32,
max_y: max.1.to_pixels().ceil().max(0.0) as u32,
}
}
pub fn for_region_i32(x: i32, y: i32, width: i32, height: i32) -> Self {
let a = (x, y);
let b = (x.saturating_add(width), y.saturating_add(height));
let (min, max) = ((a.0.min(b.0), a.1.min(b.1)), (a.0.max(b.0), a.1.max(b.1)));
Self {
min_x: min.0.max(0) as u32,
min_y: min.1.max(0) as u32,
max_x: max.0.max(0) as u32,
max_y: max.1.max(0) as u32,
}
}
pub fn for_region(x: u32, y: u32, width: u32, height: u32) -> Self {
let a = (x, y);
let b = (x.saturating_add(width), y.saturating_add(height));
let (min, max) = ((a.0.min(b.0), a.1.min(b.1)), (a.0.max(b.0), a.1.max(b.1)));
Self {
min_x: min.0,
min_y: min.1,
max_x: max.0,
max_y: max.1,
}
}
pub fn encompassing_pixels_i32(a: (i32, i32), b: (i32, i32)) -> Self {
Self::encompassing_pixels(
(a.0.max(0) as u32, a.1.max(0) as u32),
(b.0.max(0) as u32, b.1.max(0) as u32),
)
}
pub fn encompassing_pixels(a: (u32, u32), b: (u32, u32)) -> Self {
// Figure out what our two ranges are
let (min, max) = ((a.0.min(b.0), a.1.min(b.1)), (a.0.max(b.0), a.1.max(b.1)));
// Increase max by one pixel as we've calculated the *encompassed* max
let max = (max.0.saturating_add(1), max.1.saturating_add(1));
Self {
min_x: min.0,
min_y: min.1,
max_x: max.0,
max_y: max.1,
}
}
pub fn for_whole_size(width: u32, height: u32) -> Self {
Self {
min_x: 0,
min_y: 0,
max_x: width,
max_y: height,
}
}
pub fn for_pixel(x: u32, y: u32) -> Self {
Self {
min_x: x,
min_y: y,
max_x: x + 1,
max_y: y + 1,
}
}
pub fn clamp(&mut self, width: u32, height: u32) {
self.min_x = self.min_x.min(width);
self.min_y = self.min_y.min(height);
self.max_x = self.max_x.min(width);
self.max_y = self.max_y.min(height);
}
pub fn union(&mut self, other: PixelRegion) {
self.min_x = self.min_x.min(other.min_x);
self.min_y = self.min_y.min(other.min_y);
self.max_x = self.max_x.max(other.max_x);
self.max_y = self.max_y.max(other.max_y);
}
pub fn encompass(&mut self, x: u32, y: u32) {
self.min_x = self.min_x.min(x);
self.min_y = self.min_y.min(y);
self.max_x = self.max_x.max(x + 1);
self.max_y = self.max_y.max(y + 1);
}
pub fn intersects(&self, other: PixelRegion) -> bool {
self.min_x <= other.max_x
&& self.max_x >= other.min_x
&& self.min_y <= other.max_y
&& self.max_y >= other.min_y
}
pub fn width(&self) -> u32 {
self.max_x - self.min_x
}
pub fn height(&self) -> u32 {
self.max_y - self.min_y
}
}
impl From<Rectangle<Twips>> for PixelRegion {
fn from(value: Rectangle<Twips>) -> Self {
Self {
min_x: value.x_min.to_pixels().floor().max(0.0) as u32,
min_y: value.y_min.to_pixels().floor().max(0.0) as u32,
max_x: value.x_max.to_pixels().ceil().max(0.0) as u32,
max_y: value.y_max.to_pixels().ceil().max(0.0) as u32,
}
}
}