* Apply tile brush transforms once the tile sits in target space
Brush.Transform was concatenated into the shader matrix before the
translation that moves the tile onto the painted area, so it acted in
the tile's own space instead of the target's. A brush transform on a
fill away from the origin - the common case, since a relative viewport
resolves against the fill's bounds - came out displaced, and WPF, whose
behaviour these brushes follow, disagrees.
Both tile paths now place the tile first and let the transform act on
the result. Where the placement is identity, which is what the existing
goldens cover, the matrices are unchanged.
The cross suite gains an image brush so the non-scalable path is
covered too, plus a smooth ramp fixture: the star line drawing that was
the only image asset survives resampling as sparse speckle, which the
comparison metric cannot tell apart from a geometry error.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Apply the conic gradient transform after its angle
The sweep shader carried Angle as its local matrix and pre-concatenated
Brush.Transform onto it, so the transform acted on the raw sweep and
the angle then turned the result. Every other brush bakes its intrinsic
geometry in first and lets the brush transform act on the finished
pattern.
The visible effect was that a translation moved the gradient in a
direction rotated by Angle - 90 instead of the direction asked for. A
sweep gradient is fully determined by its centre and angle, so the
golden here is checked against the brush with its centre moved by the
same offset and no transform: the two renders are pixel-identical,
while the previous order is 0.169 rmse away against a 0.022 tolerance.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Add RelativeTransform to every brush
A brush transform expressed in the unit space of the painted bounds,
applied before the absolute Transform - WPF's Brush.RelativeTransform.
It lets one brush express a bounds-dependent transform, an SVG
gradientTransform in objectBoundingBox units for instance, without
baking any one consumer's bounds into a matrix, which is what makes a
single brush per gradient definition possible.
The property sits on IBrush and Brush, so every brush kind carries it
and a consumer reads it without a type test. Each immutable brush takes
it through a second constructor, leaving the existing signatures alone
and marking them for collapse in v13. The gradient brush animator
interpolates it alongside Transform, and the composition schema gains
it on both brush bases so the value reaches the render thread as a live
resource rather than a snapshot.
IBrush is NotClientImplementable, so the added member is suppressed for
API validation the way earlier additions to those interfaces are.
Nothing consumes it yet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Consume RelativeTransform in the Skia backend
The relative matrix is conjugated into target space at draw time - the
unit square maps onto the painted rect, whose origin translation and
scale wrap the matrix - and composes before the absolute brush
transform. All three gradient kinds and both tile brush paths read it;
a solid colour brush has nothing for it to act on, matching WPF. With
no relative transform every path reduces to the previous matrices, so
existing goldens stay byte-identical.
The cross suite compares the result against WPF for the linear and
radial gradients, a drawing brush tiled and untiled, an image brush,
the composition order against Transform, one brush shared by two
differently sized fills, and the solid colour no-op. Goldens under
Skia/Media additionally cover the immediate renderer and the GPU
backends, which the cross suite does not exercise; each was measured
against its WPF counterpart before being committed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* Implement visual geometry hit testing
* fix xml comment
* fix xml comment
* update api diff
* Introduced ICompositionHitTester to avoid duplicated hit test code between point and geometry
* Add geometry hit testing page to RenderDemo
* fix geometry hit testing not including stroke
* fix test
* update api and tests
* update api diff
* Update GeometryHitTestingPage with new API
* addressed reviews.
* remove nullability in GeometryHitTestResult Visual hit
---------
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
* Add render data opcode enum
First step of the Drawing/Nodes binary stream refactor.
* Add render data resource table
* Add render data stream writer and reader
Encodes the opcode stream field-by-field via BinaryPrimitives to
avoid unsafe and blittability assumptions.
* Add RenderDataStream with recording and replay
* Add hit-test walker to RenderDataStream
* Add bounds walker to RenderDataStream
* Add render data stream serialization
* Switch render data to the binary stream
* Move node level test coverage to the stream
* Delete the render data node classes
* Small optimization, stackalloc the render data walker scope stacks
* Optimize serialization for blittable types
* Encode render data via payload structs, add render data visitor
* Rename RenderDataResources.Add to AppendDeserialized
* Add effect support to render data stream
* Simplify render data read/write with MemoryMarshal
* add failing test for RenderTargetBitmap_DropShadowEffect
* Fix immidiateRenderer with Effect wasn't working as expected
* yet another render test
* XML comments for newly added members
* Address Copilot review
* Add another unit test to ensure the recent changes
don't get lost at some point in time
* address review
* Apply Review suggestion
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* inline effectPadding
* implement feedback for better bounds handling
* Update src/Avalonia.Base/Media/DrawingGroup.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* anohter minor fix
* Address PR #20790 review comments
- Fix RenderDataEffectNode.Bounds to return null when no children exist,
preventing empty effect nodes from incorrectly reporting non-null bounds
and causing render artifacts due to incorrect dirty rects.
Bounds now inflates child bounds by the effect output padding rather
than unioning with BoundsRect.
- Move effect output padding inflation from callers into DrawingContext
implementations (PlatformDrawingContext, RenderDataDrawingContext) so
callers pass content bounds to PushEffect and the API handles inflation
internally. Remove pre-inflation from ImmediateRenderer.
- Fix DrawingGroup.DrawCore to pass effectBounds (inflated) to
PushOpacityMask when an Effect is set, so the opacity mask covers
the full effect output region (e.g. shadow/blur extending beyond
visual bounds).
- Fix DrawingGroup.GetBounds to inflate EffectBounds (which now stores
content bounds) by the effect output padding.
- Fix existing compile error: Rect.IsEmpty() is a method, not property.
- Add failing tests for each of the above before fixing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix DrawingGroup.GetBounds() incorrectly inflating bounds with effect output padding
GetBounds() should return content/geometric bounds only, matching WPF behavior.
Inflating by GetEffectOutputPadding() caused DrawingImage to shift its coordinate
origin by the effect's extent (e.g. a 3.5px shadow offset would displace all
content by 3.5px), breaking Should_Render_DrawingGroup_With_Effect render test.
Effects render additively outside the content area and must not affect the
coordinate system established by GetBounds().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Prototype an AABB tree for hit testing
* Some optimizations
* Revert runtime knobs and add tests
* Avoid rebuilding the whole tree
* Revert an invalid optimization
* Add a hit test page
* Clean up
* Format code
* Nit
* Use weak CompositionVisual reference in server-side
* Bucket composition hit-test AABB tree by child order
* Use readback revisions for AABB hit-test updates
* Make some overlay-related types/members internal
* Make sure that TopLevel is no longer the actual root of the visual tree. This is needed for our future changes.
* API diff
* Apply suggestion from @Copilot
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Apply suggestion from @Copilot
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Fixed incorrect test
* Make automation to target FocusRoot
* api diff
* Hide WindowBase/EmbeddableControlRoot's parents from automation
* api diff
* Separate automation root and visual root for automation purposes
* Hide ChromeOverlayLayer from public API
* Synchronize WindowBase visibility to VisualRoot
* Hide WindowBase.ArrangeSetBounds
* api diff
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Enable nullability in UnitTests
* Enable nullability in Base.UnitTests
* Enable nullability in Markup.UnitTests
* Enable nullability in Markup.Xaml.UnitTests
- animation/layout/render cycle is now managed from a central location
- animations are now throttled if animation/layout/render pass takes longer than a frame which previously caused a soft-freeze with input not being processed
- the public API is trimmed to make sure that we can make other planned changes during the 11.x support cycle
"Changelog":
- IClock is hidden and is planned to be replaced later
- Animator classes are hidden and are planned to be refactored later
- IAnimation members are hidden, it's supposed to be a marker interface for Style.Animations collection now, to start animations manually use Animation.RunAsync
- Sealed several classes in Avalonia.Animation namespace
- Spring class is removed from the public API (it wasn't possible to use it directly in a meaningful way anyway)
- Sealed brushes, transforms, effects and drawings
- Removed separate dispatcher priorities for Layout and Composition, everything now happens from a central place with Render priority (same as WPF)
- - some private "hook" priorities are added for now, those will be removed later
- IRenderLoop is hidden and removed from locator
- IRenderer is hidden (the plan is to remove that concept later)
- - Renderer.Start/Stop exposed as StartRendering/StopRendering on the toplevel (will be on a CompositionTarget/PresentationSource-like type later)
- - Renderer.Diagnistics exposed as RendererDiagnostics (same)
- - Renderer is no longer created by the platform code and is created by TopLevel itself
- - From the user-code hit-testing should be done by VisualExtensions.GetVisual(s)At, which has the same features
- - For unit tests a separate IHitTester interface is added which can be changed for a particular toplevel
- ILayoutManager is hidden
- - LayoutManager.ExecuteLayoutPass() exposed as TopLevel.UpdateLayout()
- Custom animators now have a separate base class that only deals with interpolation
Minor improvements:
- Compositor has a mode that doesn't use DispatcherTimers, useful for unit tests
- Introduced ScopedTestBase that auto-resets the locator when test is finished
Fixes for #8092:
- Always round sizes up, not to the nearest pixel, thereby ensuring that `DesiredSize`s don't get rounded down where possible.
- Apply rounding to `Padding` and `BorderThickness` in measure pass as well as arrange pass, to ensure that `DesiredSize` takes this rounding into account.