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.
This commit is contained in:
Mike Welsh 2019-10-11 10:26:12 -07:00
parent d95e31905e
commit 4aec120ffb
8 changed files with 426 additions and 105 deletions

View File

@ -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]) -> 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_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) -> BitmapInfo {
BitmapInfo {
handle: BitmapHandle(0),
width: 0,
height: 0,
}
fn register_bitmap_png(&mut self, _swf_tag: &swf::DefineBitsLossless) -> BitmapHandle {
BitmapHandle(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) {}

128
core/src/bitmap.rs Normal file
View File

@ -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
}
}

View File

@ -2,7 +2,7 @@ pub enum Character<'gc> {
EditText(Box<crate::edit_text::EditText<'gc>>),
Graphic(Box<crate::graphic::Graphic<'gc>>),
MovieClip(Box<crate::movie_clip::MovieClip<'gc>>),
Bitmap(crate::backend::render::BitmapHandle),
Bitmap(Box<crate::bitmap::Bitmap<'gc>>),
Button(Box<crate::button::Button<'gc>>),
Font(Box<crate::font::Font>),
MorphShape(Box<crate::morph_shape::MorphShape<'gc>>),

View File

@ -4,6 +4,7 @@
mod display_object;
mod avm1;
mod bitmap;
mod bounding_box;
mod button;
mod character;

View File

@ -49,6 +49,7 @@ impl<'gc> Library<'gc> {
gc_context: MutationContext<'gc, '_>,
) -> Result<DisplayNode<'gc>, Box<dyn std::error::Error>> {
let obj: Box<dyn DisplayObject<'gc>> = 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(),

View File

@ -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(())
}

View File

@ -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<dyn std::error::Error>;
pub struct GliumRenderBackend {
display: Display,
target: Option<Frame>,
@ -19,6 +22,7 @@ pub struct GliumRenderBackend {
gradient_shader_program: glium::Program,
bitmap_shader_program: glium::Program,
meshes: Vec<Mesh>,
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<GliumRenderBackend, Box<dyn std::error::Error>> {
pub fn new(windowed_context: WindowedContext) -> Result<GliumRenderBackend, Error> {
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<Mesh, Error> {
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();

View File

@ -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) {