render: Add BitmapFormat::Yuv420p and BitmapFormat::Yuva420p
This commit is contained in:
parent
322e17a5ab
commit
5f94476b2a
|
@ -3514,6 +3514,7 @@ dependencies = [
|
|||
"flate2",
|
||||
"gc-arena",
|
||||
"gif",
|
||||
"h263-rs-yuv",
|
||||
"jpeg-decoder",
|
||||
"lyon",
|
||||
"png",
|
||||
|
|
|
@ -146,6 +146,9 @@ impl<'gc> Bitmap<'gc> {
|
|||
match bitmap.format() {
|
||||
BitmapFormat::Rgba => true,
|
||||
BitmapFormat::Rgb => false,
|
||||
_ => unreachable!(
|
||||
"Bitmap objects can only be constructed from RGB or RGBA source bitmaps"
|
||||
),
|
||||
},
|
||||
pixels,
|
||||
);
|
||||
|
|
|
@ -23,6 +23,7 @@ gc-arena = { workspace = true }
|
|||
enum-map = "2.5.0"
|
||||
serde = { version = "1.0.159", features = ["derive"] }
|
||||
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]
|
||||
version = "0.3.0"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use gc_arena::Collect;
|
||||
use h263_rs_yuv::bt601::yuv420_to_rgba;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -89,14 +90,47 @@ impl Bitmap {
|
|||
|
||||
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;
|
||||
match self.format {
|
||||
BitmapFormat::Rgb => {
|
||||
self.data = self
|
||||
.data
|
||||
.chunks_exact(3)
|
||||
.flat_map(|rgb| [rgb[0], rgb[1], rgb[2], 255])
|
||||
.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
|
||||
}
|
||||
|
||||
|
@ -110,6 +144,20 @@ impl Bitmap {
|
|||
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]
|
||||
pub fn format(&self) -> BitmapFormat {
|
||||
self.format
|
||||
|
@ -129,6 +177,9 @@ impl Bitmap {
|
|||
let chunks = match self.format {
|
||||
BitmapFormat::Rgb => self.data.chunks_exact(3),
|
||||
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| {
|
||||
let red = chunk[0];
|
||||
|
@ -148,6 +199,12 @@ pub enum BitmapFormat {
|
|||
|
||||
/// 32-bit RGBA with premultiplied alpha.
|
||||
Rgba,
|
||||
|
||||
/// planar YUV 420
|
||||
Yuv420p,
|
||||
|
||||
/// planar YUV 420, premultiplied with alpha (RGB channels are to be clamped after conversion)
|
||||
Yuva420p,
|
||||
}
|
||||
|
||||
impl BitmapFormat {
|
||||
|
@ -156,6 +213,10 @@ impl BitmapFormat {
|
|||
match self {
|
||||
BitmapFormat::Rgb => width * height * 3,
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -988,9 +988,11 @@ impl RenderBackend for WebGlRenderBackend {
|
|||
}
|
||||
|
||||
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, BitmapError> {
|
||||
let format = match bitmap.format() {
|
||||
BitmapFormat::Rgb => Gl::RGB,
|
||||
BitmapFormat::Rgba => Gl::RGBA,
|
||||
let (format, bitmap) = match bitmap.format() {
|
||||
BitmapFormat::Rgb => (Gl::RGB, bitmap),
|
||||
BitmapFormat::Rgba | BitmapFormat::Yuv420p | BitmapFormat::Yuva420p => {
|
||||
(Gl::RGBA, bitmap.to_rgba())
|
||||
}
|
||||
};
|
||||
|
||||
let texture = self
|
||||
|
|
Loading…
Reference in New Issue