core: `ColorTransform` cleanup
Main changes: * Merge `ColorTransformParams` into `ColorTransformObject`, as it's only relevant for AVM1. * Make `BitmapData::color_transform` work with a generic `ColorTransform`, which uses fixed-point arithmetic. Note that Ruffle still calculates color transforms slightly different from Flash. This is probably caused by inaccuracy of the current `ColorTransformObject` to `ColorTransform` conversion and/or the `ColorTransform` application logic itself. Since this requires further research, it'll be fixed in a future PR.
This commit is contained in:
parent
a17352a01a
commit
e7643c731b
|
@ -605,17 +605,16 @@ pub fn color_transform<'gc>(
|
|||
.get("height", activation)?
|
||||
.coerce_to_f64(activation)? as i32;
|
||||
|
||||
let min_x = x.max(0) as u32;
|
||||
let end_x = (x + width) as u32;
|
||||
let min_y = y.max(0) as u32;
|
||||
let end_y = (y + height) as u32;
|
||||
let x_min = x.max(0) as u32;
|
||||
let x_max = (x + width) as u32;
|
||||
let y_min = y.max(0) as u32;
|
||||
let y_max = (y + height) as u32;
|
||||
|
||||
if let Some(color_transform) = color_transform.as_color_transform_object() {
|
||||
let params = color_transform.get_params();
|
||||
bitmap_data
|
||||
.bitmap_data()
|
||||
.write(activation.context.gc_context)
|
||||
.color_transform(min_x, min_y, end_x, end_y, ¶ms);
|
||||
.color_transform(x_min, y_min, x_max, y_max, color_transform.into());
|
||||
}
|
||||
|
||||
return Ok(Value::Undefined);
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use crate::add_field_accessors;
|
||||
use crate::avm1::{Object, ScriptObject, TObject};
|
||||
use crate::bitmap::color_transform_params::ColorTransformParams;
|
||||
use crate::impl_custom_object;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use ruffle_render::color_transform::ColorTransform;
|
||||
use std::fmt;
|
||||
use swf::Fixed8;
|
||||
|
||||
/// A ColorTransform
|
||||
#[derive(Clone, Copy, Collect)]
|
||||
|
@ -15,21 +16,29 @@ pub struct ColorTransformObject<'gc>(GcCell<'gc, ColorTransformData<'gc>>);
|
|||
pub struct ColorTransformData<'gc> {
|
||||
/// The underlying script object.
|
||||
base: ScriptObject<'gc>,
|
||||
params: ColorTransformParams,
|
||||
|
||||
red_multiplier: f64,
|
||||
green_multiplier: f64,
|
||||
blue_multiplier: f64,
|
||||
alpha_multiplier: f64,
|
||||
red_offset: f64,
|
||||
green_offset: f64,
|
||||
blue_offset: f64,
|
||||
alpha_offset: f64,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ColorTransformObject<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let this = self.0.read();
|
||||
f.debug_struct("ColorTransform")
|
||||
.field("redMultiplier", &this.params.red_multiplier)
|
||||
.field("greenMultiplier", &this.params.green_multiplier)
|
||||
.field("blueMultiplier", &this.params.blue_multiplier)
|
||||
.field("alphaMultiplier", &this.params.alpha_multiplier)
|
||||
.field("redOffset", &this.params.red_offset)
|
||||
.field("greenOffset", &this.params.green_offset)
|
||||
.field("blueOffset", &this.params.blue_offset)
|
||||
.field("alphaOffset", &this.params.alpha_offset)
|
||||
.field("redMultiplier", &this.red_multiplier)
|
||||
.field("greenMultiplier", &this.green_multiplier)
|
||||
.field("blueMultiplier", &this.blue_multiplier)
|
||||
.field("alphaMultiplier", &this.alpha_multiplier)
|
||||
.field("redOffset", &this.red_offset)
|
||||
.field("greenOffset", &this.green_offset)
|
||||
.field("blueOffset", &this.blue_offset)
|
||||
.field("alphaOffset", &this.alpha_offset)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +52,6 @@ impl<'gc> ColorTransformObject<'gc> {
|
|||
gc_context,
|
||||
ColorTransformData {
|
||||
base: ScriptObject::new(gc_context, proto),
|
||||
params: ColorTransformParams {
|
||||
red_multiplier: 0.0,
|
||||
green_multiplier: 0.0,
|
||||
blue_multiplier: 0.0,
|
||||
|
@ -53,40 +61,33 @@ impl<'gc> ColorTransformObject<'gc> {
|
|||
blue_offset: 0.0,
|
||||
alpha_offset: 0.0,
|
||||
},
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
add_field_accessors!(
|
||||
[set_params, get_params, params, ColorTransformParams],
|
||||
[
|
||||
set_red_multiplier,
|
||||
get_red_multiplier,
|
||||
params.red_multiplier,
|
||||
f64
|
||||
],
|
||||
[set_red_multiplier, get_red_multiplier, red_multiplier, f64],
|
||||
[
|
||||
set_green_multiplier,
|
||||
get_green_multiplier,
|
||||
params.green_multiplier,
|
||||
green_multiplier,
|
||||
f64
|
||||
],
|
||||
[
|
||||
set_blue_multiplier,
|
||||
get_blue_multiplier,
|
||||
params.blue_multiplier,
|
||||
blue_multiplier,
|
||||
f64
|
||||
],
|
||||
[
|
||||
set_alpha_multiplier,
|
||||
get_alpha_multiplier,
|
||||
params.alpha_multiplier,
|
||||
alpha_multiplier,
|
||||
f64
|
||||
],
|
||||
[set_red_offset, get_red_offset, params.red_offset, f64],
|
||||
[set_green_offset, get_green_offset, params.green_offset, f64],
|
||||
[set_blue_offset, get_blue_offset, params.blue_offset, f64],
|
||||
[set_alpha_offset, get_alpha_offset, params.alpha_offset, f64],
|
||||
[set_red_offset, get_red_offset, red_offset, f64],
|
||||
[set_green_offset, get_green_offset, green_offset, f64],
|
||||
[set_blue_offset, get_blue_offset, blue_offset, f64],
|
||||
[set_alpha_offset, get_alpha_offset, alpha_offset, f64],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -95,3 +96,18 @@ impl<'gc> TObject<'gc> for ColorTransformObject<'gc> {
|
|||
bare_object(as_color_transform_object -> ColorTransformObject::empty_color_transform_object);
|
||||
});
|
||||
}
|
||||
|
||||
impl From<ColorTransformObject<'_>> for ColorTransform {
|
||||
fn from(object: ColorTransformObject) -> Self {
|
||||
Self {
|
||||
r_mult: Fixed8::from_f64(object.get_red_multiplier()),
|
||||
g_mult: Fixed8::from_f64(object.get_green_multiplier()),
|
||||
b_mult: Fixed8::from_f64(object.get_blue_multiplier()),
|
||||
a_mult: Fixed8::from_f64(object.get_alpha_multiplier()),
|
||||
r_add: object.get_red_offset() as i16,
|
||||
g_add: object.get_green_offset() as i16,
|
||||
b_add: object.get_blue_offset() as i16,
|
||||
a_add: object.get_alpha_offset() as i16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
pub mod bitmap_data;
|
||||
pub mod color_transform_params;
|
||||
pub mod turbulence;
|
||||
|
||||
/// Determine if a particular bitmap data size is valid.
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
use gc_arena::{Collect, GcCell};
|
||||
use swf::BlendMode;
|
||||
|
||||
use crate::avm2::{Object as Avm2Object, Value as Avm2Value};
|
||||
use crate::bitmap::color_transform_params::ColorTransformParams;
|
||||
use crate::bitmap::turbulence::Turbulence;
|
||||
use crate::context::RenderContext;
|
||||
use crate::context::UpdateContext;
|
||||
use crate::display_object::DisplayObject;
|
||||
use crate::display_object::TDisplayObject;
|
||||
use bitflags::bitflags;
|
||||
use gc_arena::{Collect, GcCell};
|
||||
use ruffle_render::backend::RenderBackend;
|
||||
use ruffle_render::bitmap::{Bitmap, BitmapFormat, BitmapHandle};
|
||||
use ruffle_render::color_transform::ColorTransform;
|
||||
use ruffle_render::commands::{CommandHandler, CommandList};
|
||||
use ruffle_render::transform::Transform;
|
||||
use std::ops::Range;
|
||||
use swf::BlendMode;
|
||||
|
||||
/// An implementation of the Lehmer/Park-Miller random number generator
|
||||
/// Uses the fixed parameters m = 2,147,483,647 and a = 16,807
|
||||
|
@ -131,6 +130,22 @@ impl From<i32> for Color {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<swf::Color> for Color {
|
||||
fn from(c: swf::Color) -> Self {
|
||||
Self::argb(c.a, c.r, c.g, c.b)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for swf::Color {
|
||||
fn from(c: Color) -> Self {
|
||||
let r = c.red();
|
||||
let g = c.green();
|
||||
let b = c.blue();
|
||||
let a = c.alpha();
|
||||
Self { r, g, b, a }
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct ChannelOptions: u8 {
|
||||
const RED = 1 << 0;
|
||||
|
@ -490,33 +505,22 @@ impl<'gc> BitmapData<'gc> {
|
|||
|
||||
pub fn color_transform(
|
||||
&mut self,
|
||||
min_x: u32,
|
||||
min_y: u32,
|
||||
end_x: u32,
|
||||
end_y: u32,
|
||||
color_transform: &ColorTransformParams,
|
||||
x_min: u32,
|
||||
y_min: u32,
|
||||
x_max: u32,
|
||||
y_max: u32,
|
||||
color_transform: ColorTransform,
|
||||
) {
|
||||
for x in min_x..end_x.min(self.width()) {
|
||||
for y in min_y..end_y.min(self.height()) {
|
||||
let color = self
|
||||
.get_pixel_raw(x, y)
|
||||
.unwrap_or_else(|| 0.into())
|
||||
.to_un_multiplied_alpha();
|
||||
for x in x_min..x_max.min(self.width()) {
|
||||
for y in y_min..y_max.min(self.height()) {
|
||||
let color = self.get_pixel_raw(x, y).unwrap().to_un_multiplied_alpha();
|
||||
|
||||
let alpha = ((color.alpha() as f32 * color_transform.alpha_multiplier as f32)
|
||||
+ color_transform.alpha_offset as f32) as u8;
|
||||
let red = ((color.red() as f32 * color_transform.red_multiplier as f32)
|
||||
+ color_transform.red_offset as f32) as u8;
|
||||
let green = ((color.green() as f32 * color_transform.green_multiplier as f32)
|
||||
+ color_transform.green_offset as f32) as u8;
|
||||
let blue = ((color.blue() as f32 * color_transform.blue_multiplier as f32)
|
||||
+ color_transform.blue_offset as f32) as u8;
|
||||
let color = color_transform * swf::Color::from(color);
|
||||
|
||||
self.set_pixel32_raw(
|
||||
x,
|
||||
y,
|
||||
Color::argb(alpha, red, green, blue)
|
||||
.to_premultiplied_alpha(self.transparency()),
|
||||
Color::from(color).to_premultiplied_alpha(self.transparency()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
use gc_arena::Collect;
|
||||
|
||||
#[derive(Copy, Clone, Collect)]
|
||||
#[collect(no_drop)]
|
||||
pub struct ColorTransformParams {
|
||||
pub red_multiplier: f64,
|
||||
pub green_multiplier: f64,
|
||||
pub blue_multiplier: f64,
|
||||
pub alpha_multiplier: f64,
|
||||
pub red_offset: f64,
|
||||
pub green_offset: f64,
|
||||
pub blue_offset: f64,
|
||||
pub alpha_offset: f64,
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use swf::Fixed8;
|
||||
use std::ops::{Mul, MulAssign};
|
||||
use swf::{Color, Fixed8};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct ColorTransform {
|
||||
|
@ -28,17 +29,16 @@ impl From<swf::ColorTransform> for ColorTransform {
|
|||
}
|
||||
|
||||
impl ColorTransform {
|
||||
#[allow(clippy::float_cmp)]
|
||||
pub fn is_identity(&self) -> bool {
|
||||
self.r_mult.is_one()
|
||||
&& self.g_mult.is_one()
|
||||
&& self.b_mult.is_one()
|
||||
&& self.a_mult.is_one()
|
||||
&& self.r_add == 0
|
||||
&& self.g_add == 0
|
||||
&& self.b_add == 0
|
||||
&& self.a_add == 0
|
||||
}
|
||||
pub const IDENTITY: Self = Self {
|
||||
r_mult: Fixed8::ONE,
|
||||
b_mult: Fixed8::ONE,
|
||||
g_mult: Fixed8::ONE,
|
||||
a_mult: Fixed8::ONE,
|
||||
r_add: 0,
|
||||
b_add: 0,
|
||||
g_add: 0,
|
||||
a_add: 0,
|
||||
};
|
||||
|
||||
/// Returns the multiplicative component of this color transform in RGBA order
|
||||
/// with the values normalized [0.0, 1.0].
|
||||
|
@ -63,7 +63,7 @@ impl ColorTransform {
|
|||
}
|
||||
|
||||
/// Sets the multiplicate component of this color transform.
|
||||
pub fn set_mult_color(&mut self, color: &swf::Color) {
|
||||
pub fn set_mult_color(&mut self, color: &Color) {
|
||||
self.r_mult = Fixed8::from_f32(f32::from(color.r) / 255.0);
|
||||
self.g_mult = Fixed8::from_f32(f32::from(color.g) / 255.0);
|
||||
self.b_mult = Fixed8::from_f32(f32::from(color.b) / 255.0);
|
||||
|
@ -72,29 +72,20 @@ impl ColorTransform {
|
|||
}
|
||||
|
||||
impl Default for ColorTransform {
|
||||
fn default() -> ColorTransform {
|
||||
ColorTransform {
|
||||
r_mult: Fixed8::ONE,
|
||||
b_mult: Fixed8::ONE,
|
||||
g_mult: Fixed8::ONE,
|
||||
a_mult: Fixed8::ONE,
|
||||
r_add: 0,
|
||||
b_add: 0,
|
||||
g_add: 0,
|
||||
a_add: 0,
|
||||
}
|
||||
fn default() -> Self {
|
||||
Self::IDENTITY
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul for ColorTransform {
|
||||
impl Mul for ColorTransform {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self {
|
||||
ColorTransform {
|
||||
Self {
|
||||
r_mult: self.r_mult.wrapping_mul(rhs.r_mult),
|
||||
g_mult: self.g_mult.wrapping_mul(rhs.g_mult),
|
||||
b_mult: self.b_mult.wrapping_mul(rhs.b_mult),
|
||||
a_mult: self.a_mult.wrapping_mul(rhs.a_mult),
|
||||
|
||||
r_add: self
|
||||
.r_add
|
||||
.wrapping_add(self.r_mult.wrapping_mul_int(rhs.r_add)),
|
||||
|
@ -111,8 +102,32 @@ impl std::ops::Mul for ColorTransform {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::ops::MulAssign for ColorTransform {
|
||||
impl MulAssign for ColorTransform {
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
*self = *self * rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Color> for ColorTransform {
|
||||
type Output = Color;
|
||||
|
||||
fn mul(self, mut color: Color) -> Color {
|
||||
color.r = self
|
||||
.r_mult
|
||||
.wrapping_mul_int(i16::from(color.r))
|
||||
.wrapping_add(self.r_add) as u8;
|
||||
color.g = self
|
||||
.g_mult
|
||||
.wrapping_mul_int(i16::from(color.g))
|
||||
.wrapping_add(self.g_add) as u8;
|
||||
color.b = self
|
||||
.b_mult
|
||||
.wrapping_mul_int(i16::from(color.b))
|
||||
.wrapping_add(self.b_add) as u8;
|
||||
color.a = self
|
||||
.a_mult
|
||||
.wrapping_mul_int(i16::from(color.a))
|
||||
.wrapping_add(self.a_add) as u8;
|
||||
color
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue