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:
relrelb 2022-09-10 01:24:13 +03:00 committed by relrelb
parent a17352a01a
commit e7643c731b
6 changed files with 127 additions and 108 deletions

View File

@ -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, &params);
.color_transform(x_min, y_min, x_max, y_max, color_transform.into());
}
return Ok(Value::Undefined);

View File

@ -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,50 +52,42 @@ 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,
alpha_multiplier: 0.0,
red_offset: 0.0,
green_offset: 0.0,
blue_offset: 0.0,
alpha_offset: 0.0,
},
red_multiplier: 0.0,
green_multiplier: 0.0,
blue_multiplier: 0.0,
alpha_multiplier: 0.0,
red_offset: 0.0,
green_offset: 0.0,
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,
}
}
}

View File

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

View File

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

View File

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

View File

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