render: Made render use a real Error enum and not generic box<error>

This commit is contained in:
= 2022-08-19 01:34:35 +02:00 committed by Nathan Adams
parent 726217c6c2
commit 03eb769a33
11 changed files with 90 additions and 45 deletions

2
Cargo.lock generated
View File

@ -3110,6 +3110,8 @@ dependencies = [
"png",
"smallvec",
"swf",
"thiserror",
"wasm-bindgen",
]
[[package]]

View File

@ -124,6 +124,9 @@ pub enum Error {
#[error("Invalid SWF: {0}")]
InvalidSwf(#[from] crate::tag_utils::Error),
#[error("Invalid bitmap")]
InvalidBitmap(#[from] ruffle_render::error::Error),
#[error("Unexpected content of type {1}, expected {0}")]
UnexpectedData(ContentType, ContentType),

View File

@ -15,6 +15,8 @@ flate2 = "1.0.24"
smallvec = { version = "1.9.0", features = ["union"] }
downcast-rs = "1.2.0"
lyon = { version = "1.0.0", optional = true }
thiserror = "1.0"
wasm-bindgen = { version = "=0.2.82", optional = true }
[dependencies.jpeg-decoder]
version = "0.2.6"
@ -25,4 +27,5 @@ approx = "0.5.1"
[features]
default = []
tessellator = ["lyon"]
tessellator = ["lyon"]
web = ["wasm-bindgen"]

View File

@ -11,7 +11,7 @@ log = "0.4"
ruffle_web_common = { path = "../../web/common" }
wasm-bindgen = "=0.2.82"
fnv = "1.0.7"
ruffle_render = { path = ".." }
ruffle_render = { path = "..", features = ["web"] }
swf = { path = "../../swf" }
[dependencies.web-sys]

View File

@ -3,12 +3,13 @@ use ruffle_render::backend::null::NullBitmapSource;
use ruffle_render::backend::{RenderBackend, ShapeHandle, ViewportDimensions};
use ruffle_render::bitmap::{Bitmap, BitmapFormat, BitmapHandle, BitmapSource};
use ruffle_render::color_transform::ColorTransform;
use ruffle_render::error::Error as BitmapError;
use ruffle_render::matrix::Matrix;
use ruffle_render::shape_utils::{DistilledShape, DrawCommand, LineScaleMode, LineScales};
use ruffle_render::transform::Transform;
use ruffle_web_common::{JsError, JsResult};
use swf::{BlendMode, Color};
use wasm_bindgen::{Clamped, JsCast};
use wasm_bindgen::{Clamped, JsCast, JsValue};
use web_sys::{
CanvasGradient, CanvasPattern, CanvasRenderingContext2d, CanvasWindingRule, DomMatrix, Element,
HtmlCanvasElement, ImageData, Path2d,
@ -139,7 +140,7 @@ struct BitmapData {
impl BitmapData {
/// Puts the image data into a newly created <canvas>, and caches it.
fn new(bitmap: Bitmap) -> Result<Self, Error> {
fn new(bitmap: Bitmap) -> Result<Self, JsValue> {
let bitmap = bitmap.to_rgba();
let image_data =
ImageData::new_with_u8_clamped_array(Clamped(bitmap.data()), bitmap.width())
@ -736,15 +737,15 @@ impl RenderBackend for WebCanvasRenderBackend {
bitmap.get_pixels()
}
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, Error> {
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, BitmapError> {
let handle = self.next_bitmap_handle;
self.next_bitmap_handle = BitmapHandle(self.next_bitmap_handle.0 + 1);
let bitmap_data = BitmapData::new(bitmap)?;
let bitmap_data = BitmapData::new(bitmap).map_err(BitmapError::JavascriptError)?;
self.bitmaps.insert(handle, bitmap_data);
Ok(handle)
}
fn unregister_bitmap(&mut self, bitmap: BitmapHandle) -> Result<(), Error> {
fn unregister_bitmap(&mut self, bitmap: BitmapHandle) -> Result<(), BitmapError> {
self.bitmaps.remove(&bitmap);
Ok(())
}
@ -755,12 +756,13 @@ impl RenderBackend for WebCanvasRenderBackend {
width: u32,
height: u32,
rgba: Vec<u8>,
) -> Result<BitmapHandle, Error> {
) -> Result<BitmapHandle, BitmapError> {
// TODO: Could be optimized to a single put_image_data call
// in case it is already stored as a canvas+context.
self.bitmaps.insert(
handle,
BitmapData::new(Bitmap::new(width, height, BitmapFormat::Rgba, rgba))?,
BitmapData::new(Bitmap::new(width, height, BitmapFormat::Rgba, rgba))
.map_err(BitmapError::JavascriptError)?,
);
Ok(handle)
}

View File

@ -1 +1,32 @@
pub type Error = Box<dyn std::error::Error>;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("Bitmap texture is larger than the rendering device supports")]
TooLarge,
#[error("Unknown bitmap format")]
UnknownType,
#[error("Invalid ZLIB compression")]
InvalidZlibCompression,
#[error("Invalid JPEG")]
InvalidJpeg(#[from] jpeg_decoder::Error),
#[error("Invalid PNG")]
InvalidPng(#[from] png::DecodingError),
#[error("Invalid GIF")]
InvalidGif(#[from] gif::DecodingError),
#[error("Empty GIF")]
EmptyGif,
#[error("Unsupported DefineBitsLossless{0} format {1:?}")]
UnsupportedLosslessFormat(u8, swf::BitmapFormat),
#[cfg(feature = "web")]
#[error("Javascript error")]
JavascriptError(wasm_bindgen::JsValue),
}

View File

@ -38,7 +38,7 @@ pub fn decode_define_bits_jpeg(data: &[u8], alpha_data: Option<&[u8]>) -> Result
JpegTagFormat::Jpeg => decode_jpeg(data, alpha_data),
JpegTagFormat::Png => decode_png(data),
JpegTagFormat::Gif => decode_gif(data),
JpegTagFormat::Unknown => Err("Unknown bitmap data format".into()),
JpegTagFormat::Unknown => Err(Error::UnknownType),
}
}
@ -87,15 +87,14 @@ pub fn remove_invalid_jpeg_data(mut data: &[u8]) -> Cow<[u8]> {
/// Decodes a JPEG with optional alpha data.
/// The decoded bitmap will have pre-multiplied alpha.
fn decode_jpeg(
jpeg_data: &[u8],
alpha_data: Option<&[u8]>,
) -> Result<Bitmap, Box<dyn std::error::Error>> {
fn decode_jpeg(jpeg_data: &[u8], alpha_data: Option<&[u8]>) -> Result<Bitmap, Error> {
let jpeg_data = remove_invalid_jpeg_data(jpeg_data);
let mut decoder = jpeg_decoder::Decoder::new(&jpeg_data[..]);
decoder.read_info()?;
let metadata = decoder.info().ok_or("Unable to get image info")?;
let metadata = decoder
.info()
.expect("info() should always return Some if read_info returned Ok");
let decoded_data = decoder.decode()?;
let decoded_data = match metadata.pixel_format {
@ -174,9 +173,7 @@ fn decode_jpeg(
/// Decodes the bitmap data in DefineBitsLossless tag into RGBA.
/// DefineBitsLossless is Zlib encoded pixel data (similar to PNG), possibly
/// palletized.
pub fn decode_define_bits_lossless(
swf_tag: &swf::DefineBitsLossless,
) -> Result<Bitmap, Box<dyn std::error::Error>> {
pub fn decode_define_bits_lossless(swf_tag: &swf::DefineBitsLossless) -> Result<Bitmap, Error> {
// Decompress the image data (DEFLATE compression).
let mut decoded_data = decompress_zlib(swf_tag.data)?;
@ -302,11 +299,10 @@ pub fn decode_define_bits_lossless(
out_data
}
_ => {
return Err(format!(
"Unexpected DefineBitsLossless{} format: {:?} ",
swf_tag.version, swf_tag.format,
)
.into());
return Err(Error::UnsupportedLosslessFormat(
swf_tag.version,
swf_tag.format,
));
}
};
@ -374,7 +370,7 @@ fn decode_gif(data: &[u8]) -> Result<Bitmap, Error> {
let mut decode_options = gif::DecodeOptions::new();
decode_options.set_color_output(gif::ColorOutput::RGBA);
let mut reader = decode_options.read_info(data)?;
let frame = reader.read_next_frame()?.ok_or("No frames in GIF")?;
let frame = reader.read_next_frame()?.ok_or(Error::EmptyGif)?;
// GIFs embedded in a DefineBitsJPEG tag will not have premultiplied alpha and need to be converted before sending to the renderer.
let mut data = frame.buffer.to_vec();
premultiply_alpha_rgba(&mut data);
@ -411,10 +407,12 @@ pub fn unmultiply_alpha_rgba(rgba: &mut [u8]) {
}
/// Decodes zlib-compressed data.
fn decompress_zlib(data: &[u8]) -> Result<Vec<u8>, std::io::Error> {
fn decompress_zlib(data: &[u8]) -> Result<Vec<u8>, Error> {
let mut out_data = Vec::new();
let mut decoder = flate2::bufread::ZlibDecoder::new(data);
decoder.read_to_end(&mut out_data)?;
decoder
.read_to_end(&mut out_data)
.map_err(|_| Error::InvalidZlibCompression)?;
out_data.shrink_to_fit();
Ok(out_data)
}

View File

@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
js-sys = "0.3.59"
log = "0.4"
ruffle_web_common = { path = "../../web/common" }
ruffle_render = { path = "..", features = ["tessellator"] }
ruffle_render = { path = "..", features = ["tessellator", "web"] }
wasm-bindgen = "=0.2.82"
bytemuck = { version = "1.12.1", features = ["derive"] }
fnv = "1.0.7"

View File

@ -3,6 +3,7 @@ use fnv::FnvHashMap;
use ruffle_render::backend::null::NullBitmapSource;
use ruffle_render::backend::{RenderBackend, ShapeHandle, ViewportDimensions};
use ruffle_render::bitmap::{Bitmap, BitmapFormat, BitmapHandle, BitmapSource};
use ruffle_render::error::Error as BitmapError;
use ruffle_render::shape_utils::DistilledShape;
use ruffle_render::tessellator::{
Gradient as TessGradient, GradientType, ShapeTessellator, Vertex as TessVertex,
@ -1242,7 +1243,7 @@ impl RenderBackend for WebGlRenderBackend {
self.bitmap_registry.get(&bitmap).map(|e| e.bitmap.clone())
}
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, Error> {
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, BitmapError> {
let format = match bitmap.format() {
BitmapFormat::Rgb => Gl::RGB,
BitmapFormat::Rgba => Gl::RGBA,
@ -1262,7 +1263,8 @@ impl RenderBackend for WebGlRenderBackend {
Gl::UNSIGNED_BYTE,
Some(bitmap.data()),
)
.into_js_result()?;
.into_js_result()
.map_err(|e| BitmapError::JavascriptError(e.into()))?;
// You must set the texture parameters for non-power-of-2 textures to function in WebGL1.
self.gl
@ -1293,7 +1295,7 @@ impl RenderBackend for WebGlRenderBackend {
Ok(handle)
}
fn unregister_bitmap(&mut self, bitmap: BitmapHandle) -> Result<(), Error> {
fn unregister_bitmap(&mut self, bitmap: BitmapHandle) -> Result<(), BitmapError> {
self.bitmap_registry.remove(&bitmap);
Ok(())
}
@ -1304,11 +1306,12 @@ impl RenderBackend for WebGlRenderBackend {
width: u32,
height: u32,
rgba: Vec<u8>,
) -> Result<BitmapHandle, Error> {
) -> Result<BitmapHandle, BitmapError> {
let texture = if let Some(entry) = self.bitmap_registry.get(&handle) {
&entry.texture_wrapper
} else {
return Err("update_texture: Bitmap is not regsitered".into());
log::warn!("Tried to replace nonexistent texture");
return Ok(handle);
};
self.gl.bind_texture(Gl::TEXTURE_2D, Some(&texture.texture));
@ -1325,7 +1328,8 @@ impl RenderBackend for WebGlRenderBackend {
Gl::UNSIGNED_BYTE,
Some(&rgba),
)
.into_js_result()?;
.into_js_result()
.map_err(|e| BitmapError::JavascriptError(e.into()))?;
Ok(handle)
}

View File

@ -10,6 +10,7 @@ use fnv::FnvHashMap;
use ruffle_render::backend::{RenderBackend, ShapeHandle, ViewportDimensions};
use ruffle_render::bitmap::{Bitmap, BitmapHandle, BitmapSource};
use ruffle_render::color_transform::ColorTransform;
use ruffle_render::error::Error as BitmapError;
use ruffle_render::shape_utils::DistilledShape;
use ruffle_render::tessellator::{
DrawType as TessDrawType, Gradient as TessGradient, GradientType, ShapeTessellator,
@ -1415,17 +1416,11 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
self.bitmap_registry.get(&bitmap).map(|e| e.bitmap.clone())
}
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, Error> {
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, BitmapError> {
if bitmap.width() > self.descriptors.limits.max_texture_dimension_2d
|| bitmap.height() > self.descriptors.limits.max_texture_dimension_2d
{
return Err(format!(
"Bitmap texture cannot be larger than {}px on either dimension (requested {} x {})",
self.descriptors.limits.max_texture_dimension_2d,
bitmap.width(),
bitmap.height()
)
.into());
return Err(BitmapError::TooLarge);
}
let bitmap = bitmap.to_rgba();
@ -1512,7 +1507,7 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
Ok(handle)
}
fn unregister_bitmap(&mut self, handle: BitmapHandle) -> Result<(), Error> {
fn unregister_bitmap(&mut self, handle: BitmapHandle) -> Result<(), BitmapError> {
self.bitmap_registry.remove(&handle);
Ok(())
}
@ -1523,11 +1518,12 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
width: u32,
height: u32,
rgba: Vec<u8>,
) -> Result<BitmapHandle, Error> {
) -> Result<BitmapHandle, BitmapError> {
let texture = if let Some(entry) = self.bitmap_registry.get(&handle) {
&entry.texture_wrapper.texture
} else {
return Err("update_texture: Bitmap not registered".into());
log::warn!("Tried to replace nonexistent texture");
return Ok(handle);
};
let extent = wgpu::Extent3d {

View File

@ -14,6 +14,12 @@ impl std::fmt::Display for JsError {
impl std::error::Error for JsError {}
impl From<JsError> for JsValue {
fn from(error: JsError) -> Self {
error.value
}
}
pub trait JsResult<T> {
/// Converts a `JsValue` into a standard `Error`.
fn warn_on_error(&self);