From 4aec120ffb9ae3deea07d57bf396353edfe23dd1 Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Fri, 11 Oct 2019 10:26:12 -0700 Subject: [PATCH] core: Add Bitmap display object Converts the Bitmap character to a proper display object. This can be instantiated directly in a PlaceObject tag in SWFv9 movies, compared to the previous versions which indirectly references bitmaps from Shape tags. --- core/src/backend/render.rs | 50 ++++++++--- core/src/bitmap.rs | 128 ++++++++++++++++++++++++++++ core/src/character.rs | 2 +- core/src/lib.rs | 1 + core/src/library.rs | 1 + core/src/movie_clip.rs | 55 +++++++++--- desktop/src/render.rs | 124 ++++++++++++++++++++++++--- web/src/render.rs | 170 ++++++++++++++++++++++--------------- 8 files changed, 426 insertions(+), 105 deletions(-) create mode 100644 core/src/bitmap.rs diff --git a/core/src/backend/render.rs b/core/src/backend/render.rs index fcc723d6d..45de0df25 100644 --- a/core/src/backend/render.rs +++ b/core/src/backend/render.rs @@ -11,18 +11,19 @@ pub trait RenderBackend { id: swf::CharacterId, data: &[u8], jpeg_tables: &[u8], - ) -> BitmapHandle; - fn register_bitmap_jpeg_2(&mut self, id: swf::CharacterId, data: &[u8]) -> BitmapHandle; + ) -> BitmapInfo; + fn register_bitmap_jpeg_2(&mut self, id: swf::CharacterId, data: &[u8]) -> BitmapInfo; fn register_bitmap_jpeg_3( &mut self, id: swf::CharacterId, jpeg_data: &[u8], alpha_data: &[u8], - ) -> BitmapHandle; - fn register_bitmap_png(&mut self, swf_tag: &swf::DefineBitsLossless) -> BitmapHandle; + ) -> BitmapInfo; + fn register_bitmap_png(&mut self, swf_tag: &swf::DefineBitsLossless) -> BitmapInfo; fn begin_frame(&mut self); fn clear(&mut self, color: Color); + fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform); fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform); fn end_frame(&mut self); fn draw_pause_overlay(&mut self); @@ -38,6 +39,14 @@ pub struct ShapeHandle(pub usize); #[derive(Copy, Clone, Debug)] pub struct BitmapHandle(pub usize); +/// Info returned by the `register_bitmap` methods. +#[derive(Copy, Clone, Debug)] +pub struct BitmapInfo { + pub handle: BitmapHandle, + pub width: u16, + pub height: u16, +} + #[derive(Debug, Copy, Clone, PartialEq)] pub enum Letterbox { None, @@ -60,26 +69,43 @@ impl RenderBackend for NullRenderer { _id: swf::CharacterId, _data: &[u8], _jpeg_tables: &[u8], - ) -> BitmapHandle { - BitmapHandle(0) + ) -> BitmapInfo { + BitmapInfo { + handle: BitmapHandle(0), + width: 0, + height: 0, + } } - fn register_bitmap_jpeg_2(&mut self, _id: swf::CharacterId, _data: &[u8]) -> BitmapHandle { - BitmapHandle(0) + fn register_bitmap_jpeg_2(&mut self, _id: swf::CharacterId, _data: &[u8]) -> BitmapInfo { + BitmapInfo { + handle: BitmapHandle(0), + width: 0, + height: 0, + } } fn register_bitmap_jpeg_3( &mut self, _id: swf::CharacterId, _data: &[u8], _alpha_data: &[u8], - ) -> BitmapHandle { - BitmapHandle(0) + ) -> BitmapInfo { + BitmapInfo { + handle: BitmapHandle(0), + width: 0, + height: 0, + } } - fn register_bitmap_png(&mut self, _swf_tag: &swf::DefineBitsLossless) -> BitmapHandle { - BitmapHandle(0) + fn register_bitmap_png(&mut self, _swf_tag: &swf::DefineBitsLossless) -> BitmapInfo { + BitmapInfo { + handle: BitmapHandle(0), + width: 0, + height: 0, + } } fn begin_frame(&mut self) {} fn end_frame(&mut self) {} fn clear(&mut self, _color: Color) {} + fn render_bitmap(&mut self, _bitmap: BitmapHandle, _transform: &Transform) {} fn render_shape(&mut self, _shape: ShapeHandle, _transform: &Transform) {} fn draw_pause_overlay(&mut self) {} fn draw_letterbox(&mut self, _letterbox: Letterbox) {} diff --git a/core/src/bitmap.rs b/core/src/bitmap.rs new file mode 100644 index 000000000..3f9a7fc5c --- /dev/null +++ b/core/src/bitmap.rs @@ -0,0 +1,128 @@ +//! Bitmap display object + +use crate::backend::render::BitmapHandle; +use crate::display_object::{DisplayObject, DisplayObjectBase}; +use crate::player::{RenderContext, UpdateContext}; +use crate::prelude::*; +use gc_arena::Gc; + +/// A Bitmap display object is a raw bitamp on the stage. +/// This can only be instanitated on the display list in SWFv9 AVM2 files. +/// In AVM1, this is only a library symbol that is referenced by `Graphic`. +/// Normally bitmaps are drawn in Flash as part of a Shape tag (`Graphic`), +/// but starting in AVM2, a raw `Bitmap` display object can be crated +/// with the `PlaceObject3` tag. +/// It can also be crated in ActionScript using the `Bitmap` class. +#[derive(Clone, Debug)] +pub struct Bitmap<'gc> { + base: DisplayObjectBase<'gc>, + static_data: Gc<'gc, BitmapStatic>, +} + +impl<'gc> Bitmap<'gc> { + pub fn new( + context: &mut UpdateContext<'_, 'gc, '_>, + id: CharacterId, + bitmap_handle: BitmapHandle, + width: u16, + height: u16, + ) -> Self { + Self { + base: Default::default(), + static_data: Gc::allocate( + context.gc_context, + BitmapStatic { + id, + bitmap_handle, + width, + height, + }, + ), + } + } + + pub fn bitmap_handle(&self) -> BitmapHandle { + self.static_data.bitmap_handle + } + + pub fn width(&self) -> u16 { + self.static_data.width + } + + pub fn height(&self) -> u16 { + self.static_data.height + } +} + +impl<'gc> DisplayObject<'gc> for Bitmap<'gc> { + impl_display_object!(base); + + fn id(&self) -> CharacterId { + self.static_data.id + } + + fn local_bounds(&self) -> BoundingBox { + BoundingBox { + x_min: Twips::new(0), + y_min: Twips::new(0), + x_max: Twips::new(self.width()), + y_max: Twips::new(self.height()), + valid: true, + } + } + + fn world_bounds(&self) -> BoundingBox { + // TODO: Use dirty flags and cache this. + let mut bounds = self.local_bounds().transform(self.matrix()); + let mut node = self.parent(); + while let Some(display_object) = node { + let display_object = display_object.read(); + bounds = bounds.transform(display_object.matrix()); + node = display_object.parent(); + } + bounds + } + + fn run_frame(&mut self, _context: &mut UpdateContext) { + // Noop + } + + fn render(&self, context: &mut RenderContext) { + if !self.world_bounds().intersects(&context.view_bounds) { + // Off-screen; culled + return; + } + + context.transform_stack.push(self.transform()); + + context.renderer.render_bitmap( + self.static_data.bitmap_handle, + context.transform_stack.transform(), + ); + + context.transform_stack.pop(); + } +} + +unsafe impl<'gc> gc_arena::Collect for Bitmap<'gc> { + fn trace(&self, cc: gc_arena::CollectionContext) { + self.base.trace(cc); + self.static_data.trace(cc); + } +} + +/// Static data shared between all instances of a bitmap. +#[derive(Clone)] +struct BitmapStatic { + id: CharacterId, + bitmap_handle: BitmapHandle, + width: u16, + height: u16, +} + +unsafe impl<'gc> gc_arena::Collect for BitmapStatic { + #[inline] + fn needs_trace() -> bool { + true + } +} diff --git a/core/src/character.rs b/core/src/character.rs index 86d4c851c..abf6c39d8 100644 --- a/core/src/character.rs +++ b/core/src/character.rs @@ -2,7 +2,7 @@ pub enum Character<'gc> { EditText(Box>), Graphic(Box>), MovieClip(Box>), - Bitmap(crate::backend::render::BitmapHandle), + Bitmap(Box>), Button(Box>), Font(Box), MorphShape(Box>), diff --git a/core/src/lib.rs b/core/src/lib.rs index 486a59ac7..e96d3931b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,6 +4,7 @@ mod display_object; mod avm1; +mod bitmap; mod bounding_box; mod button; mod character; diff --git a/core/src/library.rs b/core/src/library.rs index 775850a2d..c8c06b799 100644 --- a/core/src/library.rs +++ b/core/src/library.rs @@ -49,6 +49,7 @@ impl<'gc> Library<'gc> { gc_context: MutationContext<'gc, '_>, ) -> Result, Box> { let obj: Box> = match self.characters.get(&id) { + Some(Character::Bitmap(bitmap)) => bitmap.clone(), Some(Character::EditText(edit_text)) => edit_text.clone(), Some(Character::Graphic(graphic)) => graphic.clone(), Some(Character::MorphShape(morph_shape)) => morph_shape.clone(), diff --git a/core/src/movie_clip.rs b/core/src/movie_clip.rs index cab981695..2a16c3b32 100644 --- a/core/src/movie_clip.rs +++ b/core/src/movie_clip.rs @@ -679,10 +679,17 @@ impl<'gc, 'a> MovieClip<'gc> { version: u8, ) -> DecodeResult { let define_bits_lossless = reader.read_define_bits_lossless(version)?; - let handle = context.renderer.register_bitmap_png(&define_bits_lossless); + let bitmap_info = context.renderer.register_bitmap_png(&define_bits_lossless); + let bitmap = crate::bitmap::Bitmap::new( + context, + define_bits_lossless.id, + bitmap_info.handle, + bitmap_info.width, + bitmap_info.height, + ); context .library - .register_character(define_bits_lossless.id, Character::Bitmap(handle)); + .register_character(define_bits_lossless.id, Character::Bitmap(Box::new(bitmap))); Ok(()) } @@ -815,14 +822,21 @@ impl<'gc, 'a> MovieClip<'gc> { .get_mut() .take(data_len as u64) .read_to_end(&mut jpeg_data)?; - let handle = context.renderer.register_bitmap_jpeg( + let bitmap_info = context.renderer.register_bitmap_jpeg( id, &jpeg_data, context.library.jpeg_tables().unwrap(), ); + let bitmap = crate::bitmap::Bitmap::new( + context, + id, + bitmap_info.handle, + bitmap_info.width, + bitmap_info.height, + ); context .library - .register_character(id, Character::Bitmap(handle)); + .register_character(id, Character::Bitmap(Box::new(bitmap))); Ok(()) } @@ -841,10 +855,17 @@ impl<'gc, 'a> MovieClip<'gc> { .get_mut() .take(data_len as u64) .read_to_end(&mut jpeg_data)?; - let handle = context.renderer.register_bitmap_jpeg_2(id, &jpeg_data); + let bitmap_info = context.renderer.register_bitmap_jpeg_2(id, &jpeg_data); + let bitmap = crate::bitmap::Bitmap::new( + context, + id, + bitmap_info.handle, + bitmap_info.width, + bitmap_info.height, + ); context .library - .register_character(id, Character::Bitmap(handle)); + .register_character(id, Character::Bitmap(Box::new(bitmap))); Ok(()) } @@ -869,12 +890,19 @@ impl<'gc, 'a> MovieClip<'gc> { .get_mut() .take(alpha_len as u64) .read_to_end(&mut alpha_data)?; - let handle = context + let bitmap_info = context .renderer .register_bitmap_jpeg_3(id, &jpeg_data, &alpha_data); + let bitmap = crate::bitmap::Bitmap::new( + context, + id, + bitmap_info.handle, + bitmap_info.width, + bitmap_info.height, + ); context .library - .register_character(id, Character::Bitmap(handle)); + .register_character(id, Character::Bitmap(Box::new(bitmap))); Ok(()) } @@ -900,12 +928,19 @@ impl<'gc, 'a> MovieClip<'gc> { .get_mut() .take(alpha_len as u64) .read_to_end(&mut alpha_data)?; - let handle = context + let bitmap_info = context .renderer .register_bitmap_jpeg_3(id, &jpeg_data, &alpha_data); + let bitmap = crate::bitmap::Bitmap::new( + context, + id, + bitmap_info.handle, + bitmap_info.width, + bitmap_info.height, + ); context .library - .register_character(id, Character::Bitmap(handle)); + .register_character(id, Character::Bitmap(Box::new(bitmap))); Ok(()) } diff --git a/desktop/src/render.rs b/desktop/src/render.rs index 48465f669..827869ef8 100644 --- a/desktop/src/render.rs +++ b/desktop/src/render.rs @@ -7,11 +7,14 @@ use lyon::{ }; use ruffle_core::backend::render::swf::{self, FillStyle}; use ruffle_core::backend::render::{ - BitmapHandle, Color, Letterbox, RenderBackend, ShapeHandle, Transform, + BitmapHandle, BitmapInfo, Color, Letterbox, RenderBackend, ShapeHandle, Transform, }; use ruffle_core::shape_utils::{DrawCommand, DrawPath}; +use std::convert::TryInto; use swf::Twips; +type Error = Box; + pub struct GliumRenderBackend { display: Display, target: Option, @@ -19,6 +22,7 @@ pub struct GliumRenderBackend { gradient_shader_program: glium::Program, bitmap_shader_program: glium::Program, meshes: Vec, + quad_shape: ShapeHandle, textures: Vec<(swf::CharacterId, Texture)>, num_masks: u32, num_masks_active: u32, @@ -32,9 +36,7 @@ pub struct GliumRenderBackend { } impl GliumRenderBackend { - pub fn new( - windowed_context: WindowedContext, - ) -> Result> { + pub fn new(windowed_context: WindowedContext) -> Result { let display = Display::from_gl_window(windowed_context)?; use glium::program::ProgramCreationInput; @@ -80,13 +82,17 @@ impl GliumRenderBackend { }, )?; + let quad_mesh = Self::build_quad_mesh(&display)?; + let quad_shape = ShapeHandle(0); + let mut renderer = GliumRenderBackend { display, shader_program, gradient_shader_program, bitmap_shader_program, target: None, - meshes: vec![], + meshes: vec![quad_mesh], + quad_shape, textures: vec![], viewport_width: 500.0, viewport_height: 500.0, @@ -102,6 +108,53 @@ impl GliumRenderBackend { Ok(renderer) } + // Builds the quad mesh that is used for rendering bitmap display objects. + fn build_quad_mesh(display: &Display) -> Result { + let vertex_buffer = glium::VertexBuffer::new( + display, + &[ + Vertex { + position: [0.0, 0.0], + color: [1.0, 1.0, 1.0, 1.0], + }, + Vertex { + position: [1.0, 0.0], + color: [1.0, 1.0, 1.0, 1.0], + }, + Vertex { + position: [1.0, 1.0], + color: [1.0, 1.0, 1.0, 1.0], + }, + Vertex { + position: [0.0, 1.0], + color: [1.0, 1.0, 1.0, 1.0], + }, + ], + )?; + + let index_buffer = glium::IndexBuffer::new( + display, + glium::index::PrimitiveType::TrianglesList, + &[0, 1, 2, 0, 2, 3], + )?; + + let quad_mesh = Mesh { + draws: vec![Draw { + draw_type: DrawType::Bitmap { + uniforms: BitmapUniforms { + matrix: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], + id: 0, + }, + is_smoothed: true, + is_repeating: false, + }, + vertex_buffer, + index_buffer, + }], + }; + Ok(quad_mesh) + } + pub fn display(&self) -> &Display { &self.display } @@ -488,7 +541,7 @@ impl RenderBackend for GliumRenderBackend { id: swf::CharacterId, data: &[u8], jpeg_tables: &[u8], - ) -> BitmapHandle { + ) -> BitmapInfo { if !jpeg_tables.is_empty() { let mut full_jpeg = jpeg_tables[..jpeg_tables.len() - 2].to_vec(); full_jpeg.extend_from_slice(&data[2..]); @@ -499,7 +552,7 @@ impl RenderBackend for GliumRenderBackend { } } - fn register_bitmap_jpeg_2(&mut self, id: swf::CharacterId, data: &[u8]) -> BitmapHandle { + fn register_bitmap_jpeg_2(&mut self, id: swf::CharacterId, data: &[u8]) -> BitmapInfo { let data = ruffle_core::backend::render::remove_invalid_jpeg_data(data); let mut decoder = jpeg_decoder::Decoder::new(&data[..]); @@ -523,7 +576,11 @@ impl RenderBackend for GliumRenderBackend { }, )); - handle + BitmapInfo { + handle, + width: metadata.width, + height: metadata.height, + } } fn register_bitmap_jpeg_3( @@ -531,7 +588,7 @@ impl RenderBackend for GliumRenderBackend { id: swf::CharacterId, jpeg_data: &[u8], alpha_data: &[u8], - ) -> BitmapHandle { + ) -> BitmapInfo { let (width, height, rgba) = ruffle_core::backend::render::define_bits_jpeg_to_rgba(jpeg_data, alpha_data) .expect("Error decoding DefineBitsJPEG3"); @@ -548,10 +605,14 @@ impl RenderBackend for GliumRenderBackend { }, )); - handle + BitmapInfo { + handle, + width: width.try_into().unwrap(), + height: height.try_into().unwrap(), + } } - fn register_bitmap_png(&mut self, swf_tag: &swf::DefineBitsLossless) -> BitmapHandle { + fn register_bitmap_png(&mut self, swf_tag: &swf::DefineBitsLossless) -> BitmapInfo { let decoded_data = ruffle_core::backend::render::define_bits_lossless_to_rgba(swf_tag) .expect("Error decoding DefineBitsLossless"); @@ -572,7 +633,11 @@ impl RenderBackend for GliumRenderBackend { }, )); - handle + BitmapInfo { + handle, + width: swf_tag.width, + height: swf_tag.height, + } } fn begin_frame(&mut self) { @@ -604,6 +669,41 @@ impl RenderBackend for GliumRenderBackend { ); } + fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform) { + // TODO: Might be better to make this separate code to render the bitmap + // instead of going through render_shape. But render_shape already handles + // masking etc. + if let Some((id, bitmap)) = self.textures.get(bitmap.0) { + // Adjust the quad draw to use the target bitmap. + let mesh = &mut self.meshes[self.quad_shape.0]; + let draw = &mut mesh.draws[0]; + let width = bitmap.width as f32; + let height = bitmap.height as f32; + if let DrawType::Bitmap { + uniforms: BitmapUniforms { id: draw_id, .. }, + .. + } = &mut draw.draw_type + { + *draw_id = *id; + } + + // Scale the quad to the bitmap's dimensions. + use ruffle_core::matrix::Matrix; + let scale_transform = Transform { + matrix: transform.matrix + * Matrix { + a: width, + d: height, + ..Default::default() + }, + ..*transform + }; + + // Render the quad. + self.render_shape(self.quad_shape, &scale_transform); + } + } + fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform) { let target = self.target.as_mut().unwrap(); diff --git a/web/src/render.rs b/web/src/render.rs index aa366a45e..7e1993eeb 100644 --- a/web/src/render.rs +++ b/web/src/render.rs @@ -1,8 +1,10 @@ use crate::utils::JsResult; use ruffle_core::backend::render::{ - swf, swf::CharacterId, BitmapHandle, Color, Letterbox, RenderBackend, ShapeHandle, Transform, + swf, swf::CharacterId, BitmapHandle, BitmapInfo, Color, Letterbox, RenderBackend, ShapeHandle, + Transform, }; use std::collections::HashMap; +use std::convert::TryInto; use wasm_bindgen::JsCast; use web_sys::{CanvasRenderingContext2d, Element, HtmlCanvasElement, HtmlImageElement}; @@ -219,6 +221,66 @@ impl WebCanvasRenderBackend { (self.canvas.clone(), self.context.clone()) } } + + #[allow(clippy::float_cmp)] + #[inline] + fn set_transform(&mut self, transform: &Transform) { + let matrix = transform.matrix; + + self.context + .set_transform( + matrix.a.into(), + matrix.b.into(), + matrix.c.into(), + matrix.d.into(), + f64::from(matrix.tx) / 20.0, + f64::from(matrix.ty) / 20.0, + ) + .unwrap(); + + let color_transform = &transform.color_transform; + if color_transform.r_mult == 1.0 + && color_transform.g_mult == 1.0 + && color_transform.b_mult == 1.0 + && color_transform.r_add == 0.0 + && color_transform.g_add == 0.0 + && color_transform.b_add == 0.0 + && color_transform.a_add == 0.0 + { + self.context.set_global_alpha(color_transform.a_mult.into()); + } else { + // TODO HACK: Firefox is having issues with additive alpha in color transforms (see #38). + // Hack this away and just use multiplicative (not accurate in many cases, but won't look awful). + let (a_mult, a_add) = if self.use_color_transform_hack && color_transform.a_add != 0.0 { + (color_transform.a_mult + color_transform.a_add, 0.0) + } else { + (color_transform.a_mult, color_transform.a_add) + }; + + let matrix_str = format!( + "{} 0 0 0 {} 0 {} 0 0 {} 0 0 {} 0 {} 0 0 0 {} {}", + color_transform.r_mult, + color_transform.r_add, + color_transform.g_mult, + color_transform.g_add, + color_transform.b_mult, + color_transform.b_add, + a_mult, + a_add + ); + self.color_matrix + .set_attribute("values", &matrix_str) + .unwrap(); + + self.context.set_filter("url('#_cm')"); + } + } + + #[inline] + fn clear_transform(&mut self) { + self.context.set_filter("none"); + self.context.set_global_alpha(1.0); + } } impl RenderBackend for WebCanvasRenderBackend { @@ -299,14 +361,14 @@ impl RenderBackend for WebCanvasRenderBackend { id: CharacterId, data: &[u8], jpeg_tables: &[u8], - ) -> BitmapHandle { + ) -> BitmapInfo { let mut full_jpeg = jpeg_tables[..jpeg_tables.len() - 2].to_vec(); full_jpeg.extend_from_slice(&data[2..]); self.register_bitmap_jpeg_2(id, &full_jpeg[..]) } - fn register_bitmap_jpeg_2(&mut self, id: CharacterId, data: &[u8]) -> BitmapHandle { + fn register_bitmap_jpeg_2(&mut self, id: CharacterId, data: &[u8]) -> BitmapInfo { let data = ruffle_core::backend::render::remove_invalid_jpeg_data(data); let mut decoder = jpeg_decoder::Decoder::new(&data[..]); decoder.read_info().unwrap(); @@ -324,7 +386,11 @@ impl RenderBackend for WebCanvasRenderBackend { data: jpeg_encoded, }); self.id_to_bitmap.insert(id, handle); - handle + BitmapInfo { + handle, + width: metadata.width, + height: metadata.height, + } } fn register_bitmap_jpeg_3( @@ -332,7 +398,7 @@ impl RenderBackend for WebCanvasRenderBackend { id: swf::CharacterId, jpeg_data: &[u8], alpha_data: &[u8], - ) -> BitmapHandle { + ) -> BitmapInfo { let (width, height, mut rgba) = ruffle_core::backend::render::define_bits_jpeg_to_rgba(jpeg_data, alpha_data) .expect("Error decoding DefineBitsJPEG3"); @@ -352,10 +418,14 @@ impl RenderBackend for WebCanvasRenderBackend { }); self.id_to_bitmap.insert(id, handle); - handle + BitmapInfo { + handle, + width: width.try_into().expect("JPEG dimensions too large"), + height: height.try_into().expect("JPEG dimensions too large"), + } } - fn register_bitmap_png(&mut self, swf_tag: &swf::DefineBitsLossless) -> BitmapHandle { + fn register_bitmap_png(&mut self, swf_tag: &swf::DefineBitsLossless) -> BitmapInfo { let mut rgba = ruffle_core::backend::render::define_bits_lossless_to_rgba(swf_tag) .expect("Error decoding DefineBitsLossless"); @@ -376,7 +446,11 @@ impl RenderBackend for WebCanvasRenderBackend { data: png, }); self.id_to_bitmap.insert(swf_tag.id, handle); - handle + BitmapInfo { + handle, + width: swf_tag.width, + height: swf_tag.height, + } } fn begin_frame(&mut self) { @@ -398,70 +472,26 @@ impl RenderBackend for WebCanvasRenderBackend { .fill_rect(0.0, 0.0, width.into(), height.into()); } - #[allow(clippy::float_cmp)] - fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform) { - let shape = if let Some(shape) = self.shapes.get(shape.0) { - shape - } else { - return; - }; - - let matrix = transform.matrix; //self.view_matrix * transform.matrix; - - self.context - .set_transform( - matrix.a.into(), - matrix.b.into(), - matrix.c.into(), - matrix.d.into(), - f64::from(matrix.tx) / 20.0, - f64::from(matrix.ty) / 20.0, - ) - .unwrap(); - - let color_transform = &transform.color_transform; - if color_transform.r_mult == 1.0 - && color_transform.g_mult == 1.0 - && color_transform.b_mult == 1.0 - && color_transform.r_add == 0.0 - && color_transform.g_add == 0.0 - && color_transform.b_add == 0.0 - && color_transform.a_add == 0.0 - { - self.context.set_global_alpha(color_transform.a_mult.into()); - } else { - // TODO HACK: Firefox is having issues with additive alpha in color transforms (see #38). - // Hack this away and just use multiplicative (not accurate in many cases, but won't look awful). - let (a_mult, a_add) = if self.use_color_transform_hack && color_transform.a_add != 0.0 { - (color_transform.a_mult + color_transform.a_add, 0.0) - } else { - (color_transform.a_mult, color_transform.a_add) - }; - - let matrix_str = format!( - "{} 0 0 0 {} 0 {} 0 0 {} 0 0 {} 0 {} 0 0 0 {} {}", - color_transform.r_mult, - color_transform.r_add, - color_transform.g_mult, - color_transform.g_add, - color_transform.b_mult, - color_transform.b_add, - a_mult, - a_add - ); - self.color_matrix - .set_attribute("values", &matrix_str) - .unwrap(); - - self.context.set_filter("url('#_cm')"); + fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform) { + self.set_transform(transform); + if let Some(bitmap) = self.bitmaps.get(bitmap.0) { + let _ = self + .context + .draw_image_with_html_image_element(&bitmap.image, 0.0, 0.0); } + self.clear_transform(); + } - self.context - .draw_image_with_html_image_element(&shape.image, shape.x_min, shape.y_min) - .unwrap(); - - self.context.set_filter("none"); - self.context.set_global_alpha(1.0); + fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform) { + self.set_transform(transform); + if let Some(shape) = self.shapes.get(shape.0) { + let _ = self.context.draw_image_with_html_image_element( + &shape.image, + shape.x_min, + shape.y_min, + ); + } + self.clear_transform(); } fn draw_pause_overlay(&mut self) {