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)?
|
.get("height", activation)?
|
||||||
.coerce_to_f64(activation)? as i32;
|
.coerce_to_f64(activation)? as i32;
|
||||||
|
|
||||||
let min_x = x.max(0) as u32;
|
let x_min = x.max(0) as u32;
|
||||||
let end_x = (x + width) as u32;
|
let x_max = (x + width) as u32;
|
||||||
let min_y = y.max(0) as u32;
|
let y_min = y.max(0) as u32;
|
||||||
let end_y = (y + height) as u32;
|
let y_max = (y + height) as u32;
|
||||||
|
|
||||||
if let Some(color_transform) = color_transform.as_color_transform_object() {
|
if let Some(color_transform) = color_transform.as_color_transform_object() {
|
||||||
let params = color_transform.get_params();
|
|
||||||
bitmap_data
|
bitmap_data
|
||||||
.bitmap_data()
|
.bitmap_data()
|
||||||
.write(activation.context.gc_context)
|
.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);
|
return Ok(Value::Undefined);
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::add_field_accessors;
|
use crate::add_field_accessors;
|
||||||
use crate::avm1::{Object, ScriptObject, TObject};
|
use crate::avm1::{Object, ScriptObject, TObject};
|
||||||
use crate::bitmap::color_transform_params::ColorTransformParams;
|
|
||||||
use crate::impl_custom_object;
|
use crate::impl_custom_object;
|
||||||
use gc_arena::{Collect, GcCell, MutationContext};
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
|
use ruffle_render::color_transform::ColorTransform;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use swf::Fixed8;
|
||||||
|
|
||||||
/// A ColorTransform
|
/// A ColorTransform
|
||||||
#[derive(Clone, Copy, Collect)]
|
#[derive(Clone, Copy, Collect)]
|
||||||
|
@ -15,21 +16,29 @@ pub struct ColorTransformObject<'gc>(GcCell<'gc, ColorTransformData<'gc>>);
|
||||||
pub struct ColorTransformData<'gc> {
|
pub struct ColorTransformData<'gc> {
|
||||||
/// The underlying script object.
|
/// The underlying script object.
|
||||||
base: ScriptObject<'gc>,
|
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<'_> {
|
impl fmt::Debug for ColorTransformObject<'_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let this = self.0.read();
|
let this = self.0.read();
|
||||||
f.debug_struct("ColorTransform")
|
f.debug_struct("ColorTransform")
|
||||||
.field("redMultiplier", &this.params.red_multiplier)
|
.field("redMultiplier", &this.red_multiplier)
|
||||||
.field("greenMultiplier", &this.params.green_multiplier)
|
.field("greenMultiplier", &this.green_multiplier)
|
||||||
.field("blueMultiplier", &this.params.blue_multiplier)
|
.field("blueMultiplier", &this.blue_multiplier)
|
||||||
.field("alphaMultiplier", &this.params.alpha_multiplier)
|
.field("alphaMultiplier", &this.alpha_multiplier)
|
||||||
.field("redOffset", &this.params.red_offset)
|
.field("redOffset", &this.red_offset)
|
||||||
.field("greenOffset", &this.params.green_offset)
|
.field("greenOffset", &this.green_offset)
|
||||||
.field("blueOffset", &this.params.blue_offset)
|
.field("blueOffset", &this.blue_offset)
|
||||||
.field("alphaOffset", &this.params.alpha_offset)
|
.field("alphaOffset", &this.alpha_offset)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,50 +52,42 @@ impl<'gc> ColorTransformObject<'gc> {
|
||||||
gc_context,
|
gc_context,
|
||||||
ColorTransformData {
|
ColorTransformData {
|
||||||
base: ScriptObject::new(gc_context, proto),
|
base: ScriptObject::new(gc_context, proto),
|
||||||
params: ColorTransformParams {
|
red_multiplier: 0.0,
|
||||||
red_multiplier: 0.0,
|
green_multiplier: 0.0,
|
||||||
green_multiplier: 0.0,
|
blue_multiplier: 0.0,
|
||||||
blue_multiplier: 0.0,
|
alpha_multiplier: 0.0,
|
||||||
alpha_multiplier: 0.0,
|
red_offset: 0.0,
|
||||||
red_offset: 0.0,
|
green_offset: 0.0,
|
||||||
green_offset: 0.0,
|
blue_offset: 0.0,
|
||||||
blue_offset: 0.0,
|
alpha_offset: 0.0,
|
||||||
alpha_offset: 0.0,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
add_field_accessors!(
|
add_field_accessors!(
|
||||||
[set_params, get_params, params, ColorTransformParams],
|
[set_red_multiplier, get_red_multiplier, red_multiplier, f64],
|
||||||
[
|
|
||||||
set_red_multiplier,
|
|
||||||
get_red_multiplier,
|
|
||||||
params.red_multiplier,
|
|
||||||
f64
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
set_green_multiplier,
|
set_green_multiplier,
|
||||||
get_green_multiplier,
|
get_green_multiplier,
|
||||||
params.green_multiplier,
|
green_multiplier,
|
||||||
f64
|
f64
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
set_blue_multiplier,
|
set_blue_multiplier,
|
||||||
get_blue_multiplier,
|
get_blue_multiplier,
|
||||||
params.blue_multiplier,
|
blue_multiplier,
|
||||||
f64
|
f64
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
set_alpha_multiplier,
|
set_alpha_multiplier,
|
||||||
get_alpha_multiplier,
|
get_alpha_multiplier,
|
||||||
params.alpha_multiplier,
|
alpha_multiplier,
|
||||||
f64
|
f64
|
||||||
],
|
],
|
||||||
[set_red_offset, get_red_offset, params.red_offset, f64],
|
[set_red_offset, get_red_offset, red_offset, f64],
|
||||||
[set_green_offset, get_green_offset, params.green_offset, f64],
|
[set_green_offset, get_green_offset, green_offset, f64],
|
||||||
[set_blue_offset, get_blue_offset, params.blue_offset, f64],
|
[set_blue_offset, get_blue_offset, blue_offset, f64],
|
||||||
[set_alpha_offset, get_alpha_offset, params.alpha_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);
|
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 bitmap_data;
|
||||||
pub mod color_transform_params;
|
|
||||||
pub mod turbulence;
|
pub mod turbulence;
|
||||||
|
|
||||||
/// Determine if a particular bitmap data size is valid.
|
/// 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::avm2::{Object as Avm2Object, Value as Avm2Value};
|
||||||
use crate::bitmap::color_transform_params::ColorTransformParams;
|
|
||||||
use crate::bitmap::turbulence::Turbulence;
|
use crate::bitmap::turbulence::Turbulence;
|
||||||
use crate::context::RenderContext;
|
use crate::context::RenderContext;
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use crate::display_object::DisplayObject;
|
use crate::display_object::DisplayObject;
|
||||||
use crate::display_object::TDisplayObject;
|
use crate::display_object::TDisplayObject;
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
use gc_arena::{Collect, GcCell};
|
||||||
use ruffle_render::backend::RenderBackend;
|
use ruffle_render::backend::RenderBackend;
|
||||||
use ruffle_render::bitmap::{Bitmap, BitmapFormat, BitmapHandle};
|
use ruffle_render::bitmap::{Bitmap, BitmapFormat, BitmapHandle};
|
||||||
|
use ruffle_render::color_transform::ColorTransform;
|
||||||
use ruffle_render::commands::{CommandHandler, CommandList};
|
use ruffle_render::commands::{CommandHandler, CommandList};
|
||||||
use ruffle_render::transform::Transform;
|
use ruffle_render::transform::Transform;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
use swf::BlendMode;
|
||||||
|
|
||||||
/// An implementation of the Lehmer/Park-Miller random number generator
|
/// An implementation of the Lehmer/Park-Miller random number generator
|
||||||
/// Uses the fixed parameters m = 2,147,483,647 and a = 16,807
|
/// 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! {
|
bitflags! {
|
||||||
pub struct ChannelOptions: u8 {
|
pub struct ChannelOptions: u8 {
|
||||||
const RED = 1 << 0;
|
const RED = 1 << 0;
|
||||||
|
@ -490,33 +505,22 @@ impl<'gc> BitmapData<'gc> {
|
||||||
|
|
||||||
pub fn color_transform(
|
pub fn color_transform(
|
||||||
&mut self,
|
&mut self,
|
||||||
min_x: u32,
|
x_min: u32,
|
||||||
min_y: u32,
|
y_min: u32,
|
||||||
end_x: u32,
|
x_max: u32,
|
||||||
end_y: u32,
|
y_max: u32,
|
||||||
color_transform: &ColorTransformParams,
|
color_transform: ColorTransform,
|
||||||
) {
|
) {
|
||||||
for x in min_x..end_x.min(self.width()) {
|
for x in x_min..x_max.min(self.width()) {
|
||||||
for y in min_y..end_y.min(self.height()) {
|
for y in y_min..y_max.min(self.height()) {
|
||||||
let color = self
|
let color = self.get_pixel_raw(x, y).unwrap().to_un_multiplied_alpha();
|
||||||
.get_pixel_raw(x, y)
|
|
||||||
.unwrap_or_else(|| 0.into())
|
|
||||||
.to_un_multiplied_alpha();
|
|
||||||
|
|
||||||
let alpha = ((color.alpha() as f32 * color_transform.alpha_multiplier as f32)
|
let color = color_transform * swf::Color::from(color);
|
||||||
+ 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;
|
|
||||||
|
|
||||||
self.set_pixel32_raw(
|
self.set_pixel32_raw(
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
Color::argb(alpha, red, green, blue)
|
Color::from(color).to_premultiplied_alpha(self.transparency()),
|
||||||
.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)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub struct ColorTransform {
|
pub struct ColorTransform {
|
||||||
|
@ -28,17 +29,16 @@ impl From<swf::ColorTransform> for ColorTransform {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorTransform {
|
impl ColorTransform {
|
||||||
#[allow(clippy::float_cmp)]
|
pub const IDENTITY: Self = Self {
|
||||||
pub fn is_identity(&self) -> bool {
|
r_mult: Fixed8::ONE,
|
||||||
self.r_mult.is_one()
|
b_mult: Fixed8::ONE,
|
||||||
&& self.g_mult.is_one()
|
g_mult: Fixed8::ONE,
|
||||||
&& self.b_mult.is_one()
|
a_mult: Fixed8::ONE,
|
||||||
&& self.a_mult.is_one()
|
r_add: 0,
|
||||||
&& self.r_add == 0
|
b_add: 0,
|
||||||
&& self.g_add == 0
|
g_add: 0,
|
||||||
&& self.b_add == 0
|
a_add: 0,
|
||||||
&& self.a_add == 0
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the multiplicative component of this color transform in RGBA order
|
/// Returns the multiplicative component of this color transform in RGBA order
|
||||||
/// with the values normalized [0.0, 1.0].
|
/// with the values normalized [0.0, 1.0].
|
||||||
|
@ -63,7 +63,7 @@ impl ColorTransform {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the multiplicate component of this color transform.
|
/// 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.r_mult = Fixed8::from_f32(f32::from(color.r) / 255.0);
|
||||||
self.g_mult = Fixed8::from_f32(f32::from(color.g) / 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);
|
self.b_mult = Fixed8::from_f32(f32::from(color.b) / 255.0);
|
||||||
|
@ -72,29 +72,20 @@ impl ColorTransform {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ColorTransform {
|
impl Default for ColorTransform {
|
||||||
fn default() -> ColorTransform {
|
fn default() -> Self {
|
||||||
ColorTransform {
|
Self::IDENTITY
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Mul for ColorTransform {
|
impl Mul for ColorTransform {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn mul(self, rhs: Self) -> Self {
|
fn mul(self, rhs: Self) -> Self {
|
||||||
ColorTransform {
|
Self {
|
||||||
r_mult: self.r_mult.wrapping_mul(rhs.r_mult),
|
r_mult: self.r_mult.wrapping_mul(rhs.r_mult),
|
||||||
g_mult: self.g_mult.wrapping_mul(rhs.g_mult),
|
g_mult: self.g_mult.wrapping_mul(rhs.g_mult),
|
||||||
b_mult: self.b_mult.wrapping_mul(rhs.b_mult),
|
b_mult: self.b_mult.wrapping_mul(rhs.b_mult),
|
||||||
a_mult: self.a_mult.wrapping_mul(rhs.a_mult),
|
a_mult: self.a_mult.wrapping_mul(rhs.a_mult),
|
||||||
|
|
||||||
r_add: self
|
r_add: self
|
||||||
.r_add
|
.r_add
|
||||||
.wrapping_add(self.r_mult.wrapping_mul_int(rhs.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) {
|
fn mul_assign(&mut self, rhs: Self) {
|
||||||
*self = *self * rhs;
|
*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