render: Add BitmapFormat::Yuv420p and BitmapFormat::Yuva420p

This commit is contained in:
TÖRÖK Attila 2023-03-19 21:08:21 +01:00 committed by Mike Welsh
parent 322e17a5ab
commit 5f94476b2a
5 changed files with 78 additions and 10 deletions

1
Cargo.lock generated
View File

@ -3514,6 +3514,7 @@ dependencies = [
"flate2", "flate2",
"gc-arena", "gc-arena",
"gif", "gif",
"h263-rs-yuv",
"jpeg-decoder", "jpeg-decoder",
"lyon", "lyon",
"png", "png",

View File

@ -146,6 +146,9 @@ impl<'gc> Bitmap<'gc> {
match bitmap.format() { match bitmap.format() {
BitmapFormat::Rgba => true, BitmapFormat::Rgba => true,
BitmapFormat::Rgb => false, BitmapFormat::Rgb => false,
_ => unreachable!(
"Bitmap objects can only be constructed from RGB or RGBA source bitmaps"
),
}, },
pixels, pixels,
); );

View File

@ -23,6 +23,7 @@ gc-arena = { workspace = true }
enum-map = "2.5.0" enum-map = "2.5.0"
serde = { version = "1.0.159", features = ["derive"] } serde = { version = "1.0.159", features = ["derive"] }
clap = { version = "4.2.1", features = ["derive"], optional = true } clap = { version = "4.2.1", features = ["derive"], optional = true }
h263-rs-yuv = { git = "https://github.com/ruffle-rs/h263-rs", rev = "d5d78eb251c1ce1f1da57c63db14f0fdc77a4b36"}
[dependencies.jpeg-decoder] [dependencies.jpeg-decoder]
version = "0.3.0" version = "0.3.0"

View File

@ -1,4 +1,5 @@
use gc_arena::Collect; use gc_arena::Collect;
use h263_rs_yuv::bt601::yuv420_to_rgba;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
@ -89,14 +90,47 @@ impl Bitmap {
pub fn to_rgba(mut self) -> Self { pub fn to_rgba(mut self) -> Self {
// Converts this bitmap to RGBA, if it is not already. // Converts this bitmap to RGBA, if it is not already.
if self.format == BitmapFormat::Rgb { match self.format {
self.data = self BitmapFormat::Rgb => {
.data self.data = self
.chunks_exact(3) .data
.flat_map(|rgb| [rgb[0], rgb[1], rgb[2], 255]) .chunks_exact(3)
.collect(); .flat_map(|rgb| [rgb[0], rgb[1], rgb[2], 255])
self.format = BitmapFormat::Rgba; .collect();
}
BitmapFormat::Rgba => {} // no-op
BitmapFormat::Yuv420p => {
let luma_len = (self.width * self.height) as usize;
let chroma_len = (self.chroma_width() * self.chroma_height()) as usize;
let y = &self.data[0..luma_len];
let u = &self.data[luma_len..luma_len + chroma_len];
let v = &self.data[luma_len + chroma_len..luma_len + 2 * chroma_len];
self.data = yuv420_to_rgba(y, u, v, self.width as usize);
}
BitmapFormat::Yuva420p => {
let luma_len = (self.width * self.height) as usize;
let chroma_len = (self.chroma_width() * self.chroma_height()) as usize;
let y = &self.data[0..luma_len];
let u = &self.data[luma_len..luma_len + chroma_len];
let v = &self.data[luma_len + chroma_len..luma_len + chroma_len + chroma_len];
let a = &self.data[luma_len + 2 * chroma_len..2 * luma_len + 2 * chroma_len];
let rgba = yuv420_to_rgba(y, u, v, self.width as usize);
// RGB components need to be clamped to alpha to avoid invalid premultiplied colors
self.data = rgba
.chunks_exact(4)
.zip(a)
.flat_map(|(rgba, a)| [rgba[0].min(*a), rgba[1].min(*a), rgba[2].min(*a), *a])
.collect()
}
} }
self.format = BitmapFormat::Rgba;
self self
} }
@ -110,6 +144,20 @@ impl Bitmap {
self.height self.height
} }
pub fn chroma_width(&self) -> u32 {
match self.format {
BitmapFormat::Yuv420p | BitmapFormat::Yuva420p => (self.width + 1) / 2,
_ => unreachable!("Can't get chroma width for non-YUV bitmap"),
}
}
pub fn chroma_height(&self) -> u32 {
match self.format {
BitmapFormat::Yuv420p | BitmapFormat::Yuva420p => (self.height + 1) / 2,
_ => unreachable!("Can't get chroma height for non-YUV bitmap"),
}
}
#[inline] #[inline]
pub fn format(&self) -> BitmapFormat { pub fn format(&self) -> BitmapFormat {
self.format self.format
@ -129,6 +177,9 @@ impl Bitmap {
let chunks = match self.format { let chunks = match self.format {
BitmapFormat::Rgb => self.data.chunks_exact(3), BitmapFormat::Rgb => self.data.chunks_exact(3),
BitmapFormat::Rgba => self.data.chunks_exact(4), BitmapFormat::Rgba => self.data.chunks_exact(4),
_ => unimplemented!(
"Can't iterate over non-RGB(A) bitmaps as colors, convert with `to_rgba` first"
),
}; };
chunks.map(|chunk| { chunks.map(|chunk| {
let red = chunk[0]; let red = chunk[0];
@ -148,6 +199,12 @@ pub enum BitmapFormat {
/// 32-bit RGBA with premultiplied alpha. /// 32-bit RGBA with premultiplied alpha.
Rgba, Rgba,
/// planar YUV 420
Yuv420p,
/// planar YUV 420, premultiplied with alpha (RGB channels are to be clamped after conversion)
Yuva420p,
} }
impl BitmapFormat { impl BitmapFormat {
@ -156,6 +213,10 @@ impl BitmapFormat {
match self { match self {
BitmapFormat::Rgb => width * height * 3, BitmapFormat::Rgb => width * height * 3,
BitmapFormat::Rgba => width * height * 4, BitmapFormat::Rgba => width * height * 4,
BitmapFormat::Yuv420p => width * height + ((width + 1) / 2) * ((height + 1) / 2) * 2,
BitmapFormat::Yuva420p => {
width * height * 2 + ((width + 1) / 2) * ((height + 1) / 2) * 2
}
} }
} }
} }

View File

@ -988,9 +988,11 @@ impl RenderBackend for WebGlRenderBackend {
} }
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, BitmapError> { fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, BitmapError> {
let format = match bitmap.format() { let (format, bitmap) = match bitmap.format() {
BitmapFormat::Rgb => Gl::RGB, BitmapFormat::Rgb => (Gl::RGB, bitmap),
BitmapFormat::Rgba => Gl::RGBA, BitmapFormat::Rgba | BitmapFormat::Yuv420p | BitmapFormat::Yuva420p => {
(Gl::RGBA, bitmap.to_rgba())
}
}; };
let texture = self let texture = self