2022-09-24 03:14:49 +00:00
|
|
|
use gc_arena::Collect;
|
2022-09-30 21:25:54 +00:00
|
|
|
use std::fmt::Debug;
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
use downcast_rs::{impl_downcast, Downcast};
|
2023-03-27 12:12:18 +00:00
|
|
|
use swf::{Rectangle, Twips};
|
2022-09-30 21:25:54 +00:00
|
|
|
|
2022-10-20 18:31:55 +00:00
|
|
|
use crate::backend::RenderBackend;
|
|
|
|
|
2023-03-15 23:26:23 +00:00
|
|
|
#[derive(Clone, Debug, Collect)]
|
|
|
|
#[collect(require_static)]
|
2022-09-30 21:25:54 +00:00
|
|
|
pub struct BitmapHandle(pub Arc<dyn BitmapHandleImpl>);
|
|
|
|
|
|
|
|
pub trait BitmapHandleImpl: Downcast + Debug {}
|
|
|
|
impl_downcast!(BitmapHandleImpl);
|
2022-08-13 22:21:20 +00:00
|
|
|
|
2022-09-30 21:25:54 +00:00
|
|
|
/// Info returned by the `register_bitmap` methods.
|
2023-03-15 23:26:23 +00:00
|
|
|
#[derive(Clone, Debug, Collect)]
|
|
|
|
#[collect(require_static)]
|
2022-08-13 22:21:20 +00:00
|
|
|
pub struct BitmapInfo {
|
|
|
|
pub handle: BitmapHandle,
|
|
|
|
pub width: u16,
|
|
|
|
pub height: u16,
|
|
|
|
}
|
|
|
|
|
2022-10-20 18:31:55 +00:00
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
|
|
pub struct BitmapSize {
|
|
|
|
pub width: u16,
|
|
|
|
pub height: u16,
|
|
|
|
}
|
|
|
|
|
2022-08-13 22:21:20 +00:00
|
|
|
/// 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 {
|
2022-10-20 18:31:55 +00:00
|
|
|
fn bitmap_size(&self, id: u16) -> Option<BitmapSize>;
|
|
|
|
fn bitmap_handle(&self, id: u16, renderer: &mut dyn RenderBackend) -> Option<BitmapHandle>;
|
2022-08-13 22:21:20 +00:00
|
|
|
}
|
|
|
|
|
2023-03-13 00:00:42 +00:00
|
|
|
pub type RgbaBufRead<'a> = Box<dyn FnOnce(&[u8], u32) + 'a>;
|
|
|
|
|
2023-01-21 21:08:04 +00:00
|
|
|
pub trait SyncHandle: Downcast + Debug {
|
|
|
|
/// Retrieves the rendered pixels from a previous `render_offscreen` call
|
2023-03-13 00:00:42 +00:00
|
|
|
fn retrieve_offscreen_texture(
|
|
|
|
self: Box<Self>,
|
|
|
|
with_rgba: RgbaBufRead,
|
|
|
|
) -> Result<(), crate::error::Error>;
|
2023-01-21 21:08:04 +00:00
|
|
|
}
|
2023-01-07 16:08:23 +00:00
|
|
|
impl_downcast!(SyncHandle);
|
|
|
|
|
2023-01-21 21:08:04 +00:00
|
|
|
impl Clone for Box<dyn SyncHandle> {
|
|
|
|
fn clone(&self) -> Box<dyn SyncHandle> {
|
|
|
|
panic!("SyncHandle should have been consumed before clone() is called!")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-13 22:21:20 +00:00
|
|
|
/// Decoded bitmap data from an SWF tag.
|
2022-09-24 03:14:49 +00:00
|
|
|
#[derive(Clone, Debug, Collect)]
|
|
|
|
#[collect(require_static)]
|
2022-08-13 22:21:20 +00:00
|
|
|
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!(
|
2022-08-13 22:21:20 +00:00
|
|
|
"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
|
|
|
|
}
|
|
|
|
|
2022-10-19 20:30:23 +00:00
|
|
|
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])
|
|
|
|
})
|
2022-08-13 22:21:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-22 14:41:31 +00:00
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct PixelRegion {
|
2023-03-31 13:51:35 +00:00
|
|
|
pub x_min: u32,
|
|
|
|
pub y_min: u32,
|
|
|
|
pub x_max: u32,
|
|
|
|
pub y_max: u32,
|
2023-03-22 14:41:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2023-03-31 13:51:35 +00:00
|
|
|
x_min: min.0.to_pixels().floor().max(0.0) as u32,
|
|
|
|
y_min: min.1.to_pixels().floor().max(0.0) as u32,
|
|
|
|
x_max: max.0.to_pixels().ceil().max(0.0) as u32,
|
|
|
|
y_max: max.1.to_pixels().ceil().max(0.0) as u32,
|
2023-03-22 14:41:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-23 18:48:28 +00:00
|
|
|
pub fn for_region_i32(x: i32, y: i32, width: i32, height: i32) -> Self {
|
2023-03-24 16:37:17 +00:00
|
|
|
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 {
|
2023-03-31 13:51:35 +00:00
|
|
|
x_min: min.0.max(0) as u32,
|
|
|
|
y_min: min.1.max(0) as u32,
|
|
|
|
x_max: max.0.max(0) as u32,
|
|
|
|
y_max: max.1.max(0) as u32,
|
2023-03-24 16:37:17 +00:00
|
|
|
}
|
2023-03-23 18:48:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2023-03-31 13:51:35 +00:00
|
|
|
x_min: min.0,
|
|
|
|
y_min: min.1,
|
|
|
|
x_max: max.0,
|
|
|
|
y_max: max.1,
|
2023-03-23 18:48:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-22 15:46:52 +00:00
|
|
|
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 {
|
2023-03-31 13:51:35 +00:00
|
|
|
x_min: min.0,
|
|
|
|
y_min: min.1,
|
|
|
|
x_max: max.0,
|
|
|
|
y_max: max.1,
|
2023-03-22 15:46:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-22 14:41:31 +00:00
|
|
|
pub fn for_whole_size(width: u32, height: u32) -> Self {
|
|
|
|
Self {
|
2023-03-31 13:51:35 +00:00
|
|
|
x_min: 0,
|
|
|
|
y_min: 0,
|
|
|
|
x_max: width,
|
|
|
|
y_max: height,
|
2023-03-22 14:41:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-22 15:46:52 +00:00
|
|
|
pub fn for_pixel(x: u32, y: u32) -> Self {
|
|
|
|
Self {
|
2023-03-31 13:51:35 +00:00
|
|
|
x_min: x,
|
|
|
|
y_min: y,
|
|
|
|
x_max: x + 1,
|
|
|
|
y_max: y + 1,
|
2023-03-22 15:46:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-22 14:41:31 +00:00
|
|
|
pub fn clamp(&mut self, width: u32, height: u32) {
|
2023-03-31 13:51:35 +00:00
|
|
|
self.x_min = self.x_min.min(width);
|
|
|
|
self.y_min = self.y_min.min(height);
|
|
|
|
self.x_max = self.x_max.min(width);
|
|
|
|
self.y_max = self.y_max.min(height);
|
2023-03-22 14:41:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn union(&mut self, other: PixelRegion) {
|
2023-03-31 13:51:35 +00:00
|
|
|
self.x_min = self.x_min.min(other.x_min);
|
|
|
|
self.y_min = self.y_min.min(other.y_min);
|
|
|
|
self.x_max = self.x_max.max(other.x_max);
|
|
|
|
self.y_max = self.y_max.max(other.y_max);
|
2023-03-22 14:41:31 +00:00
|
|
|
}
|
|
|
|
|
2023-03-22 15:46:52 +00:00
|
|
|
pub fn encompass(&mut self, x: u32, y: u32) {
|
2023-03-31 13:51:35 +00:00
|
|
|
self.x_min = self.x_min.min(x);
|
|
|
|
self.y_min = self.y_min.min(y);
|
|
|
|
self.x_max = self.x_max.max(x + 1);
|
|
|
|
self.y_max = self.y_max.max(y + 1);
|
2023-03-22 15:46:52 +00:00
|
|
|
}
|
|
|
|
|
2023-03-24 16:37:17 +00:00
|
|
|
pub fn intersects(&self, other: PixelRegion) -> bool {
|
2023-03-31 13:51:35 +00:00
|
|
|
self.x_min <= other.x_max
|
|
|
|
&& self.x_max >= other.x_min
|
|
|
|
&& self.y_min <= other.y_max
|
|
|
|
&& self.y_max >= other.y_min
|
2023-03-24 16:37:17 +00:00
|
|
|
}
|
|
|
|
|
2023-03-22 14:41:31 +00:00
|
|
|
pub fn width(&self) -> u32 {
|
2023-03-31 13:51:35 +00:00
|
|
|
self.x_max - self.x_min
|
2023-03-22 14:41:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn height(&self) -> u32 {
|
2023-03-31 13:51:35 +00:00
|
|
|
self.y_max - self.y_min
|
2023-03-22 14:41:31 +00:00
|
|
|
}
|
|
|
|
}
|
2023-03-27 12:12:18 +00:00
|
|
|
|
|
|
|
impl From<Rectangle<Twips>> for PixelRegion {
|
|
|
|
fn from(value: Rectangle<Twips>) -> Self {
|
|
|
|
Self {
|
2023-03-31 13:51:35 +00:00
|
|
|
x_min: value.x_min.to_pixels().floor().max(0.0) as u32,
|
|
|
|
y_min: value.y_min.to_pixels().floor().max(0.0) as u32,
|
|
|
|
x_max: value.x_max.to_pixels().ceil().max(0.0) as u32,
|
|
|
|
y_max: value.y_max.to_pixels().ceil().max(0.0) as u32,
|
2023-03-27 12:12:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|