core: Move draw from BitmapData to bitmap_data_operations
This commit is contained in:
parent
57648b6380
commit
e4caedcc12
|
@ -548,11 +548,10 @@ pub fn draw<'gc>(
|
|||
|
||||
// Do this last, so that we only call `overwrite_cpu_pixels_from_gpu`
|
||||
// if we're actually going to draw something.
|
||||
let (bmd, dirty_area) = bitmap_data
|
||||
.bitmap_data_wrapper()
|
||||
.overwrite_cpu_pixels_from_gpu(&mut activation.context);
|
||||
let mut write = bmd.write(activation.context.gc_context);
|
||||
match write.draw(
|
||||
let quality = activation.context.stage.quality();
|
||||
match bitmap_data_operations::draw(
|
||||
&mut activation.context,
|
||||
bitmap_data.bitmap_data_wrapper(),
|
||||
source,
|
||||
Transform {
|
||||
matrix,
|
||||
|
@ -561,9 +560,7 @@ pub fn draw<'gc>(
|
|||
smoothing,
|
||||
blend_mode,
|
||||
None,
|
||||
activation.context.stage.quality(),
|
||||
&mut activation.context,
|
||||
dirty_area,
|
||||
quality,
|
||||
) {
|
||||
Ok(()) => {}
|
||||
Err(BitmapDataDrawError::Unimplemented) => {
|
||||
|
|
|
@ -828,23 +828,22 @@ pub fn draw<'gc>(
|
|||
return Err(format!("BitmapData.draw: unexpected source {source:?}").into());
|
||||
};
|
||||
|
||||
// Drawing onto a BitmapData doesn't use any of the CPU-side pixels
|
||||
// Do this last, so that we only call `overwrite_cpu_pixels_from_gpu`
|
||||
// if we're actually going to draw something.
|
||||
let (bitmap_data, dirty_area) =
|
||||
bitmap_data.overwrite_cpu_pixels_from_gpu(&mut activation.context);
|
||||
// If the bitmapdata is invalid, it's fine to return early, since the pixels
|
||||
// are inaccessible
|
||||
bitmap_data.read().check_valid(activation)?;
|
||||
match bitmap_data.write(activation.context.gc_context).draw(
|
||||
bitmap_data.check_valid(activation)?;
|
||||
|
||||
// Do this last, so that we only call `overwrite_cpu_pixels_from_gpu`
|
||||
// if we're actually going to draw something.
|
||||
let quality = activation.context.stage.quality();
|
||||
match bitmap_data_operations::draw(
|
||||
&mut activation.context,
|
||||
bitmap_data,
|
||||
source,
|
||||
transform,
|
||||
smoothing,
|
||||
blend_mode,
|
||||
clip_rect,
|
||||
activation.context.stage.quality(),
|
||||
&mut activation.context,
|
||||
dirty_area,
|
||||
quality,
|
||||
) {
|
||||
Ok(()) => {}
|
||||
Err(BitmapDataDrawError::Unimplemented) => {
|
||||
|
@ -862,10 +861,6 @@ pub fn draw_with_quality<'gc>(
|
|||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(bitmap_data) = this.and_then(|this| this.as_bitmap_data_wrapper()) {
|
||||
// Drawing onto a BitmapData doesn't use any of the CPU-side pixels
|
||||
let (bitmap_data, dirty_area) =
|
||||
bitmap_data.overwrite_cpu_pixels_from_gpu(&mut activation.context);
|
||||
bitmap_data.read().check_valid(activation)?;
|
||||
let mut transform = Transform::default();
|
||||
let mut blend_mode = BlendMode::Normal;
|
||||
|
||||
|
@ -900,7 +895,6 @@ pub fn draw_with_quality<'gc>(
|
|||
)?);
|
||||
}
|
||||
|
||||
let mut bitmap_data = bitmap_data.write(activation.context.gc_context);
|
||||
let smoothing = args.get_bool(5);
|
||||
|
||||
let source = args.get_object(activation, 0, "source")?;
|
||||
|
@ -929,21 +923,21 @@ pub fn draw_with_quality<'gc>(
|
|||
activation.context.stage.quality()
|
||||
};
|
||||
|
||||
match bitmap_data.draw(
|
||||
match bitmap_data_operations::draw(
|
||||
&mut activation.context,
|
||||
bitmap_data,
|
||||
source,
|
||||
transform,
|
||||
smoothing,
|
||||
blend_mode,
|
||||
clip_rect,
|
||||
quality,
|
||||
&mut activation.context,
|
||||
dirty_area,
|
||||
) {
|
||||
Ok(()) => {}
|
||||
Err(BitmapDataDrawError::Unimplemented) => {
|
||||
return Err("Render backend does not support BitmapData.draw".into());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
|
|
@ -1,20 +1,13 @@
|
|||
use crate::avm2::{Error, Object as Avm2Object, Value as Avm2Value};
|
||||
use crate::context::RenderContext;
|
||||
use crate::context::UpdateContext;
|
||||
use crate::display_object::DisplayObject;
|
||||
use crate::display_object::TDisplayObject;
|
||||
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_render::commands::{CommandHandler, CommandList};
|
||||
use ruffle_render::matrix::Matrix;
|
||||
use ruffle_render::quality::StageQuality;
|
||||
use ruffle_render::transform::Transform;
|
||||
use ruffle_wstr::WStr;
|
||||
use std::ops::Range;
|
||||
use swf::{BlendMode, Rectangle, Twips};
|
||||
use swf::Twips;
|
||||
use tracing::instrument;
|
||||
|
||||
/// An implementation of the Lehmer/Park-Miller random number generator
|
||||
|
@ -658,125 +651,6 @@ impl<'gc> BitmapData<'gc> {
|
|||
pub fn init_object2(&mut self, object: Avm2Object<'gc>) {
|
||||
self.avm2_object = Some(object)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
mut source: IBitmapDrawable<'gc>,
|
||||
transform: Transform,
|
||||
smoothing: bool,
|
||||
blend_mode: BlendMode,
|
||||
clip_rect: Option<Rectangle<Twips>>,
|
||||
quality: StageQuality,
|
||||
context: &mut UpdateContext<'_, 'gc>,
|
||||
include_dirty_area: Option<PixelRegion>,
|
||||
) -> Result<(), BitmapDataDrawError> {
|
||||
let bitmapdata_width = self.width();
|
||||
let bitmapdata_height = self.height();
|
||||
|
||||
let mut transform_stack = ruffle_render::transform::TransformStack::new();
|
||||
transform_stack.push(&transform);
|
||||
let handle = self.bitmap_handle(context.renderer).unwrap();
|
||||
|
||||
// Calculate the maximum potential area that this draw call will affect
|
||||
let matrix = transform_stack.transform().matrix;
|
||||
let bounds = source.bounds();
|
||||
|
||||
let mut dirty_region =
|
||||
PixelRegion::encompassing_twips(matrix * bounds.0, matrix * bounds.1);
|
||||
dirty_region.clamp(bitmapdata_width, bitmapdata_height);
|
||||
|
||||
// If we have another dirty area to preserve, expand this to include it
|
||||
if let Some(old) = include_dirty_area {
|
||||
dirty_region.union(old);
|
||||
}
|
||||
|
||||
let mut render_context = RenderContext {
|
||||
renderer: context.renderer,
|
||||
commands: CommandList::new(),
|
||||
gc_context: context.gc_context,
|
||||
library: context.library,
|
||||
transform_stack: &mut transform_stack,
|
||||
is_offscreen: true,
|
||||
stage: context.stage,
|
||||
allow_mask: true,
|
||||
};
|
||||
|
||||
// Make the screen opacity match the opacity of this bitmap
|
||||
|
||||
let clip_mat = clip_rect.map(|clip_rect| {
|
||||
// Note - we do *not* apply the matrix to the clip rect,
|
||||
// to match Flash's behavior.
|
||||
let clip_mat = Matrix {
|
||||
a: (clip_rect.x_max - clip_rect.x_min).to_pixels() as f32,
|
||||
b: 0.0,
|
||||
c: 0.0,
|
||||
d: (clip_rect.y_max - clip_rect.y_min).to_pixels() as f32,
|
||||
tx: clip_rect.x_min,
|
||||
ty: clip_rect.y_min,
|
||||
};
|
||||
|
||||
render_context.commands.push_mask();
|
||||
// The color doesn't matter, as this is a mask.
|
||||
render_context
|
||||
.commands
|
||||
.draw_rect(swf::Color::BLACK, clip_mat);
|
||||
render_context.commands.activate_mask();
|
||||
|
||||
clip_mat
|
||||
});
|
||||
|
||||
match &mut source {
|
||||
IBitmapDrawable::BitmapData(data) => {
|
||||
data.render(smoothing, &mut render_context);
|
||||
}
|
||||
IBitmapDrawable::DisplayObject(object) => {
|
||||
// Note that we do *not* use `render_base`,
|
||||
// as we want to ignore the object's mask and normal transform
|
||||
object.render_self(&mut render_context);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(clip_mat) = clip_mat {
|
||||
// Draw the rectangle again after deactivating the mask,
|
||||
// to reset the stencil buffer.
|
||||
render_context.commands.deactivate_mask();
|
||||
render_context
|
||||
.commands
|
||||
.draw_rect(swf::Color::BLACK, clip_mat);
|
||||
render_context.commands.pop_mask();
|
||||
}
|
||||
|
||||
self.update_dirty_texture(render_context.renderer);
|
||||
|
||||
let commands = if blend_mode == BlendMode::Normal {
|
||||
render_context.commands
|
||||
} else {
|
||||
let mut commands = CommandList::new();
|
||||
commands.blend(render_context.commands, blend_mode);
|
||||
commands
|
||||
};
|
||||
|
||||
let image = context
|
||||
.renderer
|
||||
.render_offscreen(handle, commands, quality, dirty_region);
|
||||
|
||||
match image {
|
||||
Some(sync_handle) => {
|
||||
match self.dirty_state {
|
||||
DirtyState::Clean => {
|
||||
self.dirty_state = DirtyState::GpuModified(sync_handle, dirty_region)
|
||||
}
|
||||
DirtyState::CpuModified(_) | DirtyState::GpuModified(_, _) => panic!(
|
||||
"Called BitmapData.render while already dirty: {:?}",
|
||||
self.dirty_state
|
||||
),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
None => Err(BitmapDataDrawError::Unimplemented),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum IBitmapDrawable<'gc> {
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
use crate::bitmap::bitmap_data::{
|
||||
BitmapData, BitmapDataWrapper, ChannelOptions, Color, LehmerRng, ThresholdOperation,
|
||||
BitmapData, BitmapDataDrawError, BitmapDataWrapper, ChannelOptions, Color, IBitmapDrawable,
|
||||
LehmerRng, ThresholdOperation,
|
||||
};
|
||||
use crate::bitmap::turbulence::Turbulence;
|
||||
use crate::context::UpdateContext;
|
||||
use crate::context::{RenderContext, UpdateContext};
|
||||
use crate::display_object::TDisplayObject;
|
||||
use gc_arena::GcCell;
|
||||
use ruffle_render::bitmap::PixelRegion;
|
||||
use ruffle_render::commands::{CommandHandler, CommandList};
|
||||
use ruffle_render::filters::Filter;
|
||||
use swf::{ColorTransform, Fixed8};
|
||||
use ruffle_render::matrix::Matrix;
|
||||
use ruffle_render::quality::StageQuality;
|
||||
use ruffle_render::transform::Transform;
|
||||
use swf::{BlendMode, ColorTransform, Fixed8, Rectangle, Twips};
|
||||
|
||||
/// AVM1 and AVM2 have a shared set of operations they can perform on BitmapDatas.
|
||||
/// Instead of directly manipulating the BitmapData in each place, they should call
|
||||
|
@ -1186,3 +1192,115 @@ pub fn apply_filter<'gc>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn draw<'gc>(
|
||||
context: &mut UpdateContext<'_, 'gc>,
|
||||
target: BitmapDataWrapper<'gc>,
|
||||
mut source: IBitmapDrawable<'gc>,
|
||||
transform: Transform,
|
||||
smoothing: bool,
|
||||
blend_mode: BlendMode,
|
||||
clip_rect: Option<Rectangle<Twips>>,
|
||||
quality: StageQuality,
|
||||
) -> Result<(), BitmapDataDrawError> {
|
||||
let (target, include_dirty_area) = target.overwrite_cpu_pixels_from_gpu(context);
|
||||
let mut write = target.write(context.gc_context);
|
||||
|
||||
let bitmapdata_width = write.width();
|
||||
let bitmapdata_height = write.height();
|
||||
|
||||
let mut transform_stack = ruffle_render::transform::TransformStack::new();
|
||||
transform_stack.push(&transform);
|
||||
let handle = write.bitmap_handle(context.renderer).unwrap();
|
||||
|
||||
// Calculate the maximum potential area that this draw call will affect
|
||||
let matrix = transform_stack.transform().matrix;
|
||||
let bounds = source.bounds();
|
||||
|
||||
let mut dirty_region = PixelRegion::encompassing_twips(matrix * bounds.0, matrix * bounds.1);
|
||||
dirty_region.clamp(bitmapdata_width, bitmapdata_height);
|
||||
|
||||
// If we have another dirty area to preserve, expand this to include it
|
||||
if let Some(old) = include_dirty_area {
|
||||
dirty_region.union(old);
|
||||
}
|
||||
|
||||
let mut render_context = RenderContext {
|
||||
renderer: context.renderer,
|
||||
commands: CommandList::new(),
|
||||
gc_context: context.gc_context,
|
||||
library: context.library,
|
||||
transform_stack: &mut transform_stack,
|
||||
is_offscreen: true,
|
||||
stage: context.stage,
|
||||
allow_mask: true,
|
||||
};
|
||||
|
||||
// Make the screen opacity match the opacity of this bitmap
|
||||
|
||||
let clip_mat = clip_rect.map(|clip_rect| {
|
||||
// Note - we do *not* apply the matrix to the clip rect,
|
||||
// to match Flash's behavior.
|
||||
let clip_mat = Matrix {
|
||||
a: (clip_rect.x_max - clip_rect.x_min).to_pixels() as f32,
|
||||
b: 0.0,
|
||||
c: 0.0,
|
||||
d: (clip_rect.y_max - clip_rect.y_min).to_pixels() as f32,
|
||||
tx: clip_rect.x_min,
|
||||
ty: clip_rect.y_min,
|
||||
};
|
||||
|
||||
render_context.commands.push_mask();
|
||||
// The color doesn't matter, as this is a mask.
|
||||
render_context
|
||||
.commands
|
||||
.draw_rect(swf::Color::BLACK, clip_mat);
|
||||
render_context.commands.activate_mask();
|
||||
|
||||
clip_mat
|
||||
});
|
||||
|
||||
match &mut source {
|
||||
IBitmapDrawable::BitmapData(data) => {
|
||||
data.render(smoothing, &mut render_context);
|
||||
}
|
||||
IBitmapDrawable::DisplayObject(object) => {
|
||||
// Note that we do *not* use `render_base`,
|
||||
// as we want to ignore the object's mask and normal transform
|
||||
object.render_self(&mut render_context);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(clip_mat) = clip_mat {
|
||||
// Draw the rectangle again after deactivating the mask,
|
||||
// to reset the stencil buffer.
|
||||
render_context.commands.deactivate_mask();
|
||||
render_context
|
||||
.commands
|
||||
.draw_rect(swf::Color::BLACK, clip_mat);
|
||||
render_context.commands.pop_mask();
|
||||
}
|
||||
|
||||
write.update_dirty_texture(render_context.renderer);
|
||||
|
||||
let commands = if blend_mode == BlendMode::Normal {
|
||||
render_context.commands
|
||||
} else {
|
||||
let mut commands = CommandList::new();
|
||||
commands.blend(render_context.commands, blend_mode);
|
||||
commands
|
||||
};
|
||||
|
||||
let image = context
|
||||
.renderer
|
||||
.render_offscreen(handle, commands, quality, dirty_region);
|
||||
|
||||
match image {
|
||||
Some(sync_handle) => {
|
||||
write.set_gpu_dirty(sync_handle, dirty_region);
|
||||
Ok(())
|
||||
}
|
||||
None => Err(BitmapDataDrawError::Unimplemented),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue