core: Implements `BitmapData.pixelDissolve()` for AVM2.
The changes have automated tests for them. The implementation has not been implemented or tested for AVM1 yet.
This commit is contained in:
parent
ad77cb0209
commit
1bd9f82496
|
@ -61,6 +61,10 @@ package flash.display {
|
||||||
sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, operation:String, threshold:uint, color:uint = 0, mask:uint = 0xFFFFFFFF, copySource:Boolean = false
|
sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, operation:String, threshold:uint, color:uint = 0, mask:uint = 0xFFFFFFFF, copySource:Boolean = false
|
||||||
):uint;
|
):uint;
|
||||||
public native function compare(otherBitmapData:BitmapData):Object;
|
public native function compare(otherBitmapData:BitmapData):Object;
|
||||||
|
public native function pixelDissolve(
|
||||||
|
sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, randomSeed:int = 0, numPixels:int = 0,
|
||||||
|
fillColor:uint = 0
|
||||||
|
):int;
|
||||||
|
|
||||||
public function generateFilterRect(sourceRect:Rectangle, filter:BitmapFilter):Rectangle {
|
public function generateFilterRect(sourceRect:Rectangle, filter:BitmapFilter):Rectangle {
|
||||||
stub_method("flash.display.BitmapData", "generateFilterRect");
|
stub_method("flash.display.BitmapData", "generateFilterRect");
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! `flash.display.BitmapData` builtin/prototype
|
//! `flash.display.BitmapData` builtin/prototype
|
||||||
|
|
||||||
use crate::avm2::activation::Activation;
|
use crate::avm2::activation::Activation;
|
||||||
use crate::avm2::error::{argument_error, make_error_2008};
|
use crate::avm2::error::{argument_error, make_error_2008, range_error};
|
||||||
use crate::avm2::filters::FilterAvm2Ext;
|
use crate::avm2::filters::FilterAvm2Ext;
|
||||||
use crate::avm2::object::{BitmapDataObject, ByteArrayObject, Object, TObject, VectorObject};
|
use crate::avm2::object::{BitmapDataObject, ByteArrayObject, Object, TObject, VectorObject};
|
||||||
use crate::avm2::value::Value;
|
use crate::avm2::value::Value;
|
||||||
|
@ -1050,7 +1050,7 @@ pub fn apply_filter<'gc>(
|
||||||
source_rect.width().to_pixels().ceil() as u32,
|
source_rect.width().to_pixels().ceil() as u32,
|
||||||
source_rect.height().to_pixels().ceil() as u32,
|
source_rect.height().to_pixels().ceil() as u32,
|
||||||
);
|
);
|
||||||
let dest_point = args.get_object(activation, 2, "dstPoint")?;
|
let dest_point = args.get_object(activation, 2, "destPoint")?;
|
||||||
let dest_point = (
|
let dest_point = (
|
||||||
dest_point
|
dest_point
|
||||||
.get_public_property("x", activation)?
|
.get_public_property("x", activation)?
|
||||||
|
@ -1123,7 +1123,7 @@ pub fn palette_map<'gc>(
|
||||||
source_rect.width().to_pixels().ceil() as i32,
|
source_rect.width().to_pixels().ceil() as i32,
|
||||||
source_rect.height().to_pixels().ceil() as i32,
|
source_rect.height().to_pixels().ceil() as i32,
|
||||||
);
|
);
|
||||||
let dest_point = args.get_object(activation, 2, "dstPoint")?;
|
let dest_point = args.get_object(activation, 2, "destPoint")?;
|
||||||
let dest_point = (
|
let dest_point = (
|
||||||
dest_point
|
dest_point
|
||||||
.get_public_property("x", activation)?
|
.get_public_property("x", activation)?
|
||||||
|
@ -1239,7 +1239,7 @@ pub fn threshold<'gc>(
|
||||||
if !bitmap_data.disposed() {
|
if !bitmap_data.disposed() {
|
||||||
let src_bitmap = args.get_object(activation, 0, "sourceBitmapData")?;
|
let src_bitmap = args.get_object(activation, 0, "sourceBitmapData")?;
|
||||||
let source_rect = args.get_object(activation, 1, "sourceRect")?;
|
let source_rect = args.get_object(activation, 1, "sourceRect")?;
|
||||||
let dest_point = args.get_object(activation, 2, "dstPoint")?;
|
let dest_point = args.get_object(activation, 2, "destPoint")?;
|
||||||
let dest_point = (
|
let dest_point = (
|
||||||
dest_point
|
dest_point
|
||||||
.get_public_property("x", activation)?
|
.get_public_property("x", activation)?
|
||||||
|
@ -1336,11 +1336,15 @@ pub fn compare<'gc>(
|
||||||
{
|
{
|
||||||
other_bitmap_data
|
other_bitmap_data
|
||||||
} else {
|
} else {
|
||||||
// The documentation says that -1 should be returned here, but -2 is actually returned.
|
// The documentation for AVM1 says that -1 should be returned here,
|
||||||
|
// but -2 is actually returned.
|
||||||
|
// TODO: Has this been tested for AVM2?
|
||||||
return Ok(BITMAP_DISPOSED.into());
|
return Ok(BITMAP_DISPOSED.into());
|
||||||
};
|
};
|
||||||
other_bitmap_data.check_valid(activation)?;
|
other_bitmap_data.check_valid(activation)?;
|
||||||
|
|
||||||
|
// TODO: Given the above check with `other_bitmap_data.check_valid`, this branch will
|
||||||
|
// presumably never get executed.
|
||||||
if other_bitmap_data.disposed() {
|
if other_bitmap_data.disposed() {
|
||||||
return Ok(BITMAP_DISPOSED.into());
|
return Ok(BITMAP_DISPOSED.into());
|
||||||
}
|
}
|
||||||
|
@ -1369,3 +1373,78 @@ pub fn compare<'gc>(
|
||||||
None => Ok(EQUIVALENT.into()),
|
None => Ok(EQUIVALENT.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements `BitmapData.pixelDissolve`.
|
||||||
|
pub fn pixel_dissolve<'gc>(
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
this: Option<Object<'gc>>,
|
||||||
|
args: &[Value<'gc>],
|
||||||
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
if let Some(bitmap_data) = this.and_then(|t| t.as_bitmap_data()) {
|
||||||
|
bitmap_data.check_valid(activation)?;
|
||||||
|
|
||||||
|
let src_bitmap_data = args.get_object(activation, 0, "sourceBitmapData")?;
|
||||||
|
|
||||||
|
let source_rect = args.get_object(activation, 1, "sourceRect")?;
|
||||||
|
|
||||||
|
// TODO: `BitmapData.pixelDissolve() might be called frequently. Is this performant,
|
||||||
|
// and if not, is there a faster alternative?
|
||||||
|
let src_min_x = source_rect
|
||||||
|
.get_public_property("x", activation)?
|
||||||
|
.coerce_to_i32(activation)?;
|
||||||
|
let src_min_y = source_rect
|
||||||
|
.get_public_property("y", activation)?
|
||||||
|
.coerce_to_i32(activation)?;
|
||||||
|
let src_width = source_rect
|
||||||
|
.get_public_property("width", activation)?
|
||||||
|
.coerce_to_i32(activation)?;
|
||||||
|
let src_height = source_rect
|
||||||
|
.get_public_property("height", activation)?
|
||||||
|
.coerce_to_i32(activation)?;
|
||||||
|
|
||||||
|
let dest_point = args.get_object(activation, 2, "destPoint")?;
|
||||||
|
let dest_point = (
|
||||||
|
dest_point
|
||||||
|
.get_public_property("x", activation)?
|
||||||
|
.coerce_to_i32(activation)?,
|
||||||
|
dest_point
|
||||||
|
.get_public_property("y", activation)?
|
||||||
|
.coerce_to_i32(activation)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
let random_seed = args.get_i32(activation, 3)?;
|
||||||
|
|
||||||
|
let num_pixels = args.get_i32(activation, 4)?;
|
||||||
|
if num_pixels < 0 {
|
||||||
|
return Err(Error::AvmError(range_error(
|
||||||
|
activation,
|
||||||
|
&format!("Parameter numPixels must be a non-negative number; got {num_pixels}."),
|
||||||
|
2027,
|
||||||
|
)?));
|
||||||
|
}
|
||||||
|
|
||||||
|
let fill_color = args.get_i32(activation, 5)?;
|
||||||
|
|
||||||
|
// Apparently, if this check fails, a type error for `null` is given.
|
||||||
|
if let Some(src_bitmap_data) = src_bitmap_data.as_bitmap_data() {
|
||||||
|
src_bitmap_data.check_valid(activation)?;
|
||||||
|
|
||||||
|
return Ok(operations::pixel_dissolve(
|
||||||
|
activation.context.gc_context,
|
||||||
|
bitmap_data,
|
||||||
|
src_bitmap_data,
|
||||||
|
(src_min_x, src_min_y, src_width, src_height),
|
||||||
|
dest_point,
|
||||||
|
random_seed,
|
||||||
|
num_pixels,
|
||||||
|
fill_color,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Should it always return `Ok(Value::Undefined)` in this case?
|
||||||
|
// Can this even be tested for?
|
||||||
|
|
||||||
|
Ok(Value::Undefined)
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ use ruffle_render::filters::Filter;
|
||||||
use ruffle_render::matrix::Matrix;
|
use ruffle_render::matrix::Matrix;
|
||||||
use ruffle_render::quality::StageQuality;
|
use ruffle_render::quality::StageQuality;
|
||||||
use ruffle_render::transform::Transform;
|
use ruffle_render::transform::Transform;
|
||||||
|
use std::cell::{Ref, RefMut};
|
||||||
use swf::{BlendMode, ColorTransform, Fixed8, Rectangle, Twips};
|
use swf::{BlendMode, ColorTransform, Fixed8, Rectangle, Twips};
|
||||||
|
|
||||||
/// AVM1 and AVM2 have a shared set of operations they can perform on BitmapDatas.
|
/// AVM1 and AVM2 have a shared set of operations they can perform on BitmapDatas.
|
||||||
|
@ -1616,3 +1617,244 @@ pub fn set_pixels_from_byte_array<'gc>(
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns at least 2. Always returns an even number.
|
||||||
|
fn get_feistel_block_size(sequence_length: u32) -> u32 {
|
||||||
|
let sequence_length = sequence_length.max(2);
|
||||||
|
|
||||||
|
// For the given sequence length, figure out the number of bits required to
|
||||||
|
// represent all indices for the sequence. After that, round up to
|
||||||
|
// the nearest even number of bits.
|
||||||
|
// For instance, for a sequence of length 9, 4 bits are required,
|
||||||
|
// and 4 is even, so the result is 4.
|
||||||
|
// For a sequence of length 8, 3 bits are required, but 3 is not
|
||||||
|
// even, so round up to 4.
|
||||||
|
|
||||||
|
let mut bit_number: u32 = 0;
|
||||||
|
let mut num = sequence_length - 1;
|
||||||
|
|
||||||
|
while num > 0 {
|
||||||
|
num /= 2;
|
||||||
|
bit_number += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bit_number + (bit_number % 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Meant to be a bijective function that takes a raw index and gives the corresponding
|
||||||
|
/// Feistel index.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `raw_permutation_index` - Must obey '0 <= permutation_raw_index < permutation_length`.
|
||||||
|
/// * `feistel_block_size` - See `get_feistel_block_size()`.
|
||||||
|
fn pixel_dissolve_raw_to_feistel_index(raw_permutation_index: u32, feistel_block_size: u32) -> u32 {
|
||||||
|
// Discussion on Feistel networks:
|
||||||
|
// https://github.com/ruffle-rs/ruffle/issues/10962
|
||||||
|
|
||||||
|
// For the simple balanced variant of a Feistel network, an even number of
|
||||||
|
// bits for the block size is required (unbalanced Feistel networks
|
||||||
|
// also exists, but are presumably more complex).
|
||||||
|
|
||||||
|
// Applying a single round of Feistel.
|
||||||
|
|
||||||
|
let feistel_halfpiece_size = feistel_block_size / 2;
|
||||||
|
|
||||||
|
let halfpiece1 = raw_permutation_index >> feistel_halfpiece_size;
|
||||||
|
let halfpiece2 = raw_permutation_index & ((1 << feistel_halfpiece_size) - 1);
|
||||||
|
|
||||||
|
// Apply some function to make the output appear more random.
|
||||||
|
// TODO: Apply some decent but fast PRNG? Or at least some better and less non-random-looking
|
||||||
|
// function? Can also take `feistel_block_size` as an argument.
|
||||||
|
// This specific function was gotten by trial-and-error on what might make the output look
|
||||||
|
// random.
|
||||||
|
fn f(num: u32) -> u32 {
|
||||||
|
num * num + 1
|
||||||
|
}
|
||||||
|
let result_before_xor = (f(halfpiece2)) % (1 << feistel_halfpiece_size);
|
||||||
|
|
||||||
|
let new_halfpiece1 = halfpiece2;
|
||||||
|
let new_halfpiece2 = halfpiece1 ^ result_before_xor;
|
||||||
|
|
||||||
|
(new_halfpiece2 << feistel_halfpiece_size) | new_halfpiece1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn pixel_dissolve<'gc>(
|
||||||
|
mc: MutationContext<'gc, '_>,
|
||||||
|
target: BitmapDataWrapper<'gc>,
|
||||||
|
source_bitmap: BitmapDataWrapper<'gc>,
|
||||||
|
src_rect: (i32, i32, i32, i32),
|
||||||
|
dest_point: (i32, i32),
|
||||||
|
random_seed: i32,
|
||||||
|
num_pixels: i32,
|
||||||
|
fill_color: i32,
|
||||||
|
) -> i32 {
|
||||||
|
// TODO: What should happen in AVM1 when `num_pixels` is negative?
|
||||||
|
|
||||||
|
// Apparently,
|
||||||
|
// "numPixels:int (default = 0) — The default is 1/30 of the source area (width x height). "
|
||||||
|
// is wrong.
|
||||||
|
|
||||||
|
// TODO: Ensure that the AVM1 version returns -1 (probably in the AVM1 interface layer)
|
||||||
|
// when:
|
||||||
|
// "Calls to any method or property of a BitmapData object fail if the BitmapData object is
|
||||||
|
// invalid (for example, if it has height == 0 and width == 0),
|
||||||
|
// and upon failing those properties and methods that return Number values return -1. ".
|
||||||
|
|
||||||
|
// Extract points and areas.
|
||||||
|
|
||||||
|
let (src_min_x, src_min_y, src_width, src_height) = src_rect;
|
||||||
|
|
||||||
|
let src_width = src_width.max(0);
|
||||||
|
let src_height = src_height.max(0);
|
||||||
|
|
||||||
|
if src_width == 0 || src_height == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (dest_min_x, dest_min_y) = dest_point;
|
||||||
|
|
||||||
|
let transparency = target.transparency();
|
||||||
|
|
||||||
|
let mut source_region =
|
||||||
|
PixelRegion::for_whole_size(source_bitmap.width(), source_bitmap.height());
|
||||||
|
let mut dest_region = PixelRegion::for_whole_size(target.width(), target.height());
|
||||||
|
dest_region.clamp_with_intersection(
|
||||||
|
(dest_min_x, dest_min_y),
|
||||||
|
(src_min_x, src_min_y),
|
||||||
|
(src_width, src_height),
|
||||||
|
&mut source_region,
|
||||||
|
);
|
||||||
|
|
||||||
|
if dest_region.width() == 0 || dest_region.height() == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let write_offset = (dest_region.x_min, dest_region.y_min);
|
||||||
|
let read_offset = (source_region.x_min, source_region.y_min);
|
||||||
|
|
||||||
|
fn write_pixel(
|
||||||
|
write: &mut RefMut<BitmapData>,
|
||||||
|
different_source_than_target: &Option<Ref<BitmapData>>,
|
||||||
|
fill_color: i32,
|
||||||
|
transparency: bool,
|
||||||
|
base_point: (u32, u32),
|
||||||
|
read_offset: (u32, u32),
|
||||||
|
write_offset: (u32, u32),
|
||||||
|
) {
|
||||||
|
let read_point = (read_offset.0 + base_point.0, read_offset.1 + base_point.1);
|
||||||
|
let write_point = (write_offset.0 + base_point.0, write_offset.1 + base_point.1);
|
||||||
|
|
||||||
|
match different_source_than_target {
|
||||||
|
None => {
|
||||||
|
write.set_pixel32_raw(
|
||||||
|
write_point.0,
|
||||||
|
write_point.1,
|
||||||
|
Color::from(fill_color).to_premultiplied_alpha(transparency),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(different_source) => {
|
||||||
|
write.set_pixel32_raw(
|
||||||
|
write_point.0,
|
||||||
|
write_point.1,
|
||||||
|
different_source.get_pixel32_raw(read_point.0, read_point.1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let different_source_than_target = if source_bitmap.ptr_eq(target) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(source_bitmap.read_area(source_region))
|
||||||
|
};
|
||||||
|
|
||||||
|
let final_pixel_sequence_length = dest_region.width() * dest_region.height();
|
||||||
|
|
||||||
|
let num_pixels = num_pixels.min(final_pixel_sequence_length as i32);
|
||||||
|
|
||||||
|
// TODO: Should this be `target.sync()`? Or something else?
|
||||||
|
let target = target.sync();
|
||||||
|
|
||||||
|
let mut write = target.write(mc);
|
||||||
|
|
||||||
|
// For compliance with the official Flash Player, we always write the pixel at (0, 0).
|
||||||
|
write_pixel(
|
||||||
|
&mut write,
|
||||||
|
&different_source_than_target,
|
||||||
|
fill_color,
|
||||||
|
transparency,
|
||||||
|
(0, 0),
|
||||||
|
read_offset,
|
||||||
|
write_offset,
|
||||||
|
);
|
||||||
|
|
||||||
|
let feistel_block_size: u32 = get_feistel_block_size(final_pixel_sequence_length);
|
||||||
|
let permutation_length = 1 << feistel_block_size;
|
||||||
|
// Raw permutation index.
|
||||||
|
let mut raw_perm_index = (random_seed % (permutation_length as i32)) as u32;
|
||||||
|
|
||||||
|
for _ in 0..num_pixels {
|
||||||
|
// Feistel permutation index.
|
||||||
|
let mut feistel_perm_index = 0;
|
||||||
|
|
||||||
|
// Safety mechanism, in case there is a bug in the implementation.
|
||||||
|
let mut loop_counter = 0;
|
||||||
|
|
||||||
|
// Find a valid index to write to.
|
||||||
|
// Since the pixel at (0, 0) is always written, we always skip `feistel_perm_index == 0`.
|
||||||
|
while (feistel_perm_index == 0 || feistel_perm_index >= final_pixel_sequence_length)
|
||||||
|
&& final_pixel_sequence_length != 1
|
||||||
|
{
|
||||||
|
raw_perm_index = (raw_perm_index + 1) % permutation_length;
|
||||||
|
|
||||||
|
feistel_perm_index =
|
||||||
|
pixel_dissolve_raw_to_feistel_index(raw_perm_index, feistel_block_size);
|
||||||
|
|
||||||
|
loop_counter += 1;
|
||||||
|
|
||||||
|
if loop_counter > permutation_length + 2 {
|
||||||
|
// TODO: Is this the correct way to panic or error?
|
||||||
|
panic!(
|
||||||
|
"operations::pixel_dissolve() failed:\n\
|
||||||
|
Using Feistel network permutations:\n\
|
||||||
|
`raw_perm_index`: {raw_perm_index}\n\
|
||||||
|
`feistel_perm_index`: {feistel_perm_index}\n\
|
||||||
|
`permutation_length`: {permutation_length}\n\
|
||||||
|
`final_pixel_sequence_length`: {final_pixel_sequence_length}\n\
|
||||||
|
`src_rect`: {}, {}, {}, {}\n\
|
||||||
|
`dest_region`: {}, {}, {}, {}\n\
|
||||||
|
`dest_point`: {}, {}.",
|
||||||
|
src_min_x,
|
||||||
|
src_min_y,
|
||||||
|
src_width,
|
||||||
|
src_height,
|
||||||
|
dest_region.x_min,
|
||||||
|
dest_region.y_min,
|
||||||
|
dest_region.x_max,
|
||||||
|
dest_region.y_max,
|
||||||
|
dest_point.0,
|
||||||
|
dest_point.1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let base_point = (
|
||||||
|
feistel_perm_index % dest_region.width(),
|
||||||
|
feistel_perm_index / dest_region.width(),
|
||||||
|
);
|
||||||
|
|
||||||
|
write_pixel(
|
||||||
|
&mut write,
|
||||||
|
&different_source_than_target,
|
||||||
|
fill_color,
|
||||||
|
transparency,
|
||||||
|
base_point,
|
||||||
|
read_offset,
|
||||||
|
write_offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_perm_index as i32
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,255 @@
|
||||||
|
|
||||||
|
package
|
||||||
|
{
|
||||||
|
import flash.display.Sprite;
|
||||||
|
import flash.display.BitmapData;
|
||||||
|
import flash.display.Bitmap;
|
||||||
|
import flash.geom.Point;
|
||||||
|
import flash.geom.Rectangle;
|
||||||
|
import flash.utils.Timer;
|
||||||
|
import flash.events.TimerEvent;
|
||||||
|
|
||||||
|
public class test extends Sprite {
|
||||||
|
|
||||||
|
private function countTraceBitmap(bitmap: BitmapData, round: uint, fillColor: uint = 0xFFFF0000): void {
|
||||||
|
|
||||||
|
var count: int = 0;
|
||||||
|
|
||||||
|
var x: int = 0;
|
||||||
|
while (x < bitmap.width)
|
||||||
|
{
|
||||||
|
var y: int = 0;
|
||||||
|
while (y < bitmap.height)
|
||||||
|
{
|
||||||
|
if (bitmap.getPixel32(x, y) == fillColor) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
y += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
x += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace("(" + round + ") Overwritten pixel count: " + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dissolveOnce(bitmap: BitmapData, rectangle: Rectangle, destPoint: Point, rand: Number, numPixels: Number): Number {
|
||||||
|
var red:uint = 0xFFFF0000;
|
||||||
|
var newRand: Number = bitmap.pixelDissolve(bitmap, rectangle, destPoint, rand, numPixels, red);
|
||||||
|
return newRand;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dissolveRounds(
|
||||||
|
rounds: uint,
|
||||||
|
width: uint, height: uint,
|
||||||
|
useBitmapDataOwnRectangle: Boolean,
|
||||||
|
sourceRect: Rectangle,
|
||||||
|
destPoint: Point,
|
||||||
|
numPixels: int
|
||||||
|
): void {
|
||||||
|
|
||||||
|
rounds = Math.max(1, rounds);
|
||||||
|
|
||||||
|
var sourceRectString: String = useBitmapDataOwnRectangle ? "sourceRect: [own]" : "sourceRect: " + sourceRect;
|
||||||
|
|
||||||
|
trace("Dissolving for " + rounds + " rounds, with data: width: " + width + ", height: " + height + ", " + sourceRectString + ", destPoint: " + destPoint + ", numPixels: " + numPixels);
|
||||||
|
|
||||||
|
var bmd2:BitmapData = new BitmapData(width, height, false, 0xFFCCCCCC);
|
||||||
|
|
||||||
|
var round:int;
|
||||||
|
var randNum: Number = 0;
|
||||||
|
for (round = 1; round <= rounds; round++)
|
||||||
|
{
|
||||||
|
randNum = dissolveOnce(bmd2, useBitmapDataOwnRectangle ? bmd2.rect : sourceRect, destPoint, randNum, numPixels);
|
||||||
|
countTraceBitmap(bmd2, round);
|
||||||
|
}
|
||||||
|
|
||||||
|
trace("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dissolveRounds_1(
|
||||||
|
rounds: uint,
|
||||||
|
width: uint, height: uint,
|
||||||
|
numPixels: int
|
||||||
|
): void {
|
||||||
|
dissolveRounds(rounds, width, height, true, null, new Point(0, 0), numPixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dissolveRounds_2(
|
||||||
|
rounds: uint,
|
||||||
|
width: uint, height: uint,
|
||||||
|
sourceRect: Rectangle,
|
||||||
|
numPixels: int
|
||||||
|
): void {
|
||||||
|
dissolveRounds(rounds, width, height, false, sourceRect, new Point(0, 0), numPixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dissolveRounds_3(
|
||||||
|
rounds: uint,
|
||||||
|
width: uint, height: uint,
|
||||||
|
sourceRect: Rectangle,
|
||||||
|
destPoint: Point,
|
||||||
|
numPixels: int
|
||||||
|
): void {
|
||||||
|
dissolveRounds(rounds, width, height, false, sourceRect, destPoint, numPixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test() {
|
||||||
|
|
||||||
|
trace("------------------------------------------");
|
||||||
|
trace("Basic test.");
|
||||||
|
trace("");
|
||||||
|
dissolveRounds_1(100, 10, 10, 1);
|
||||||
|
|
||||||
|
trace("");
|
||||||
|
trace("");
|
||||||
|
trace("------------------------------------------");
|
||||||
|
trace("`numPixels`.");
|
||||||
|
trace("");
|
||||||
|
dissolveRounds_1(4, 10, 10, 0);
|
||||||
|
try {
|
||||||
|
dissolveRounds_1(4, 10, 10, -1);
|
||||||
|
}
|
||||||
|
catch (e:Error) {
|
||||||
|
trace("Negative `numPixels` should error.");
|
||||||
|
}
|
||||||
|
dissolveRounds_1(35, 10, 10, 3);
|
||||||
|
|
||||||
|
trace("");
|
||||||
|
trace("");
|
||||||
|
trace("------------------------------------------");
|
||||||
|
trace("Dimensions.");
|
||||||
|
trace("");
|
||||||
|
// WARNING: Apparently, `width` or `height` being 1 means that nothing is written,
|
||||||
|
// not even the pixel at (0, 0). This seems like a bug in Flash Player and will
|
||||||
|
// not necessarily be emulated. So, do not test for it here.
|
||||||
|
dissolveRounds_1(61, 150, 2, 5);
|
||||||
|
dissolveRounds_1(61, 2, 150, 5);
|
||||||
|
dissolveRounds_1(66, 8, 8, 1);
|
||||||
|
dissolveRounds_1(66, 7, 9, 1); // 7*9 = 8*8 - 1.
|
||||||
|
dissolveRounds_1(66, 5, 13, 1); // 5*13 = 8*8 + 1.
|
||||||
|
|
||||||
|
trace("");
|
||||||
|
trace("");
|
||||||
|
trace("------------------------------------------");
|
||||||
|
trace("`sourceRect`.");
|
||||||
|
trace("");
|
||||||
|
dissolveRounds_2(101, 10, 10, new Rectangle(0, 0, 10, 10), 1);
|
||||||
|
dissolveRounds_2(20, 10, 10, new Rectangle(0, 0, 4, 4), 1);
|
||||||
|
dissolveRounds_2(35, 10, 10, new Rectangle(0, 0, 8, 4), 1);
|
||||||
|
dissolveRounds_2(35, 10, 10, new Rectangle(0, 0, 4, 8), 1);
|
||||||
|
dissolveRounds_2(27, 10, 10, new Rectangle(0, 0, 3, 8), 1);
|
||||||
|
dissolveRounds_2(31, 10, 10, new Rectangle(0, 0, 4, 7), 1);
|
||||||
|
dissolveRounds_2(24, 10, 10, new Rectangle(0, 0, 3, 7), 1);
|
||||||
|
dissolveRounds_2(30, 10, 10, new Rectangle(1, 3, 4, 8), 1);
|
||||||
|
dissolveRounds_2(4, 10, 10, new Rectangle(14, 3, 4, 8), 1);
|
||||||
|
dissolveRounds_2(4, 10, 10, new Rectangle(3, 14, 4, 8), 1);
|
||||||
|
dissolveRounds_2(25, 10, 10, new Rectangle(-1, 3, 4, 8), 1);
|
||||||
|
dissolveRounds_2(25, 10, 10, new Rectangle(-1, -1, 4, 8), 1);
|
||||||
|
dissolveRounds_2(30, 10, 10, new Rectangle(2, -1, 4, 8), 1);
|
||||||
|
dissolveRounds_2(2, 10, 10, new Rectangle(1, 2, -1, -3), 1);
|
||||||
|
dissolveRounds_2(2, 10, 10, new Rectangle(1, 2, -1, 3), 1);
|
||||||
|
dissolveRounds_2(2, 10, 10, new Rectangle(1, 2, 1, -3), 1);
|
||||||
|
dissolveRounds_2(2, 10, 10, new Rectangle(1, 2, 0, 1), 1);
|
||||||
|
dissolveRounds_2(2, 10, 10, new Rectangle(1, 2, 1, 0), 1);
|
||||||
|
dissolveRounds_2(2, 10, 10, new Rectangle(1, 2, 0, 0), 1);
|
||||||
|
// Note: Apparently, Flash Player seems to round width and height
|
||||||
|
// in really peculiar ways when the x- and y-coordinates have
|
||||||
|
// fractional components. Therefore, the tests involving
|
||||||
|
// fractional parts are commented out.
|
||||||
|
//dissolveRounds_2(18, 10, 10, new Rectangle(0.5, 0.5, 3, 3), 1);
|
||||||
|
//dissolveRounds_2(18, 10, 10, new Rectangle(0.5, 0.5, 3, 4), 1);
|
||||||
|
//dissolveRounds_2(27, 10, 10, new Rectangle(0.5, 0.5, 3, 5), 1);
|
||||||
|
//dissolveRounds_2(18, 10, 10, new Rectangle(0.5, 0.5, 4, 3), 1);
|
||||||
|
//dissolveRounds_2(18, 10, 10, new Rectangle(0.5, 0.5, 4, 4), 1);
|
||||||
|
//dissolveRounds_2(14, 10, 10, new Rectangle(0.4, 1, 3, 4), 1);
|
||||||
|
//dissolveRounds_2(14, 10, 10, new Rectangle(0.6, 1, 3, 4), 1);
|
||||||
|
//dissolveRounds_2(14, 10, 10, new Rectangle(1, 1, 3.4, 4), 1);
|
||||||
|
//dissolveRounds_2(14, 10, 10, new Rectangle(1, 1, 3.5, 4), 1);
|
||||||
|
//dissolveRounds_2(18, 10, 10, new Rectangle(1, 1, 3.6, 4), 1);
|
||||||
|
//dissolveRounds_2(18, 10, 10, new Rectangle(0.4, 1, 3.4, 4), 1);
|
||||||
|
//dissolveRounds_2(18, 10, 10, new Rectangle(0.5, 1, 3.4, 4), 1);
|
||||||
|
//dissolveRounds_2(14, 10, 10, new Rectangle(0.6, 1, 3.4, 4), 1);
|
||||||
|
//dissolveRounds_2(18, 10, 10, new Rectangle(0.4, 1, 3.5, 4), 1);
|
||||||
|
//dissolveRounds_2(18, 10, 10, new Rectangle(0.5, 1, 3.5, 4), 1);
|
||||||
|
//dissolveRounds_2(14, 10, 10, new Rectangle(0.6, 1, 3.5, 4), 1);
|
||||||
|
//dissolveRounds_2(18, 10, 10, new Rectangle(0.4, 1, 3.6, 4), 1);
|
||||||
|
//dissolveRounds_2(18, 10, 10, new Rectangle(0.5, 1, 3.6, 4), 1);
|
||||||
|
//dissolveRounds_2(14, 10, 10, new Rectangle(0.6, 1, 3.6, 4), 1);
|
||||||
|
try {
|
||||||
|
dissolveRounds_2(20, 10, 10, null, 1);
|
||||||
|
}
|
||||||
|
catch (e: Error) {
|
||||||
|
trace("`null` `sourceRect` should error: " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
trace("");
|
||||||
|
trace("");
|
||||||
|
trace("------------------------------------------");
|
||||||
|
trace("`destPoint`.");
|
||||||
|
trace("");
|
||||||
|
dissolveRounds_3(18, 10, 10, new Rectangle(0, 0, 3, 7), new Point(3, 5), 1);
|
||||||
|
// This would result in a 1x6 resulting area. And Flash Player's pixelDissolve()
|
||||||
|
// cannot handle a size where at least one of the dimensions is 1.
|
||||||
|
//dissolveRounds_3(8, 10, 10, new Rectangle(0, 0, 3, 7), new Point(-2, -1), 1);
|
||||||
|
dissolveRounds_3(15, 10, 10, new Rectangle(0, 0, 3, 7), new Point(-1, -1), 1);
|
||||||
|
dissolveRounds_3(3, 10, 10, new Rectangle(0, 0, 3, 7), new Point(-10, -10), 1);
|
||||||
|
dissolveRounds_3(3, 10, 10, new Rectangle(0, 0, 3, 7), new Point(-2, 10), 1);
|
||||||
|
dissolveRounds_3(21, 10, 10, new Rectangle(0, 0, 3, 7), new Point(5, 4),1);
|
||||||
|
|
||||||
|
trace("");
|
||||||
|
trace("");
|
||||||
|
trace("------------------------------------------");
|
||||||
|
trace("Default parameters.");
|
||||||
|
trace("");
|
||||||
|
var bmd:BitmapData = new BitmapData(10, 10, false, 0xFFCCCCCC);
|
||||||
|
// Omitting `numPixels` and `fillColor`.
|
||||||
|
// Apparently, "numPixels:int (default = 0) — The default is 1/30 of the source area (width x height). " is wrong.
|
||||||
|
bmd.pixelDissolve(bmd, bmd.rect, new Point(0, 0));
|
||||||
|
countTraceBitmap(bmd, 0, 0xFF000000);
|
||||||
|
|
||||||
|
trace("");
|
||||||
|
trace("");
|
||||||
|
trace("------------------------------------------");
|
||||||
|
trace("`null` `sourceBitmapData`.");
|
||||||
|
trace("");
|
||||||
|
var bmd2:BitmapData = new BitmapData(10, 10, false, 0xFFCCCCCC);
|
||||||
|
try {
|
||||||
|
bmd2.pixelDissolve(null, bmd2.rect, new Point(0, 0));
|
||||||
|
}
|
||||||
|
catch (e: Error) {
|
||||||
|
trace("`sourceBitmapData` being `null` should result in error: " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
trace("");
|
||||||
|
trace("");
|
||||||
|
trace("------------------------------------------");
|
||||||
|
trace("`null` `sourceBitmapData`.");
|
||||||
|
trace("");
|
||||||
|
var bmd3:BitmapData = new BitmapData(10, 10, false, 0xFFCCCCCC);
|
||||||
|
try {
|
||||||
|
bmd3.pixelDissolve(5 as BitmapData, bmd3.rect, new Point(0, 0));
|
||||||
|
}
|
||||||
|
catch (e: Error) {
|
||||||
|
trace("`sourceBitmapData` being the wrong type should result in error: " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
trace("");
|
||||||
|
trace("");
|
||||||
|
trace("------------------------------------------");
|
||||||
|
trace("Invalid other bitmap.");
|
||||||
|
trace("");
|
||||||
|
var bmd4:BitmapData = new BitmapData(10, 10, false, 0xFFCCCCCC);
|
||||||
|
var bmd5:BitmapData = new BitmapData(10, 10, false, 0xFFCFFCCC);
|
||||||
|
bmd5.dispose();
|
||||||
|
try {
|
||||||
|
bmd4.pixelDissolve(bmd5, bmd4.rect, new Point(0, 0));
|
||||||
|
}
|
||||||
|
catch (e: Error) {
|
||||||
|
trace("`sourceBitmapData` being disposed() should result in error: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
num_frames = 1
|
Binary file not shown.
After Width: | Height: | Size: 9.5 KiB |
|
@ -0,0 +1,121 @@
|
||||||
|
|
||||||
|
package
|
||||||
|
{
|
||||||
|
import flash.display.Sprite;
|
||||||
|
import flash.display.BitmapData;
|
||||||
|
import flash.display.Bitmap;
|
||||||
|
import flash.geom.Point;
|
||||||
|
import flash.geom.Rectangle;
|
||||||
|
import flash.utils.Timer;
|
||||||
|
import flash.events.TimerEvent;
|
||||||
|
|
||||||
|
public class test extends Sprite {
|
||||||
|
|
||||||
|
private function add(bitmapData: BitmapData, x: Number, y: Number): void {
|
||||||
|
|
||||||
|
var destBitmap:Bitmap = new Bitmap(bitmapData);
|
||||||
|
destBitmap.x = x;
|
||||||
|
destBitmap.y = y;
|
||||||
|
this.addChild(destBitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test() {
|
||||||
|
|
||||||
|
var x: uint;
|
||||||
|
var y: uint;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// Transparency.
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function sourceHandlingOpaque(dest: BitmapData, x: Number, y: Number): void {
|
||||||
|
dest.pixelDissolve(dest, dest.rect, new Point(0, 0), 0, 9999, 0xFF33FF99);
|
||||||
|
add(dest, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sourceHandlingSemiTransparent(dest: BitmapData, x: Number, y: Number): void {
|
||||||
|
dest.pixelDissolve(dest, dest.rect, new Point(0, 0), 0, 9999, 0xAA5511FF);
|
||||||
|
add(dest, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sourceHandlingCompletelyTransparent(dest: BitmapData, x: Number, y: Number): void {
|
||||||
|
dest.pixelDissolve(dest, dest.rect, new Point(0, 0), 0, 9999, 0x003377FF);
|
||||||
|
add(dest, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceBitmapData4:BitmapData = new BitmapData(100, 100, true, 0xFFFFAA33);
|
||||||
|
for (x = 0; x < 100; x += 1) {
|
||||||
|
for (y = 0; y < 20; y += 1) {
|
||||||
|
sourceBitmapData4.setPixel32(x, y, 0x00FFAA00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (x = 0; x < 100; x += 1) {
|
||||||
|
for (y = 20; y < 40; y += 1) {
|
||||||
|
sourceBitmapData4.setPixel32(x, y, 0x88337777);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function sourceHandlingMixedBitmapSource(dest: BitmapData, x: Number, y: Number): void {
|
||||||
|
dest.pixelDissolve(sourceBitmapData4, dest.rect, new Point(0, 0), 0, 9999);
|
||||||
|
add(dest, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function destOpaque(): BitmapData {
|
||||||
|
return new BitmapData(100, 100, true, 0xFF1199DD);
|
||||||
|
}
|
||||||
|
|
||||||
|
function destSemiTransparent(): BitmapData {
|
||||||
|
return new BitmapData(100, 100, true, 0xAA44BB11);
|
||||||
|
}
|
||||||
|
|
||||||
|
function destCompletelyTransparent(): BitmapData {
|
||||||
|
return new BitmapData(100, 100, true, 0x0000DD44);
|
||||||
|
}
|
||||||
|
|
||||||
|
var destFactories: Array = [
|
||||||
|
destOpaque,
|
||||||
|
destSemiTransparent,
|
||||||
|
destCompletelyTransparent
|
||||||
|
];
|
||||||
|
var sourceHandlings: Array = [
|
||||||
|
sourceHandlingOpaque,
|
||||||
|
sourceHandlingSemiTransparent,
|
||||||
|
sourceHandlingCompletelyTransparent,
|
||||||
|
sourceHandlingMixedBitmapSource
|
||||||
|
];
|
||||||
|
|
||||||
|
for (x = 0; x < sourceHandlings.length; x += 1) {
|
||||||
|
for (y = 0; y < destFactories.length; y += 1) {
|
||||||
|
// Create a fresh destination BitmapData, call `pixelDissolve` on it with the given source, and place it at (x*100, y*100).
|
||||||
|
sourceHandlings[x](destFactories[y](), x*100, y*100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// Source and destination offsets and areas.
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
var offsetBitmapData: BitmapData = new BitmapData(100, 100, false, 0xFF000000);
|
||||||
|
var offsetBitmapData2: BitmapData = new BitmapData(100, 100, false, 0xFF000000);
|
||||||
|
for (x = 0; x < 100; x += 1) {
|
||||||
|
for (y = 0; y < 100; y += 1) {
|
||||||
|
offsetBitmapData.setPixel(x, y, x + y + 0x990000);
|
||||||
|
offsetBitmapData2.setPixel(x, y, x + y + 0x009900);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offsetBitmapData2.pixelDissolve(offsetBitmapData, new Rectangle(30, 40, 35, 55), new Point(25, 10), 0, 9999);
|
||||||
|
add(offsetBitmapData2, 400, 0);
|
||||||
|
|
||||||
|
var offsetBitmapData3: BitmapData = new BitmapData(100, 100, false, 0xFF000000);
|
||||||
|
var offsetBitmapData4: BitmapData = new BitmapData(100, 100, false, 0xFF000000);
|
||||||
|
for (x = 0; x < 100; x += 1) {
|
||||||
|
for (y = 0; y < 100; y += 1) {
|
||||||
|
offsetBitmapData3.setPixel(x, y, x - y + 0x990000);
|
||||||
|
offsetBitmapData4.setPixel(x, y, x + y + 0x009900);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offsetBitmapData4.pixelDissolve(offsetBitmapData3, new Rectangle(30, 40, 35, 55), new Point(25, 10), 0, 9999);
|
||||||
|
add(offsetBitmapData4, 400, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
||||||
|
num_frames = 1
|
||||||
|
|
||||||
|
[image_comparison]
|
||||||
|
tolerance = 1
|
||||||
|
|
||||||
|
[player_options]
|
||||||
|
with_renderer = { optional = false, sample_count = 1 }
|
Loading…
Reference in New Issue