avm1: Migrate `DisplacementMapFilter` to `NativeObject`
This commit is contained in:
parent
420643b045
commit
ee30646745
|
@ -26,7 +26,7 @@ pub(crate) mod context_menu;
|
|||
pub(crate) mod context_menu_item;
|
||||
pub mod convolution_filter;
|
||||
pub(crate) mod date;
|
||||
pub mod displacement_map_filter;
|
||||
pub(crate) mod displacement_map_filter;
|
||||
pub(crate) mod drop_shadow_filter;
|
||||
pub(crate) mod error;
|
||||
mod external_interface;
|
||||
|
@ -499,8 +499,6 @@ pub struct SystemPrototypes<'gc> {
|
|||
pub context_menu_item_constructor: Object<'gc>,
|
||||
pub bitmap_filter: Object<'gc>,
|
||||
pub bitmap_filter_constructor: Object<'gc>,
|
||||
pub displacement_map_filter: Object<'gc>,
|
||||
pub displacement_map_filter_constructor: Object<'gc>,
|
||||
pub convolution_filter: Object<'gc>,
|
||||
pub convolution_filter_constructor: Object<'gc>,
|
||||
pub gradient_bevel_filter: Object<'gc>,
|
||||
|
@ -508,7 +506,7 @@ pub struct SystemPrototypes<'gc> {
|
|||
pub gradient_glow_filter: Object<'gc>,
|
||||
pub gradient_glow_filter_constructor: Object<'gc>,
|
||||
pub date_constructor: Object<'gc>,
|
||||
pub bitmap_data_constructor: Object<'gc>,
|
||||
pub bitmap_data: Object<'gc>,
|
||||
pub video: Object<'gc>,
|
||||
pub video_constructor: Object<'gc>,
|
||||
}
|
||||
|
@ -760,15 +758,8 @@ pub fn create_globals<'gc>(
|
|||
let color_matrix_filter =
|
||||
color_matrix_filter::create_constructor(context, bitmap_filter_proto, function_proto);
|
||||
|
||||
let displacement_map_filter_proto =
|
||||
displacement_map_filter::create_proto(context, bitmap_filter_proto, function_proto);
|
||||
let displacement_map_filter = FunctionObject::constructor(
|
||||
gc_context,
|
||||
Executable::Native(displacement_map_filter::constructor),
|
||||
constructor_to_fn!(displacement_map_filter::constructor),
|
||||
function_proto,
|
||||
displacement_map_filter_proto,
|
||||
);
|
||||
let displacement_map_filter =
|
||||
displacement_map_filter::create_constructor(context, bitmap_filter_proto, function_proto);
|
||||
|
||||
let convolution_filter_proto =
|
||||
convolution_filter::create_proto(context, bitmap_filter_proto, function_proto);
|
||||
|
@ -862,7 +853,8 @@ pub fn create_globals<'gc>(
|
|||
Attribute::empty(),
|
||||
);
|
||||
|
||||
let bitmap_data = bitmap_data::create_constructor(context, object_proto, function_proto);
|
||||
let bitmap_data_proto = ScriptObject::new(context.gc_context, Some(object_proto));
|
||||
let bitmap_data = bitmap_data::create_constructor(context, bitmap_data_proto, function_proto);
|
||||
|
||||
display.define_value(
|
||||
gc_context,
|
||||
|
@ -1118,8 +1110,6 @@ pub fn create_globals<'gc>(
|
|||
context_menu_item_constructor: context_menu_item,
|
||||
bitmap_filter: bitmap_filter_proto,
|
||||
bitmap_filter_constructor: bitmap_filter,
|
||||
displacement_map_filter: displacement_map_filter_proto,
|
||||
displacement_map_filter_constructor: displacement_map_filter,
|
||||
convolution_filter: convolution_filter_proto,
|
||||
convolution_filter_constructor: convolution_filter,
|
||||
gradient_bevel_filter: gradient_bevel_filter_proto,
|
||||
|
@ -1127,7 +1117,7 @@ pub fn create_globals<'gc>(
|
|||
gradient_glow_filter: gradient_glow_filter_proto,
|
||||
gradient_glow_filter_constructor: gradient_glow_filter,
|
||||
date_constructor: date,
|
||||
bitmap_data_constructor: bitmap_data,
|
||||
bitmap_data: bitmap_data_proto.into(),
|
||||
video: video_proto,
|
||||
video_constructor: video,
|
||||
},
|
||||
|
|
|
@ -1417,18 +1417,17 @@ fn load_bitmap<'gc>(
|
|||
|
||||
pub fn create_constructor<'gc>(
|
||||
context: &mut GcContext<'_, 'gc>,
|
||||
proto: Object<'gc>,
|
||||
proto: ScriptObject<'gc>,
|
||||
fn_proto: Object<'gc>,
|
||||
) -> Object<'gc> {
|
||||
let bitmap_data_proto = ScriptObject::new(context.gc_context, Some(proto));
|
||||
define_properties_on(PROTO_DECLS, context, bitmap_data_proto, fn_proto);
|
||||
define_properties_on(PROTO_DECLS, context, proto, fn_proto);
|
||||
|
||||
let bitmap_data_constructor = FunctionObject::constructor(
|
||||
context.gc_context,
|
||||
Executable::Native(constructor),
|
||||
constructor_to_fn!(constructor),
|
||||
fn_proto,
|
||||
bitmap_data_proto.into(),
|
||||
proto.into(),
|
||||
);
|
||||
let object = bitmap_data_constructor.raw_script_object();
|
||||
define_properties_on(OBJECT_DECLS, context, object, fn_proto);
|
||||
|
|
|
@ -40,6 +40,11 @@ pub fn clone<'gc>(
|
|||
NativeObject::ColorMatrixFilter(color_matrix_filter) => NativeObject::ColorMatrixFilter(
|
||||
color_matrix_filter.duplicate(activation.context.gc_context),
|
||||
),
|
||||
NativeObject::DisplacementMapFilter(displacement_map_filter) => {
|
||||
NativeObject::DisplacementMapFilter(
|
||||
displacement_map_filter.duplicate(activation.context.gc_context),
|
||||
)
|
||||
}
|
||||
_ => NativeObject::None,
|
||||
};
|
||||
if !matches!(native, NativeObject::None) {
|
||||
|
@ -59,41 +64,6 @@ pub fn clone<'gc>(
|
|||
return Ok(cloned.into());
|
||||
}
|
||||
|
||||
if let Some(this) = this.as_displacement_map_filter_object() {
|
||||
let proto = activation
|
||||
.context
|
||||
.avm1
|
||||
.prototypes()
|
||||
.displacement_map_filter_constructor;
|
||||
|
||||
let map_bitmap = this.get("mapBitmap", activation)?;
|
||||
let map_point = this.get("mapPoint", activation)?;
|
||||
let component_x = this.get("componentX", activation)?;
|
||||
let component_y = this.get("componentY", activation)?;
|
||||
let scale_x = this.get("scaleX", activation)?;
|
||||
let scale_y = this.get("scaleY", activation)?;
|
||||
let mode = this.get("mode", activation)?;
|
||||
let color = this.get("color", activation)?;
|
||||
let alpha = this.get("alpha", activation)?;
|
||||
|
||||
let cloned = proto.construct(
|
||||
activation,
|
||||
&[
|
||||
map_bitmap,
|
||||
map_point,
|
||||
component_x,
|
||||
component_y,
|
||||
scale_x,
|
||||
scale_y,
|
||||
mode,
|
||||
color,
|
||||
alpha,
|
||||
],
|
||||
)?;
|
||||
|
||||
return Ok(cloned);
|
||||
}
|
||||
|
||||
if let Some(this) = this.as_convolution_filter_object() {
|
||||
let proto = activation
|
||||
.context
|
||||
|
|
|
@ -1,311 +1,388 @@
|
|||
//! flash.filters.DisplacementMapFilter object
|
||||
|
||||
use crate::avm1::activation::Activation;
|
||||
use crate::avm1::clamp::Clamp;
|
||||
use crate::avm1::error::Error;
|
||||
use crate::avm1::object::displacement_map_filter::DisplacementMapFilterObject;
|
||||
use crate::avm1::function::{Executable, FunctionObject};
|
||||
use crate::avm1::object::NativeObject;
|
||||
use crate::avm1::property_decl::{define_properties_on, Declaration};
|
||||
use crate::avm1::{Object, TObject, Value};
|
||||
use crate::context::GcContext;
|
||||
use crate::string::{AvmString, WStr};
|
||||
use crate::avm1::{Activation, Error, Object, ScriptObject, TObject, Value};
|
||||
use crate::bitmap::bitmap_data::BitmapDataWrapper;
|
||||
use crate::context::{GcContext, UpdateContext};
|
||||
use crate::string::{AvmString, FromWStr, WStr};
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use std::convert::Infallible;
|
||||
use std::fmt::Debug;
|
||||
use swf::{Color, Point};
|
||||
|
||||
const PROTO_DECLS: &[Declaration] = declare_properties! {
|
||||
"alpha" => property(alpha, set_alpha);
|
||||
"color" => property(color, set_color);
|
||||
"componentX" => property(component_x, set_component_x);
|
||||
"componentY" => property(component_y, set_component_y);
|
||||
"mapBitmap" => property(map_bitmap, set_map_bitmap);
|
||||
"mapPoint" => property(map_point, set_map_point);
|
||||
"mode" => property(mode, set_mode);
|
||||
"scaleX" => property(scale_x, set_scale_x);
|
||||
"scaleY" => property(scale_y, set_scale_y);
|
||||
};
|
||||
|
||||
pub fn constructor<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
set_map_bitmap(activation, this, args.get(0..1).unwrap_or_default())?;
|
||||
set_map_point(activation, this, args.get(1..2).unwrap_or_default())?;
|
||||
set_component_x(activation, this, args.get(2..3).unwrap_or_default())?;
|
||||
set_component_y(activation, this, args.get(3..4).unwrap_or_default())?;
|
||||
set_scale_x(activation, this, args.get(4..5).unwrap_or_default())?;
|
||||
set_scale_y(activation, this, args.get(5..6).unwrap_or_default())?;
|
||||
set_mode(activation, this, args.get(6..7).unwrap_or_default())?;
|
||||
set_color(activation, this, args.get(7..8).unwrap_or_default())?;
|
||||
set_alpha(activation, this, args.get(8..9).unwrap_or_default())?;
|
||||
|
||||
Ok(this.into())
|
||||
#[derive(Copy, Clone, Collect, Debug, Default)]
|
||||
#[collect(require_static)]
|
||||
enum Mode {
|
||||
#[default]
|
||||
Wrap,
|
||||
Clamp,
|
||||
Ignore,
|
||||
Color,
|
||||
}
|
||||
|
||||
pub fn alpha<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(filter) = this.as_displacement_map_filter_object() {
|
||||
return Ok(filter.alpha().into());
|
||||
impl From<Mode> for &'static WStr {
|
||||
fn from(mode: Mode) -> &'static WStr {
|
||||
match mode {
|
||||
Mode::Wrap => WStr::from_units(b"wrap"),
|
||||
Mode::Clamp => WStr::from_units(b"clamp"),
|
||||
Mode::Ignore => WStr::from_units(b"ignore"),
|
||||
Mode::Color => WStr::from_units(b"color"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWStr for Mode {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_wstr(s: &WStr) -> Result<Self, Self::Err> {
|
||||
if s.eq_ignore_case(WStr::from_units(b"clamp")) {
|
||||
Ok(Self::Clamp)
|
||||
} else if s.eq_ignore_case(WStr::from_units(b"ignore")) {
|
||||
Ok(Self::Ignore)
|
||||
} else if s.eq_ignore_case(WStr::from_units(b"color")) {
|
||||
Ok(Self::Color)
|
||||
} else {
|
||||
Ok(Self::Wrap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Collect, Debug, Default)]
|
||||
#[collect(no_drop)]
|
||||
struct DisplacementMapFilterData<'gc> {
|
||||
map_bitmap: Option<BitmapDataWrapper<'gc>>,
|
||||
#[collect(require_static)]
|
||||
map_point: Point<i32>,
|
||||
component_x: i32,
|
||||
component_y: i32,
|
||||
scale_x: f32,
|
||||
scale_y: f32,
|
||||
mode: Mode,
|
||||
#[collect(require_static)]
|
||||
color: Color,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Collect)]
|
||||
#[collect(no_drop)]
|
||||
#[repr(transparent)]
|
||||
pub struct DisplacementMapFilter<'gc>(GcCell<'gc, DisplacementMapFilterData<'gc>>);
|
||||
|
||||
impl<'gc> DisplacementMapFilter<'gc> {
|
||||
fn new(activation: &mut Activation<'_, 'gc>, args: &[Value<'gc>]) -> Result<Self, Error<'gc>> {
|
||||
let displacement_map_filter = Self(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
Default::default(),
|
||||
));
|
||||
displacement_map_filter.set_map_bitmap(activation, args.get(0))?;
|
||||
displacement_map_filter.set_map_point(activation, args.get(1))?;
|
||||
displacement_map_filter.set_component_x(activation, args.get(2))?;
|
||||
displacement_map_filter.set_component_y(activation, args.get(3))?;
|
||||
displacement_map_filter.set_scale_x(activation, args.get(4))?;
|
||||
displacement_map_filter.set_scale_y(activation, args.get(5))?;
|
||||
displacement_map_filter.set_mode(activation, args.get(6))?;
|
||||
displacement_map_filter.set_color(activation, args.get(7))?;
|
||||
displacement_map_filter.set_alpha(activation, args.get(8))?;
|
||||
Ok(displacement_map_filter)
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn set_alpha<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let alpha = args
|
||||
.get(0)
|
||||
.unwrap_or(&0.into())
|
||||
.coerce_to_f64(activation)
|
||||
.map(|x| x.clamp_also_nan(0.0, 1.0))?;
|
||||
|
||||
if let Some(filter) = this.as_displacement_map_filter_object() {
|
||||
filter.set_alpha(activation.context.gc_context, alpha);
|
||||
pub(crate) fn duplicate(&self, gc_context: MutationContext<'gc, '_>) -> Self {
|
||||
Self(GcCell::allocate(gc_context, self.0.read().clone()))
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn color<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
return Ok(object.color().into());
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn set_color<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let color = args
|
||||
.get(0)
|
||||
.unwrap_or(&0x000000.into())
|
||||
.coerce_to_u32(activation)?;
|
||||
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
object.set_color(activation.context.gc_context, color & 0xFFFFFF);
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn component_x<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
return Ok(object.component_x().into());
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn set_component_x<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let component = args.get(0).unwrap_or(&0.into()).coerce_to_i32(activation)?;
|
||||
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
object.set_component_x(activation.context.gc_context, component);
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn component_y<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
return Ok(object.component_y().into());
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn set_component_y<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let component = args.get(0).unwrap_or(&0.into()).coerce_to_i32(activation)?;
|
||||
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
object.set_component_y(activation.context.gc_context, component);
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn map_bitmap<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
if let Some(map_bitmap) = object.map_bitmap() {
|
||||
return Ok(map_bitmap.into());
|
||||
fn map_bitmap(&self, context: &mut UpdateContext<'_, 'gc>) -> Option<Object<'gc>> {
|
||||
if let Some(map_bitmap) = self.0.read().map_bitmap {
|
||||
let proto = context.avm1.prototypes().bitmap_data;
|
||||
let result = ScriptObject::new(context.gc_context, Some(proto));
|
||||
result.set_native(context.gc_context, NativeObject::BitmapData(map_bitmap));
|
||||
Some(result.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn set_map_bitmap<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
if let [Value::Object(map_bitmap), ..] = args {
|
||||
if let NativeObject::BitmapData(_) = map_bitmap.native() {
|
||||
object.set_map_bitmap(activation.context.gc_context, Some(*map_bitmap));
|
||||
fn set_map_bitmap(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
value: Option<&Value<'gc>>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
if let Some(Value::Object(object)) = value {
|
||||
if let NativeObject::BitmapData(bitmap_data) = object.native() {
|
||||
self.0.write(activation.context.gc_context).map_bitmap = Some(bitmap_data);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn map_point<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
let (x, y) = object.map_point();
|
||||
|
||||
let proto = activation.context.avm1.prototypes().point_constructor;
|
||||
let point = proto.construct(activation, &[x.into(), y.into()])?;
|
||||
return Ok(point);
|
||||
fn map_point(&self, activation: &mut Activation<'_, 'gc>) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let map_point = self.0.read().map_point;
|
||||
let args = &[map_point.x.into(), map_point.y.into()];
|
||||
let constructor = activation.context.avm1.prototypes().point_constructor;
|
||||
constructor.construct(activation, args)
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
fn set_map_point(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
value: Option<&Value<'gc>>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
if let Some(Value::Object(object)) = value {
|
||||
if let Some(x) = object.get_local_stored("x", activation) {
|
||||
let x = x.coerce_to_f64(activation)?.clamp_to_i32();
|
||||
if let Some(y) = object.get_local_stored("y", activation) {
|
||||
let y = y.coerce_to_f64(activation)?.clamp_to_i32();
|
||||
self.0.write(activation.context.gc_context).map_point = Point::new(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn component_x(&self) -> i32 {
|
||||
self.0.read().component_x
|
||||
}
|
||||
|
||||
fn set_component_x(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
value: Option<&Value<'gc>>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
if let Some(value) = value {
|
||||
let component_x = value.coerce_to_i32(activation)?;
|
||||
self.0.write(activation.context.gc_context).component_x = component_x;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn component_y(&self) -> i32 {
|
||||
self.0.read().component_y
|
||||
}
|
||||
|
||||
fn set_component_y(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
value: Option<&Value<'gc>>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
if let Some(value) = value {
|
||||
let component_y = value.coerce_to_i32(activation)?;
|
||||
self.0.write(activation.context.gc_context).component_y = component_y;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scale_x(&self) -> f32 {
|
||||
self.0.read().scale_x
|
||||
}
|
||||
|
||||
fn set_scale_x(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
value: Option<&Value<'gc>>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
if let Some(value) = value {
|
||||
const MAX: f64 = u16::MAX as f64;
|
||||
const MIN: f64 = -MAX;
|
||||
let scale_x = value.coerce_to_f64(activation)?.clamp_also_nan(MIN, MAX);
|
||||
self.0.write(activation.context.gc_context).scale_x = scale_x as f32;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scale_y(&self) -> f32 {
|
||||
self.0.read().scale_y
|
||||
}
|
||||
|
||||
fn set_scale_y(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
value: Option<&Value<'gc>>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
if let Some(value) = value {
|
||||
const MAX: f64 = u16::MAX as f64;
|
||||
const MIN: f64 = -MAX;
|
||||
let scale_y = value.coerce_to_f64(activation)?.clamp_also_nan(MIN, MAX);
|
||||
self.0.write(activation.context.gc_context).scale_y = scale_y as f32;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mode(&self) -> Mode {
|
||||
self.0.read().mode
|
||||
}
|
||||
|
||||
fn set_mode(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
value: Option<&Value<'gc>>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
if let Some(value) = value {
|
||||
let mode = value.coerce_to_string(activation)?.parse().unwrap();
|
||||
self.0.write(activation.context.gc_context).mode = mode;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn color(&self) -> Color {
|
||||
self.0.read().color.clone()
|
||||
}
|
||||
|
||||
fn set_color(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
value: Option<&Value<'gc>>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
if let Some(value) = value {
|
||||
let color = Color::from_rgb(value.coerce_to_u32(activation)?, u8::MAX);
|
||||
self.0.write(activation.context.gc_context).color = color;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_alpha(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
value: Option<&Value<'gc>>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
if let Some(value) = value {
|
||||
let alpha = value.coerce_to_f64(activation)?.clamp_also_nan(0.0, 1.0);
|
||||
self.0.write(activation.context.gc_context).color.a = (alpha * 255.0) as u8;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_map_point<'gc>(
|
||||
macro_rules! displacement_map_filter_method {
|
||||
($index:literal) => {
|
||||
|activation, this, args| method(activation, this, args, $index)
|
||||
};
|
||||
}
|
||||
|
||||
const PROTO_DECLS: &[Declaration] = declare_properties! {
|
||||
"mapBitmap" => property(displacement_map_filter_method!(1), displacement_map_filter_method!(2));
|
||||
"mapPoint" => property(displacement_map_filter_method!(3), displacement_map_filter_method!(4));
|
||||
"componentX" => property(displacement_map_filter_method!(5), displacement_map_filter_method!(6));
|
||||
"componentY" => property(displacement_map_filter_method!(7), displacement_map_filter_method!(8));
|
||||
"scaleX" => property(displacement_map_filter_method!(9), displacement_map_filter_method!(10));
|
||||
"scaleY" => property(displacement_map_filter_method!(11), displacement_map_filter_method!(12));
|
||||
"mode" => property(displacement_map_filter_method!(13), displacement_map_filter_method!(14));
|
||||
"color" => property(displacement_map_filter_method!(15), displacement_map_filter_method!(16));
|
||||
"alpha" => property(displacement_map_filter_method!(17), displacement_map_filter_method!(18));
|
||||
};
|
||||
|
||||
fn method<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
index: u8,
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let obj = args
|
||||
.get(0)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.coerce_to_object(activation);
|
||||
const CONSTRUCTOR: u8 = 0;
|
||||
const GET_MAP_BITMAP: u8 = 1;
|
||||
const SET_MAP_BITMAP: u8 = 2;
|
||||
const GET_MAP_POINT: u8 = 3;
|
||||
const SET_MAP_POINT: u8 = 4;
|
||||
const GET_COMPONENT_X: u8 = 5;
|
||||
const SET_COMPONENT_X: u8 = 6;
|
||||
const GET_COMPONENT_Y: u8 = 7;
|
||||
const SET_COMPONENT_Y: u8 = 8;
|
||||
const GET_SCALE_X: u8 = 9;
|
||||
const SET_SCALE_X: u8 = 10;
|
||||
const GET_SCALE_Y: u8 = 11;
|
||||
const SET_SCALE_Y: u8 = 12;
|
||||
const GET_MODE: u8 = 13;
|
||||
const SET_MODE: u8 = 14;
|
||||
const GET_COLOR: u8 = 15;
|
||||
const SET_COLOR: u8 = 16;
|
||||
const GET_ALPHA: u8 = 17;
|
||||
const SET_ALPHA: u8 = 18;
|
||||
|
||||
let x = obj.get("x", activation)?.coerce_to_i32(activation)?;
|
||||
let y = obj.get("y", activation)?.coerce_to_i32(activation)?;
|
||||
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
object.set_map_point(activation.context.gc_context, (x, y));
|
||||
if index == CONSTRUCTOR {
|
||||
let displacement_map_filter = DisplacementMapFilter::new(activation, args)?;
|
||||
this.set_native(
|
||||
activation.context.gc_context,
|
||||
NativeObject::DisplacementMapFilter(displacement_map_filter),
|
||||
);
|
||||
return Ok(this.into());
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
let this = match this.native() {
|
||||
NativeObject::DisplacementMapFilter(displacement_map_filter) => displacement_map_filter,
|
||||
_ => return Ok(Value::Undefined),
|
||||
};
|
||||
|
||||
Ok(match index {
|
||||
GET_MAP_BITMAP => this
|
||||
.map_bitmap(&mut activation.context)
|
||||
.map_or(Value::Undefined, Value::from),
|
||||
SET_MAP_BITMAP => {
|
||||
this.set_map_bitmap(activation, args.get(0))?;
|
||||
Value::Undefined
|
||||
}
|
||||
GET_MAP_POINT => this.map_point(activation)?,
|
||||
SET_MAP_POINT => {
|
||||
this.set_map_point(activation, args.get(0))?;
|
||||
Value::Undefined
|
||||
}
|
||||
GET_COMPONENT_X => this.component_x().into(),
|
||||
SET_COMPONENT_X => {
|
||||
this.set_component_x(activation, args.get(0))?;
|
||||
Value::Undefined
|
||||
}
|
||||
GET_COMPONENT_Y => this.component_y().into(),
|
||||
SET_COMPONENT_Y => {
|
||||
this.set_component_y(activation, args.get(0))?;
|
||||
Value::Undefined
|
||||
}
|
||||
GET_SCALE_X => this.scale_x().into(),
|
||||
SET_SCALE_X => {
|
||||
this.set_scale_x(activation, args.get(0))?;
|
||||
Value::Undefined
|
||||
}
|
||||
GET_SCALE_Y => this.scale_y().into(),
|
||||
SET_SCALE_Y => {
|
||||
this.set_scale_y(activation, args.get(0))?;
|
||||
Value::Undefined
|
||||
}
|
||||
GET_MODE => {
|
||||
let mode: &WStr = this.mode().into();
|
||||
AvmString::from(mode).into()
|
||||
}
|
||||
SET_MODE => {
|
||||
this.set_mode(activation, args.get(0))?;
|
||||
Value::Undefined
|
||||
}
|
||||
GET_COLOR => this.color().to_rgb().into(),
|
||||
SET_COLOR => {
|
||||
this.set_color(activation, args.get(0))?;
|
||||
Value::Undefined
|
||||
}
|
||||
GET_ALPHA => (this.color().a as f64 / 255.0).into(),
|
||||
SET_ALPHA => {
|
||||
this.set_alpha(activation, args.get(0))?;
|
||||
Value::Undefined
|
||||
}
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn mode<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
let mode: &WStr = object.mode().into();
|
||||
return Ok(AvmString::from(mode).into());
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn set_mode<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let mode = args
|
||||
.get(0)
|
||||
.unwrap_or(&"wrap".into())
|
||||
.coerce_to_string(activation)?;
|
||||
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
object.set_mode(activation.context.gc_context, mode.as_wstr().into());
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn scale_x<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
return Ok(object.scale_x().into());
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn set_scale_x<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let scale = args.get(0).unwrap_or(&0.into()).coerce_to_f64(activation)?;
|
||||
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
object.set_scale_x(activation.context.gc_context, scale);
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn scale_y<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
return Ok(object.scale_y().into());
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn set_scale_y<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let scale = args.get(0).unwrap_or(&0.into()).coerce_to_f64(activation)?;
|
||||
|
||||
if let Some(object) = this.as_displacement_map_filter_object() {
|
||||
object.set_scale_y(activation.context.gc_context, scale);
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn create_proto<'gc>(
|
||||
pub fn create_constructor<'gc>(
|
||||
context: &mut GcContext<'_, 'gc>,
|
||||
proto: Object<'gc>,
|
||||
fn_proto: Object<'gc>,
|
||||
) -> Object<'gc> {
|
||||
let filter = DisplacementMapFilterObject::empty_object(context.gc_context, proto);
|
||||
let object = filter.raw_script_object();
|
||||
define_properties_on(PROTO_DECLS, context, object, fn_proto);
|
||||
filter.into()
|
||||
let displacement_map_filter_proto = ScriptObject::new(context.gc_context, Some(proto));
|
||||
define_properties_on(
|
||||
PROTO_DECLS,
|
||||
context,
|
||||
displacement_map_filter_proto,
|
||||
fn_proto,
|
||||
);
|
||||
FunctionObject::constructor(
|
||||
context.gc_context,
|
||||
Executable::Native(displacement_map_filter_method!(0)),
|
||||
constructor_to_fn!(displacement_map_filter_method!(0)),
|
||||
fn_proto,
|
||||
displacement_map_filter_proto.into(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@ use crate::avm1::globals::blur_filter::BlurFilter;
|
|||
use crate::avm1::globals::color_matrix_filter::ColorMatrixFilter;
|
||||
use crate::avm1::globals::color_transform::ColorTransformObject;
|
||||
use crate::avm1::globals::date::Date;
|
||||
use crate::avm1::globals::displacement_map_filter::DisplacementMapFilter;
|
||||
use crate::avm1::globals::drop_shadow_filter::DropShadowFilter;
|
||||
use crate::avm1::globals::glow_filter::GlowFilter;
|
||||
use crate::avm1::object::array_object::ArrayObject;
|
||||
use crate::avm1::object::convolution_filter::ConvolutionFilterObject;
|
||||
use crate::avm1::object::displacement_map_filter::DisplacementMapFilterObject;
|
||||
use crate::avm1::object::gradient_bevel_filter::GradientBevelFilterObject;
|
||||
use crate::avm1::object::gradient_glow_filter::GradientGlowFilterObject;
|
||||
use crate::avm1::object::shared_object::SharedObject;
|
||||
|
@ -34,7 +34,6 @@ use std::fmt::Debug;
|
|||
pub mod array_object;
|
||||
pub mod convolution_filter;
|
||||
mod custom_object;
|
||||
pub mod displacement_map_filter;
|
||||
pub mod gradient_bevel_filter;
|
||||
pub mod gradient_glow_filter;
|
||||
pub mod script_object;
|
||||
|
@ -57,6 +56,7 @@ pub enum NativeObject<'gc> {
|
|||
GlowFilter(GlowFilter<'gc>),
|
||||
DropShadowFilter(DropShadowFilter<'gc>),
|
||||
ColorMatrixFilter(ColorMatrixFilter<'gc>),
|
||||
DisplacementMapFilter(DisplacementMapFilter<'gc>),
|
||||
ColorTransform(GcCell<'gc, ColorTransformObject>),
|
||||
TextFormat(GcCell<'gc, TextFormat>),
|
||||
NetStream(NetStream<'gc>),
|
||||
|
@ -81,7 +81,6 @@ pub enum NativeObject<'gc> {
|
|||
FunctionObject(FunctionObject<'gc>),
|
||||
SharedObject(SharedObject<'gc>),
|
||||
TransformObject(TransformObject<'gc>),
|
||||
DisplacementMapFilterObject(DisplacementMapFilterObject<'gc>),
|
||||
ConvolutionFilterObject(ConvolutionFilterObject<'gc>),
|
||||
GradientBevelFilterObject(GradientBevelFilterObject<'gc>),
|
||||
GradientGlowFilterObject(GradientGlowFilterObject<'gc>),
|
||||
|
@ -606,11 +605,6 @@ pub trait TObject<'gc>: 'gc + Collect + Into<Object<'gc>> + Clone + Copy {
|
|||
None
|
||||
}
|
||||
|
||||
/// Get the underlying `DisplacementMapFilterObject`, if it exists
|
||||
fn as_displacement_map_filter_object(&self) -> Option<DisplacementMapFilterObject<'gc>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Get the underlying `ConvolutionFilterObject`, if it exists
|
||||
fn as_convolution_filter_object(&self) -> Option<ConvolutionFilterObject<'gc>> {
|
||||
None
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
use crate::add_field_accessors;
|
||||
use crate::avm1::{Object, ScriptObject, TObject};
|
||||
use crate::impl_custom_object;
|
||||
use crate::string::WStr;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Collect)]
|
||||
#[collect(no_drop)]
|
||||
pub enum DisplacementMapFilterMode {
|
||||
Wrap,
|
||||
Clamp,
|
||||
Ignore,
|
||||
Color,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a WStr> for DisplacementMapFilterMode {
|
||||
fn from(v: &'a WStr) -> DisplacementMapFilterMode {
|
||||
if v == b"clamp" {
|
||||
DisplacementMapFilterMode::Clamp
|
||||
} else if v == b"ignore" {
|
||||
DisplacementMapFilterMode::Ignore
|
||||
} else if v == b"color" {
|
||||
DisplacementMapFilterMode::Color
|
||||
} else {
|
||||
DisplacementMapFilterMode::Wrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DisplacementMapFilterMode> for &'static WStr {
|
||||
fn from(v: DisplacementMapFilterMode) -> Self {
|
||||
let s: &[u8] = match v {
|
||||
DisplacementMapFilterMode::Wrap => b"wrap",
|
||||
DisplacementMapFilterMode::Clamp => b"clamp",
|
||||
DisplacementMapFilterMode::Ignore => b"ignore",
|
||||
DisplacementMapFilterMode::Color => b"color",
|
||||
};
|
||||
WStr::from_units(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// A DisplacementMapFilter
|
||||
#[derive(Clone, Copy, Collect)]
|
||||
#[collect(no_drop)]
|
||||
pub struct DisplacementMapFilterObject<'gc>(GcCell<'gc, DisplacementMapFilterData<'gc>>);
|
||||
|
||||
#[derive(Clone, Collect)]
|
||||
#[collect(no_drop)]
|
||||
pub struct DisplacementMapFilterData<'gc> {
|
||||
/// The underlying script object.
|
||||
base: ScriptObject<'gc>,
|
||||
|
||||
alpha: f64,
|
||||
color: u32,
|
||||
component_x: i32,
|
||||
component_y: i32,
|
||||
map_bitmap: Option<Object<'gc>>,
|
||||
map_point: (i32, i32),
|
||||
mode: DisplacementMapFilterMode,
|
||||
scale_x: f64,
|
||||
scale_y: f64,
|
||||
}
|
||||
|
||||
impl fmt::Debug for DisplacementMapFilterObject<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let this = self.0.read();
|
||||
f.debug_struct("DisplacementMapFilter")
|
||||
.field("alpha", &this.alpha)
|
||||
.field("color", &this.color)
|
||||
.field("componentX", &this.component_x)
|
||||
.field("componentY", &this.component_y)
|
||||
.field("mapBitmap", &this.map_bitmap)
|
||||
.field("mapPoint", &this.map_point)
|
||||
.field("mode", &this.mode)
|
||||
.field("scaleX", &this.scale_x)
|
||||
.field("scaleY", &this.scale_y)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> DisplacementMapFilterObject<'gc> {
|
||||
add_field_accessors!(
|
||||
[set_alpha, alpha, alpha, f64],
|
||||
[set_color, color, color, u32],
|
||||
[set_component_x, component_x, component_x, i32],
|
||||
[set_component_y, component_y, component_y, i32],
|
||||
[set_map_bitmap, map_bitmap, map_bitmap, Option<Object<'gc>>],
|
||||
[set_map_point, map_point, map_point, (i32, i32)],
|
||||
[set_mode, mode, mode, DisplacementMapFilterMode],
|
||||
[set_scale_x, scale_x, scale_x, f64],
|
||||
[set_scale_y, scale_y, scale_y, f64],
|
||||
);
|
||||
|
||||
pub fn empty_object(gc_context: MutationContext<'gc, '_>, proto: Object<'gc>) -> Self {
|
||||
DisplacementMapFilterObject(GcCell::allocate(
|
||||
gc_context,
|
||||
DisplacementMapFilterData {
|
||||
base: ScriptObject::new(gc_context, Some(proto)),
|
||||
alpha: 0.0,
|
||||
color: 0,
|
||||
component_x: 0,
|
||||
component_y: 0,
|
||||
map_bitmap: None,
|
||||
map_point: (0, 0),
|
||||
mode: DisplacementMapFilterMode::Wrap,
|
||||
scale_x: 0.0,
|
||||
scale_y: 0.0,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> TObject<'gc> for DisplacementMapFilterObject<'gc> {
|
||||
impl_custom_object!(base {
|
||||
bare_object(as_displacement_map_filter_object -> DisplacementMapFilterObject::empty_object);
|
||||
});
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
use crate::avm2::{Object as Avm2Object, Value as Avm2Value};
|
||||
use crate::display_object::{DisplayObject, TDisplayObject};
|
||||
use bitflags::bitflags;
|
||||
use core::fmt;
|
||||
use gc_arena::Collect;
|
||||
use ruffle_render::backend::RenderBackend;
|
||||
use ruffle_render::bitmap::{Bitmap, BitmapFormat, BitmapHandle, PixelRegion, SyncHandle};
|
||||
use ruffle_wstr::WStr;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Range;
|
||||
use swf::{Rectangle, Twips};
|
||||
use tracing::instrument;
|
||||
|
@ -253,8 +253,6 @@ mod wrapper {
|
|||
|
||||
use super::{copy_pixels_to_bitmapdata, BitmapData, DirtyState};
|
||||
|
||||
#[derive(Collect, Copy, Clone)]
|
||||
#[collect(no_drop)]
|
||||
/// A wrapper type that ensures that we always wait for a pending
|
||||
/// GPU -> CPU sync to complete (using `sync_handle`) before accessing
|
||||
/// the CPU-side pixels.
|
||||
|
@ -283,7 +281,8 @@ mod wrapper {
|
|||
///
|
||||
/// Note that we also perform CPU-GPU syncs from `BitmapData.update_dirty_texture` when `dirty` is set.
|
||||
/// `sync_handle` and `dirty` can never be set at the same time - we can only have one of them set, or none of them set.
|
||||
///
|
||||
#[derive(Copy, Clone, Collect, Debug)]
|
||||
#[collect(no_drop)]
|
||||
pub struct BitmapDataWrapper<'gc>(GcCell<'gc, BitmapData<'gc>>);
|
||||
|
||||
impl<'gc> BitmapDataWrapper<'gc> {
|
||||
|
@ -470,8 +469,8 @@ mod wrapper {
|
|||
|
||||
pub use wrapper::BitmapDataWrapper;
|
||||
|
||||
impl fmt::Debug for BitmapData<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
impl std::fmt::Debug for BitmapData<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("BitmapData")
|
||||
.field("dirty_state", &self.dirty_state)
|
||||
.field("width", &self.width)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/// An RGBA (red, green, blue, alpha) color.
|
||||
///
|
||||
/// All components are stored as [`u8`] and have a color range of 0-255.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Color {
|
||||
/// The red component value.
|
||||
pub r: u8,
|
||||
|
|
Loading…
Reference in New Issue