core: Re-arrange internal implementation of 'pixelDissolve()'.

This commit is contained in:
iwannabethedev 2023-05-20 20:17:04 +02:00 committed by Nathan Adams
parent deae231176
commit 0f30b378b0
1 changed files with 95 additions and 92 deletions

View File

@ -1618,68 +1618,6 @@ pub fn set_pixels_from_byte_array<'gc>(
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: It would be good to test and improve this heuristic function for creating
// random-looking output. One possibility is some decent but fast PRNG. The 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, '_>,
@ -1691,6 +1629,101 @@ pub fn pixel_dissolve<'gc>(
num_pixels: i32,
fill_color: i32,
) -> i32 {
/// 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: It would be good to test and improve this heuristic function for creating
// random-looking output. One possibility is some decent but fast PRNG. The 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
}
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),
);
}
}
}
// Apparently,
// "numPixels:int (default = 0) — The default is 1/30 of the source area (width x height). "
// is wrong.
@ -1727,36 +1760,6 @@ pub fn pixel_dissolve<'gc>(
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 {