diff --git a/core/src/display_object.rs b/core/src/display_object.rs index 0904753ca3e21..00a1295ec49e1 100644 --- a/core/src/display_object.rs +++ b/core/src/display_object.rs @@ -1446,6 +1446,11 @@ pub trait TDisplayObject<'gc>: if let Some(ctr) = self.as_container() { for child in ctr.iter_render_list() { + // Masks clip other children without contributing visible pixels. + // Their animation must not move the bitmap cache's origin. + if (child.clip_depth() > 0 && child.allow_as_mask()) || child.maskee().is_some() { + continue; + } let matrix = *matrix * child.base().matrix(); bounds = bounds.union(&child.render_bounds_with_transform(&matrix, true, view_matrix)); diff --git a/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/Test.as b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/Test.as new file mode 100644 index 0000000000000..9c53b905a614e --- /dev/null +++ b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/Test.as @@ -0,0 +1,148 @@ +package { + import flash.display.Bitmap; + import flash.display.BlendMode; + import flash.display.MovieClip; + import flash.display.Shape; + import flash.display.Sprite; + import flash.display.StageAlign; + import flash.display.StageScaleMode; + import flash.events.Event; + import flash.filters.ColorMatrixFilter; + import flash.geom.Rectangle; + import flash.system.fscommand; + + [SWF(width="480", height="300", frameRate="30", backgroundColor="#A0A0A0")] + public class Test extends MovieClip { + [Embed(source="maskee.png")] + private var Maskee:Class; + + private var containers:Array = []; + private var bitmaps:Array = []; + private var masks:Array = []; + private var frame:int = 0; + + public function Test() { + stage.scaleMode = StageScaleMode.NO_SCALE; + stage.align = StageAlign.TOP_LEFT; + + trace("Animated mask / filter bounds"); + trace("Top row, left to right: ColorMatrix+HardLight, ColorMatrix, HardLight"); + trace("Bottom row: no effects, ColorMatrix+moving clipping edge, ColorMatrix+moving bitmap"); + + for (var i:int = 0; i < 6; i++) { + var container:Sprite = new Sprite(); + container.x = 40 + (i % 3) * 160; + container.y = 35 + int(i / 3) * 150; + addChild(container); + + var bitmap:Bitmap = new Maskee(); + bitmap.scaleX = bitmap.scaleY = 4; + container.addChild(bitmap); + + var maskShape:Shape = new Shape(); + container.addChild(maskShape); + if (i == 4) { + drawMask(maskShape, 0, 0, 40, 80); + } else { + drawMask(maskShape, -12, -12, 104, 104); + } + bitmap.mask = maskShape; + + if (i == 0 || i == 1 || i == 4 || i == 5) { + container.filters = [new ColorMatrixFilter([ + 0.8, 0, 0, 0, 12, + 0, 0.9, 0, 0, 6, + 0, 0, 0.7, 0, 18, + 0, 0, 0, 1, 0 + ])]; + } + + if (i == 0 || i == 2) { + container.blendMode = BlendMode.HARDLIGHT; + } + + + containers.push(container); + bitmaps.push(bitmap); + masks.push(maskShape); + } + + addEventListener(Event.ENTER_FRAME, onFrame); + } + + private function drawMask( + maskShape:Shape, + x:Number, + y:Number, + width:Number, + height:Number + ):void { + maskShape.graphics.clear(); + maskShape.graphics.beginFill(0x000000); + maskShape.graphics.drawRect(x, y, width, height); + maskShape.graphics.endFill(); + } + + private function bounds(rect:Rectangle):String { + return rect.x.toFixed(2) + "," + + rect.y.toFixed(2) + "," + + rect.width.toFixed(2) + "," + + rect.height.toFixed(2); + } + + private function onFrame(event:Event):void { + frame++; + + var phase:int = (frame - 1) % 40; + var controlPhase:int = int((frame - 1) / 2) % 4; + var delta:Number = phase * 0.05; + + for (var i:int = 0; i < 6; i++) { + var maskShape:Shape = masks[i]; + var bitmap:Bitmap = bitmaps[i]; + + if (i == 4) { + // Intentional visible clipping motion. + maskShape.x = controlPhase * 10; + } else { + // Move the mask while it still fully covers the bitmap. + maskShape.x = -delta; + maskShape.y = -delta; + } + + if (i == 5) { + // Intentional bitmap motion. + bitmap.x = controlPhase * 2; + } + } + + if ( + frame == 1 || frame == 11 || frame == 19 || + frame == 35 || frame == 40 || frame == 60 || + frame == 123 + ) { + trace("frame=" + frame + " delta=" + delta.toFixed(2)); + + for (i = 0; i < 6; i++) { + trace( + "case=" + i + + " bitmap=" + + bounds(Bitmap(bitmaps[i]).getBounds(this)) + + " mask=" + + bounds(Shape(masks[i]).getBounds(this)) + + " container=" + + bounds(Sprite(containers[i]).getBounds(this)) + ); + } + } + + if (frame == 11 || frame == 19 || frame == 35) { + fscommand("captureImage", "frame" + frame); + } + if (frame == 123) { + trace("DONE"); + removeEventListener(Event.ENTER_FRAME, onFrame); + } + } + } +} diff --git a/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/frame11.expected.png b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/frame11.expected.png new file mode 100644 index 0000000000000..837c6c953fd12 Binary files /dev/null and b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/frame11.expected.png differ diff --git a/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/frame19.expected.png b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/frame19.expected.png new file mode 100644 index 0000000000000..837c6c953fd12 Binary files /dev/null and b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/frame19.expected.png differ diff --git a/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/frame35.expected.png b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/frame35.expected.png new file mode 100644 index 0000000000000..837c6c953fd12 Binary files /dev/null and b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/frame35.expected.png differ diff --git a/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/maskee.png b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/maskee.png new file mode 100644 index 0000000000000..53329c7710588 Binary files /dev/null and b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/maskee.png differ diff --git a/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/output.expected.png b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/output.expected.png new file mode 100644 index 0000000000000..837c6c953fd12 Binary files /dev/null and b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/output.expected.png differ diff --git a/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/output.txt b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/output.txt new file mode 100644 index 0000000000000..fc6c20bd74756 --- /dev/null +++ b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/output.txt @@ -0,0 +1,53 @@ +Animated mask / filter bounds +Top row, left to right: ColorMatrix+HardLight, ColorMatrix, HardLight +Bottom row: no effects, ColorMatrix+moving clipping edge, ColorMatrix+moving bitmap +frame=1 delta=0.00 +case=0 bitmap=40.00,35.00,80.00,80.00 mask=28.00,23.00,104.00,104.00 container=28.00,23.00,104.00,104.00 +case=1 bitmap=200.00,35.00,80.00,80.00 mask=188.00,23.00,104.00,104.00 container=188.00,23.00,104.00,104.00 +case=2 bitmap=360.00,35.00,80.00,80.00 mask=348.00,23.00,104.00,104.00 container=348.00,23.00,104.00,104.00 +case=3 bitmap=40.00,185.00,80.00,80.00 mask=28.00,173.00,104.00,104.00 container=28.00,173.00,104.00,104.00 +case=4 bitmap=200.00,185.00,80.00,80.00 mask=200.00,185.00,40.00,80.00 container=200.00,185.00,80.00,80.00 +case=5 bitmap=360.00,185.00,80.00,80.00 mask=348.00,173.00,104.00,104.00 container=348.00,173.00,104.00,104.00 +frame=11 delta=0.50 +case=0 bitmap=40.00,35.00,80.00,80.00 mask=27.50,22.50,104.00,104.00 container=27.50,22.50,104.00,104.00 +case=1 bitmap=200.00,35.00,80.00,80.00 mask=187.50,22.50,104.00,104.00 container=187.50,22.50,104.00,104.00 +case=2 bitmap=360.00,35.00,80.00,80.00 mask=347.50,22.50,104.00,104.00 container=347.50,22.50,104.00,104.00 +case=3 bitmap=40.00,185.00,80.00,80.00 mask=27.50,172.50,104.00,104.00 container=27.50,172.50,104.00,104.00 +case=4 bitmap=200.00,185.00,80.00,80.00 mask=210.00,185.00,40.00,80.00 container=200.00,185.00,80.00,80.00 +case=5 bitmap=362.00,185.00,80.00,80.00 mask=347.50,172.50,104.00,104.00 container=347.50,172.50,104.00,104.00 +frame=19 delta=0.90 +case=0 bitmap=40.00,35.00,80.00,80.00 mask=27.10,22.10,104.00,104.00 container=27.10,22.10,104.00,104.00 +case=1 bitmap=200.00,35.00,80.00,80.00 mask=187.10,22.10,104.00,104.00 container=187.10,22.10,104.00,104.00 +case=2 bitmap=360.00,35.00,80.00,80.00 mask=347.10,22.10,104.00,104.00 container=347.10,22.10,104.00,104.00 +case=3 bitmap=40.00,185.00,80.00,80.00 mask=27.10,172.10,104.00,104.00 container=27.10,172.10,104.00,104.00 +case=4 bitmap=200.00,185.00,80.00,80.00 mask=210.00,185.00,40.00,80.00 container=200.00,185.00,80.00,80.00 +case=5 bitmap=362.00,185.00,80.00,80.00 mask=347.10,172.10,104.00,104.00 container=347.10,172.10,104.00,104.00 +frame=35 delta=1.70 +case=0 bitmap=40.00,35.00,80.00,80.00 mask=26.30,21.30,104.00,104.00 container=26.30,21.30,104.00,104.00 +case=1 bitmap=200.00,35.00,80.00,80.00 mask=186.30,21.30,104.00,104.00 container=186.30,21.30,104.00,104.00 +case=2 bitmap=360.00,35.00,80.00,80.00 mask=346.30,21.30,104.00,104.00 container=346.30,21.30,104.00,104.00 +case=3 bitmap=40.00,185.00,80.00,80.00 mask=26.30,171.30,104.00,104.00 container=26.30,171.30,104.00,104.00 +case=4 bitmap=200.00,185.00,80.00,80.00 mask=210.00,185.00,40.00,80.00 container=200.00,185.00,80.00,80.00 +case=5 bitmap=362.00,185.00,80.00,80.00 mask=346.30,171.30,104.00,104.00 container=346.30,171.30,104.00,104.00 +frame=40 delta=1.95 +case=0 bitmap=40.00,35.00,80.00,80.00 mask=26.05,21.05,104.00,104.00 container=26.05,21.05,104.00,104.00 +case=1 bitmap=200.00,35.00,80.00,80.00 mask=186.05,21.05,104.00,104.00 container=186.05,21.05,104.00,104.00 +case=2 bitmap=360.00,35.00,80.00,80.00 mask=346.05,21.05,104.00,104.00 container=346.05,21.05,104.00,104.00 +case=3 bitmap=40.00,185.00,80.00,80.00 mask=26.05,171.05,104.00,104.00 container=26.05,171.05,104.00,104.00 +case=4 bitmap=200.00,185.00,80.00,80.00 mask=230.00,185.00,40.00,80.00 container=200.00,185.00,80.00,80.00 +case=5 bitmap=366.00,185.00,80.00,80.00 mask=346.05,171.05,104.00,104.00 container=346.05,171.05,104.00,104.00 +frame=60 delta=0.95 +case=0 bitmap=40.00,35.00,80.00,80.00 mask=27.05,22.05,104.00,104.00 container=27.05,22.05,104.00,104.00 +case=1 bitmap=200.00,35.00,80.00,80.00 mask=187.05,22.05,104.00,104.00 container=187.05,22.05,104.00,104.00 +case=2 bitmap=360.00,35.00,80.00,80.00 mask=347.05,22.05,104.00,104.00 container=347.05,22.05,104.00,104.00 +case=3 bitmap=40.00,185.00,80.00,80.00 mask=27.05,172.05,104.00,104.00 container=27.05,172.05,104.00,104.00 +case=4 bitmap=200.00,185.00,80.00,80.00 mask=210.00,185.00,40.00,80.00 container=200.00,185.00,80.00,80.00 +case=5 bitmap=362.00,185.00,80.00,80.00 mask=347.05,172.05,104.00,104.00 container=347.05,172.05,104.00,104.00 +frame=123 delta=0.10 +case=0 bitmap=40.00,35.00,80.00,80.00 mask=27.90,22.90,104.00,104.00 container=27.90,22.90,104.00,104.00 +case=1 bitmap=200.00,35.00,80.00,80.00 mask=187.90,22.90,104.00,104.00 container=187.90,22.90,104.00,104.00 +case=2 bitmap=360.00,35.00,80.00,80.00 mask=347.90,22.90,104.00,104.00 container=347.90,22.90,104.00,104.00 +case=3 bitmap=40.00,185.00,80.00,80.00 mask=27.90,172.90,104.00,104.00 container=27.90,172.90,104.00,104.00 +case=4 bitmap=200.00,185.00,80.00,80.00 mask=210.00,185.00,40.00,80.00 container=200.00,185.00,80.00,80.00 +case=5 bitmap=362.00,185.00,80.00,80.00 mask=347.90,172.90,104.00,104.00 container=347.90,172.90,104.00,104.00 +DONE diff --git a/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/test.swf b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/test.swf new file mode 100644 index 0000000000000..289d6c31d0c5b Binary files /dev/null and b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/test.swf differ diff --git a/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/test.toml b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/test.toml new file mode 100644 index 0000000000000..1eeadb79075b5 --- /dev/null +++ b/tests/tests/swfs/visual/cache_as_bitmap/animated_mask_filter_bounds/test.toml @@ -0,0 +1,19 @@ +num_frames = 124 + +[image_comparisons.frame11] +trigger = "fs_command" +tolerance = 1 + +[image_comparisons.frame19] +trigger = "fs_command" +tolerance = 1 + +[image_comparisons.frame35] +trigger = "fs_command" +tolerance = 1 + +[image_comparisons.output] +tolerance = 1 + +[player_options] +with_renderer = { optional = false, quality = "high" } diff --git a/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/Test.as b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/Test.as new file mode 100644 index 0000000000000..51b32c48126fe --- /dev/null +++ b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/Test.as @@ -0,0 +1,47 @@ +package { + import flash.display.MovieClip; + import flash.display.StageAlign; + import flash.display.StageQuality; + import flash.display.StageScaleMode; + import flash.events.Event; + import flash.system.fscommand; + + [SWF(width="160", height="160", frameRate="24", backgroundColor="#A0A0A0")] + public class Test extends MovieClip { + private var scene:MovieClip; + private var sampleFrame:int = 1; + private var holdFrames:int = 0; + + public function Test() { + stage.scaleMode = StageScaleMode.NO_SCALE; + stage.align = StageAlign.TOP_LEFT; + stage.quality = StageQuality.HIGH; + + addEventListener(Event.ENTER_FRAME, onFrame); + } + + private function onFrame(event:Event):void { + if (scene == null) { + // The build script adds the native timeline to the root frame. + if (numChildren == 0) { + return; + } + scene = MovieClip(getChildAt(0)); + trace("Timeline mask cache bounds"); + } + if (holdFrames > 0) { + holdFrames--; + return; + } + scene.gotoAndStop(sampleFrame); + holdFrames = 71; + trace("frame=" + scene.currentFrame); + fscommand("captureImage", "frame0" + sampleFrame); + if (sampleFrame == scene.totalFrames) { + removeEventListener(Event.ENTER_FRAME, onFrame); + trace("DONE"); + } + sampleFrame++; + } + } +} diff --git a/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/frame01.expected.png b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/frame01.expected.png new file mode 100644 index 0000000000000..143db719121c7 Binary files /dev/null and b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/frame01.expected.png differ diff --git a/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/frame02.expected.png b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/frame02.expected.png new file mode 100644 index 0000000000000..143db719121c7 Binary files /dev/null and b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/frame02.expected.png differ diff --git a/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/frame03.expected.png b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/frame03.expected.png new file mode 100644 index 0000000000000..143db719121c7 Binary files /dev/null and b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/frame03.expected.png differ diff --git a/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/frame04.expected.png b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/frame04.expected.png new file mode 100644 index 0000000000000..143db719121c7 Binary files /dev/null and b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/frame04.expected.png differ diff --git a/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/output.txt b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/output.txt new file mode 100644 index 0000000000000..e6992cea22223 --- /dev/null +++ b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/output.txt @@ -0,0 +1,6 @@ +Timeline mask cache bounds +frame=1 +frame=2 +frame=3 +frame=4 +DONE diff --git a/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/test.swf b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/test.swf new file mode 100644 index 0000000000000..07265ff85fdb2 Binary files /dev/null and b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/test.swf differ diff --git a/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/test.toml b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/test.toml new file mode 100644 index 0000000000000..0b3931f51ea5e --- /dev/null +++ b/tests/tests/swfs/visual/cache_as_bitmap/timeline_mask_cache_bounds/test.toml @@ -0,0 +1,20 @@ +num_frames = 240 + +[player_options] +with_renderer = { optional = false, quality = "high" } + +[image_comparisons.frame01] +trigger = "fs_command" +tolerance = 54 + +[image_comparisons.frame02] +trigger = "fs_command" +tolerance = 54 + +[image_comparisons.frame03] +trigger = "fs_command" +tolerance = 54 + +[image_comparisons.frame04] +trigger = "fs_command" +tolerance = 54