Visitar URL original
fix(imaging): Let a long-lived source drop the brushes it painted by mikernet · Pull Request #25633 · unoplatform/uno · GitHub
Skip to content

fix(imaging): Let a long-lived source drop the brushes it painted - #25633

Merged
Xiaoy312 merged 15 commits into
unoplatform:masterfrom
mikernet:dev/mikernet/image-brush-source-leak-25596
Oct 28, 2026
Merged

Xiaoy312 merged 15 commits into
unoplatform:masterfrom
mikernet:dev/mikernet/image-brush-source-leak-25596

Conversation

@mikernet

@mikernet mikernet commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

GitHub Issue: closes #25596

PR Type:

🐞 Bugfix

What changed? 🚀

Stacked on #25124, which fixes the Image half of #25596. Until #25124 merges, this PR also shows its commits; the last two are this PR's.

A source that outlives the brushes that paint it, such as an icon in a static field or in the app resources, kept every ImageBrush that ever used it alive, and with each brush the element it painted (the brush's inheritance context parent). The source held each brush through two references, and either one alone was enough (#25124 review, probes P06 to P09):

  • its subscription, whose callback captures the brush;
  • its internal Invalidated event, which the brush handled.

Now the source holds both weakly:

  • Subscription: ImageSource.SubscribeWeakly keeps the brush's callback through a weak reference. The brush holds the returned lease, which keeps the callback alive for as long as the brush lives. Once the brush is collected, the lease's finalizer ends the subscription on the UI thread, so the source can release the decoded pixels it owns, like those of any image nothing displays. Pixels shared through the bitmap cache stay with the cache, as before.
  • Open in flight: while a source opens, it keeps these callbacks alive, as it did when it held them strongly. An app may wait for a brush's ImageOpened with nothing else referencing the brush. A brush that leaves the source meanwhile isn't kept.
  • Invalidation: Invalidated becomes RegisterInvalidated, a weak registration built on WeakEventHelper, as Brush.RegisterInvalidateRender is. The brush keeps the registration, on the source it shows even when an ImageOpened handler switched it while it subscribed.

Image is unchanged: since #25124 it subscribes only while it is loaded.

Nothing else holds image consumers this way: Image and ImageBrush are the only callers of ImageSource.Subscribe, the brush was the only Invalidated handler, and no framework code handles the public ImageOpened, ImageFailed, Opened or OpenFailed events of a source. A source with a single owner still keeps it as its inheritance context parent, which #25102 addresses.

Tests

In Given_Image_Release (Skia):

  • When_Shared_Source_Outlives_Brushes_Then_Brushes_And_Borders_Collected: two ImageBrushes on two Borders share a stream bitmap, an SVG or a cached UriSource. Once the Borders leave the tree, the brushes and the Borders are collected.
  • When_Brushes_Of_Shared_Source_Collected_Then_Decoded_Image_Released: once those brushes are collected, a stream source releases its decoded pixels.
  • When_Collections_Run_While_Brush_Painted_Then_Brush_Still_Notified (desktop): a brush still painted keeps getting its source's images and change notifications across collections.
  • When_Only_App_Waits_For_Brush_While_Source_Downloads_Then_ImageOpened_Raised (desktop): brushes nothing else references still raise ImageOpened once their source finishes downloading.
  • When_Brush_Leaves_Source_While_It_Downloads_Then_Brush_Collected (desktop): a brush that left the source while another one keeps its download going is collected.
  • When_Open_Handler_Switches_Brush_Source_Then_Brush_Told_Of_Replacement_Changes (desktop): a brush whose ImageOpened handler switched it to another source is told when that source changes.
  • When_Brush_Open_Handler_Throws_Then_Its_Subscription_Still_Ends: a brush whose ImageOpened handler threw while it subscribed doesn't keep its source's pixels once it is collected.

Each test fails with the change it covers reverted. On #25124 alone, the leak and release tests fail. The guard test fails when the lease stops keeping the brush's callback, or the brush stops keeping its Invalidated registration.

Runs:

  • Skia desktop, 23 image-related classes: 307 passed, 5 skipped, 1 inconclusive and 1 failed. The failure, When_WriteableBitmap_Assigned_With_Data_Present, also fails with master's code on this machine, which runs at 125% display scaling (see fix(imaging): Release a decoded image when nothing displays it #25124). The inconclusive one, When_Render_Border_GetPixelsAsync, needs 100% scaling.
  • Skia desktop, Given_Border, Given_PersonPicture and Given_BitmapEncoder: only Check_Border_Margin (2 cases) fails, the same way without this change (125% scaling).
  • WebAssembly, 10 image classes (with Given_PersonPicture): 110 passed, 34 desktop-only tests skipped, 2 inconclusive, and the same When_WriteableBitmap_Assigned_With_Data_Present failure. The three new tests that aren't desktop-only pass there.

PR Checklist ✅

A decoded image's pixels were only freed through a chain of
finalizers, each link needing another collection.
CompositionImageSurface can now release its decoded frames and cached
texture on demand, staying usable for a later load, and reports the
bytes they hold, so a source can release what nothing displays and the
undisplayed bytes can be bounded. ImageData marks data shared through
the bitmap cache, which no single source may release.
WinUI frees the surfaces of an image that left the tree once it has
been off it for 1 s (sc_imageSurfaceWrapperReleaseDelayMilliseconds,
CCoreServices::TrimMemoryOnSurfaceWrappers). PendingImageReleases
keeps what nothing displays for that delay and releases it at the end
of a layout pass, so an element re-parented or recycled meanwhile keeps
its pixels.

Uno decodes images in full unless asked otherwise, so the waiting
images are also bounded, oldest first, by
FeatureConfiguration.Image.MaxUndisplayedImageBytes: 128 MB on
desktop, 64 MB on Android, iOS and tvOS, 32 MB on WebAssembly. The
waits are two queues, ReleaseDelay in the order they end and data
released after the pass, so a check costs what it releases.
An ImageSource kept its decoded data after its last subscriber left.
It now hands the data it owns to PendingImageReleases then, and takes
it back if a subscriber returns in time.

Data it replaced stays while a subscriber still draws it or was given
it, as WinUI keeps the surface while it switches URIs. Each
subscription tracks what it draws apart from what it was given, and a
record per surface counts the holds, the Images that left with it,
which then waits the full delay so they can come back with it, and its
wait. A source that failed or was cleared gives a returning subscriber
an empty image. A notification skips subscriptions disposed meanwhile
and stops once one changes the source, and Dispose opens the source
again for what still shows it.
The Skia Image had no unload handling, so an unloaded Image kept its
subscription, and its pixels, for its own lifetime. It now releases it
when unloaded and subscribes again when loaded, passing what its sprite
still draws, and reports what it draws once measured. It raises
ImageOpened once per source, as WinUI, and shows nothing when its
source fails.

ImageBrush keeps one subscription across UriSource changes, as the
source reopens for it, and lets go of its surface when given another
ImageSource, as a WinUI brush draws its source's surface. Both keep
only the last subscription when a handler changes the source while
they subscribe.
Clicking quickly through large images supersedes opens whose decodes
still land, and their surfaces were left to finalization. A superseded
open now releases what it produced, unless the bitmap cache shares it,
whose load keeps running for its other requesters. An open that app
code supersedes or ends before it returns leaves the source's state
alone.

BitmapImage checks after each size setter, so a superseded open can't
overwrite the replacement's size or raise its events, and ImageOpened
is raised once per source, as WinUI. A RenderTargetBitmap open no
longer runs on the open's token, so a superseded result still reaches
Open, which releases it.
A loopback server that holds a response's body lets a test act while a
download is in flight. Given_BitmapImage had one; it is now a helper
the image release tests use too, accepting off the UI thread so a test
can hold that thread while a response is pending. The download tests
load their Images in a panel, as an unloaded Image no longer opens its
source.
Given_Image_Release observes each surface's release directly or
through a codec wrapper: waits and the limit, replaced pixels, Images
that leave and return, brushes, re-entrant handlers, superseded opens
and events. Tests that need the loopback server or hold the UI thread
run on Skia desktop.
A load that has already completed is published before
TryOpenSourceAsync returns, and its PixelWidth, ImageOpened and
ImageFailed handlers run then. Open marked itself in flight only once
the loader returned, so a handler that unloaded the last Image found
neither data nor an open, nothing was queued, and the decode that
followed stayed outside both the delay and the limit.

Open now marks itself in flight first, and a decode that completes
while nothing subscribes and nothing waits starts its own wait.
A source kept its failure after its last subscriber left, and an Image
that came back alone took the path that opens a source nothing
subscribes to: it retried the unchanged source, raising ImageFailed
again on BitmapImage and Image, where one that attached while another
subscriber remained got nothing.

As on WinUI, where a source that needs no reload is not reloaded for
an element that attaches to it, a failure now stays until the source
changes. An Image still drawing an earlier image gets an empty one, as
when other subscribers remain.
An icon held in a static field or an app resource outlives the Images
that show it. On master an Image subscribes to its source until its
Source changes, and the source keeps that callback, which captures the
Image, in a strong list: every Image that ever showed the icon stays
alive, with everything it references.

An Image now subscribes only while it is loaded, so the source lets go
of it once it leaves the tree. Cover a bitmap, a cached URI and an SVG
source, each shared by two Images, as icons are: a source with a single
owner still keeps it as its inheritance context parent, which this does
not change.

Refs unoplatform#25596
An auto-sized Image showing a source that released its pixels, because
nothing displayed them for the release delay, measured a surface with
no size and laid out at 0×0 until the source decoded them again. With
the bitmap cache off, a URI downloads again meanwhile, so the layout
around the Image collapsed and then grew back. This happened to an
Image coming back to the tree, and to another Image given the source,
such as a recycled list item.

A source now keeps the pixel size of what it released until it opens
again or changes, and an Image measures with it meanwhile, as WinUI
keeps the image's size. A source that fails or is cleared still leaves
the Image empty.
An Image now subscribes to its source only while it is loaded, so one
that was never added to the tree no longer opens its source or raises
ImageOpened, as on WinUI. Cover it, and that the source opens once the
Image is loaded.
Releasing a surface built around a texture would dispose the texture,
which is its content: nothing could decode it again. No image source
creates such a surface today, but one that did would have shown
nothing once its Images came back. A source now neither counts nor
releases one, and the surface itself ignores a release.
Copilot AI balanced review requested due to automatic review settings October 21, 2026 20:40
@github-actions github-actions Bot added area/skia ✏️ Categorizes an issue or PR as relevant to Skia kind/documentation labels Oct 21, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

In-flight opens still retain explicitly unsubscribed brushes through the callback keep-alive list.

1 open issue
What changed in this PR

Fixes #25596’s ImageBrush retention path, building on #25124’s image subscription and decoded-pixel lifetime changes.

Changes:

  • Uses weak brush subscriptions and invalidation registrations.
  • Coordinates decoded-image release with layout, cancellation, and memory limits.
  • Adds lifetime regression tests and documents the memory setting.
File Description
src/​Uno.UI/​UI/​Xaml/​Media/​PendingImageReleases.cs Schedules delayed, memory-bounded releases.
src/​Uno.UI/​UI/​Xaml/​Media/​Imaging/​RenderTargetBitmap.cs Preserves cleanup of superseded results.
src/​Uno.UI/​UI/​Xaml/​Media/​Imaging/​BitmapSource.cs Versions stream changes.
src/​Uno.UI/​UI/​Xaml/​Media/​Imaging/​BitmapImage.cs Handles shared ownership and superseded loads.
src/​Uno.UI/​UI/​Xaml/​Media/​ImageSource.cs Adds weak invalidation registration.
src/​Uno.UI/​UI/​Xaml/​Media/​ImageSource.crossruntime.cs Manages subscriptions and surface lifetimes.
src/​Uno.UI/​UI/​Xaml/​Media/​ImageData.cs Marks cache-shared data.
src/​Uno.UI/​UI/​Xaml/​Media/​ImageBrush.cs Tracks current-source invalidation weakly.
src/​Uno.UI/​UI/​Xaml/​Media/​ImageBrush.crossruntime.cs Uses weak, reentrancy-aware subscriptions.
src/​Uno.UI/​UI/​Xaml/​Internal/​CoreServices.cs Integrates release checks with layout.
src/​Uno.UI/​UI/​Xaml/​Controls/​Image/​Image.cs Subscribes while loaded and acknowledges displayed data.
src/​Uno.UI/​FeatureConfiguration.cs Adds undisplayed-image memory limits.
src/​Uno.UI.RuntimeTests/​Microsoft/​UI/​Xaml/​Media/​Imaging/​Given_BitmapImage.cs Adapts download tests to loaded images.
src/​Uno.UI.RuntimeTests/​Microsoft/​UI/​Xaml/​Controls/​Given_Image.cs Tests reload sizing and off-tree behavior.
src/​Uno.UI.RuntimeTests/​Microsoft/​UI/​Xaml/​Controls/​Given_Image_Release.cs Covers collection, release, and reentrancy.
src/​Uno.UI.RuntimeTests/​Helpers/​GatedImageServer.cs Provides controlled downloads for tests.
src/​Uno.UI.Composition/​Composition/​CompositionImageSurface.skia.cs Adds byte accounting and explicit frame release.
doc/​articles/​feature-flags.md Documents image memory limits.

Review effort: Balanced

An ImageBrush subscribed to its source, and handled the source's
Invalidated event, until its ImageSource changed, and the source held
both strongly. A source that outlives its brushes, such as an icon in a
static field or in the app resources, therefore kept every ImageBrush
that ever painted it alive, and with each one the element it painted,
its inheritance context parent. Either reference alone was enough.

The source now holds both weakly:
- SubscribeWeakly keeps the brush's callback through a weak reference
  and returns a lease, which the brush holds and which keeps the
  callback alive as long as the brush lives. Once the brush is
  collected, the lease's finalizer ends the subscription on the UI
  thread, so the source can release the decoded pixels it owns, as for
  any image nothing displays. A delivery that finds the callback gone
  ends it too. The lease is linked to its subscription before anything
  is delivered, so an ImageOpened handler that throws can't leave a
  subscription nothing ends, and it refers to the subscription weakly,
  so a source collected with the brush isn't brought back.
- While an open is in flight, the source keeps the callbacks of these
  subscribers alive, as it did when it held them strongly: an app may
  wait for a brush's ImageOpened with nothing else referencing it.
  A subscription that ends lets its callback go then.
- Invalidated becomes RegisterInvalidated, a weak registration the
  brush keeps, as Brush.RegisterInvalidateRender is. The brush registers
  with the source it shows, which an ImageOpened handler may have
  changed while the brush subscribed.

Image is unchanged: it subscribes only while it is loaded.

Fixes unoplatform#25596
Two brushes on two Borders share a stream bitmap, an SVG or a cached
UriSource, as icons are. Once the Borders left the tree, the brushes and
the Borders are collected, and a stream source releases its decoded
pixels once the brushes are gone. A cached URI's pixels stay with the
cache, as before.

Guard the weak references the source now holds:
- a brush still painted keeps getting its source's images and change
  notifications across collections;
- brushes only an app waits on still raise ImageOpened when their
  source finishes downloading;
- a brush whose ImageOpened handler switched it to another source is
  told when that source changes;
- a brush whose ImageOpened handler threw while it subscribed doesn't
  keep its source's pixels once it is collected.
@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-25633/docs/index.html

@mikernet
mikernet force-pushed the dev/mikernet/image-brush-source-leak-25596 branch from 7bcb1e9 to 9fcac22 Compare October 21, 2026 21:15
Copilot AI balanced review requested due to automatic review settings October 21, 2026 21:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Finalizer-driven cleanup and surface ownership across asynchronous loads and reentrant layout lifecycles warrant human review and cross-platform runtime validation.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-25633/docs/index.html

mikernet added a commit to Singulink/uno that referenced this pull request Oct 21, 2026
Port of unoplatform#25633 (9f71cb9 and 9fcac22), with the
shared-source Image test of unoplatform#25124's fa4a240, whose helpers its
tests use.

A source that outlives its brushes, such as an icon in a static field
or in the app resources, kept every ImageBrush that ever painted it
alive, and with each one the element it painted. The source held the
brush's subscription and its Invalidated handler strongly. It now
holds both weakly: the brush keeps a lease on its subscription, whose
finalizer ends the subscription once the brush is collected, so the
source can release the pixels it owns; an open in flight keeps the
brushes waiting for it alive, but not one that left the source; and
Invalidated becomes a weak RegisterInvalidated registration.

6.8 differences:
- The open's completion also ends a shared decode's open use (the
  unoplatform#25559 port), so the keep-alive release sits inside that try.
- The native Image's WriteableBitmap handler (Image.cs) uses the weak
  registration too. The native WebAssembly brush keeps its own
  per-element subscription, unchanged.
- The test surface type is SkiaCompositionSurface.

@Xiaoy312 Xiaoy312 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving this PR's own two commits (9f71cb9989, 9fcac225b5). It still has to wait for #25124 to merge.

The design holds up:

  • A lease keeps the brush's callback alive, and the source keeps that callback only weakly.
  • The source holds callbacks strongly only while an open is in flight, and a subscription that ends lets its callback go.
  • Invalidated is now a weak registration, the same pattern as Brush.RegisterInvalidateRender.

Things I checked that hold:

  • Deliver can dispose a subscription whose callback was collected, because OnOpened loops over a copy (ToArray) of the subscriptions.
  • The lease tracks its subscription with a GCHandle rather than a WeakReference, which could be finalized before the lease.
  • When an ImageOpened handler switches the source, registering Invalidated on brush.ImageSource gives the right result whichever order the property callbacks run in.

Local run. Skia desktop, Windows, at the PR head, with retries off (attempts=1): all 59 cases of Given_Image_Release pass, including the 7 new tests (9 cases).

Comments, none blocking:

  1. CI. Uno.UI - CI hasn't run on this PR or on #25124. Three of the new tests aren't limited to desktop, so they'll also run on WASM and Skia mobile:

    • When_Shared_Source_Outlives_Brushes_Then_Brushes_And_Borders_Collected
    • When_Brushes_Of_Shared_Source_Collected_Then_Decoded_Image_Released
    • When_Brush_Open_Handler_Throws_Then_Its_Subscription_Still_Ends

    They wait for objects to be collected, within TryWaitUntilCollected's 3 s limit, so please get a green pipeline before merging.

  2. A brush whose ImageOpened handler throws while it subscribes. The lease never gets back to the brush, so the next GC ends the subscription even if the brush is still painted. The property callback runs before OnPropertyChanged2, so the throw probably also skips RegisterInvalidated. Before, such a brush leaked but kept updating; now it stops updating after the next GC. When_Brush_Open_Handler_Throws_Then_Its_Subscription_Still_Ends asserts that the subscription ends, so this looks intended. If a live brush should keep working, SubscribeWeakly could hand the lease back before rethrowing. The trigger is an app handler that throws, so this is low priority.

  3. Note. A brush stays alive for as long as its source's open lasts, up to the HTTP timeout for a download that hangs. That's no worse than before, and a brush that leaves the source is released at once, which the tests cover.

@Xiaoy312

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Sample App stage site is ready! Visit it here:

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-25633/docs/index.html

@nventive-devops

Copy link
Copy Markdown
Contributor

The build 242009 found UI Test snapshots differences: skia-linux-screenshots: 118, skia-windows-screenshots: 2458

Details
  • skia-linux-screenshots: 118 changed over 2458

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • Buttons.png-dark
    • Buttons.png
    • CalendarView_Theming.png-dark
    • CalendarView_Theming.png
    • CompositionEffectBrush.png-dark
    • CompositionEffectBrush.png
    • CompositionNineGridBrush_Source_Changes.png-dark
    • CompositionNineGridBrush_Source_Changes.png
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • ClipboardTests.png-dark
    • DropDownButtonPage.png-dark
    • Examples.png
    • DoubleImageBrushInList.png-dark
    • DoubleImageBrushInList.png
    • Focus_FocusVisual_Properties.png-dark
    • Focus_FocusVisual_Properties.png
    • DisplayInformation.png-dark
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
  • skia-windows-screenshots: 2458 changed over 2492

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • Accessibility_ScreenReader.png
    • Accessibility_ScreenReader.png-dark
    • AppBarButtonWithIconTest.png
    • AppBarButtonTest.png-dark
    • ApplicationViewMode.png
    • AppBarToggleButtonTest.png
    • AppWindowClosing.png
    • ApplicationViewSizing.png
    • AppWindowPresenters.png
    • Application_DispatcherShutdownMode.png
    • Arrange_Performance01.png
    • AppWindowTitleBar Properties.png-dark
    • Attributed_text_Supserscript.png-dark
    • AppXamlDefinedResources.png-dark
    • AutoBorderStretchwithleftmargin.png
    • Attributed_text_FontSize_Changing.png
    • AutoBorderStretchwithtopmargin.png-dark
    • Attributed_text_Supserscript.png
    • AutoBorderStretchwithtopmargin.png
    • AuthenticationBroker_Cancel.png-dark

@mergify

mergify Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

This pull request does not currently match the merge queue conditions, so it cannot be queued from here. The box comes back if it matches again.

@Xiaoy312
Xiaoy312 merged commit 1d33309 into unoplatform:master Oct 28, 2026
49 checks passed
mikernet added a commit to Singulink/uno that referenced this pull request Nov 14, 2026
Takes Uno's servicing/6.8 from 6951c2c up to 5254ace (42 pull
requests). Conflicts, by area:

IME and TextBox (unoplatform#25984, backport of unoplatform#25804; unoplatform#25952, backport of unoplatform#25619;
unoplatform#25976, backport of unoplatform#25818)
- Takes upstream's composition design (unoplatform#25804): the browser's input is
  the source of truth for the text, compositions included. This drops
  the composition parts of the unoplatform#24502 port (90746b8): its
  TypeScript composition handling, the StartIndex/TextChangePending
  event arguments, the IsReadOnly forwarding to the hidden input, and
  its Given_TextBox.BrowserSoftKeyboard tests.
- Keeps the non-IME browser fixes on top of it, in the form their
  master pull requests have on unoplatform#25804: Enter raised once per press and
  tracked by key code (unoplatform#25174, with Given_TextBox.BrowserEnterKey from
  master in place of the Enter tests that lived in the dropped file),
  the iOS refocus on tap (unoplatform#25185), pastes without text and isIOS from
  BrowserInputHelper.
- Moves the unoplatform#26005 MaxLength fixes onto unoplatform#25804's native input path, as
  master has them: a refused browser edit keeps the TextBox's selection,
  TextBoxView.UpdateTextFromNative reports whether the edit went in, and
  the IME keeps its range on the applied text (master be85699, which
  d226897 had ported onto the unoplatform#24502 IME). Adds that commit's browser
  and platform-applied IME tests.
- TextBox.pointers.skia.cs: upstream's file (unoplatform#25619 iOS touch editing,
  unoplatform#25818, unoplatform#25804), with the unoplatform#25126 port (the hold acts when it starts,
  a touch right-tap keeps the caret) in the form master's unoplatform#25126 branch
  has it on unoplatform#25619, including focusing before the iOS hold timer acts
  and its iOS not-focusable test.

Pointers (unoplatform#25976, backport of unoplatform#25818)
- Tapped and RightTapped are raised once the PointerReleased bubbled
  (unoplatform#25818), with the unoplatform#25732 port's window coordinates (2-argument
  routed event args, RawPosition for ContextRequested), as master's
  unoplatform#25732 branch has them.
- The release wrapper of unoplatform#25818 keeps the unoplatform#25126 port's
  StopContextMenuTimer at the start of the release.

DependencyObject (unoplatform#26142, backport of unoplatform#26123)
- Takes upstream, then the code of unoplatform#25839 (97a2f17), which replaces
  this branch's unoplatform#25102 ports (800247f, be41da6, fd40bf9,
  7546379). DependencyObjectStore.Binder.cs, .alc.cs,
  Application.Alc.cs, ResourceDictionary.alc.cs and their tests now
  match unoplatform#25839; this drops 800247f's own
  SingleParentNonFE_Parent_Removes_Value_Then_Next_Parent_Starts_Clean
  test, which unoplatform#25839 doesn't have.

Imaging (unoplatform#25597, unoplatform#25915, unoplatform#25911, unoplatform#25897)
- Keeps this branch's side: it has the later forms of these fixes
  (unoplatform#25124, unoplatform#25559, unoplatform#25633 and unoplatform#26370 ports), where upstream has the
  6.8-only unoplatform#25597 design and earlier snapshots. Upstream's shared
  download, IgnoreImageCache and failed-load fixes are covered by
  SharedImageDecode. Adds upstream's When_Source_Outlives_Brushes test.

ToolTip (unoplatform#26098, backport of unoplatform#25137 and unoplatform#26096)
- Keeps this branch's unoplatform#25137 port and adds unoplatform#26096 in master's form: the
  service releases only a ToolTip it opened, and leaves one the app
  opened during the show delay to the app. Keeps both sides' tests.

Flyout (unoplatform#26062, backport of unoplatform#26060)
- This branch had the same commits already; keeps the "Keep
  non-resizable flyouts in window" port and its tests.

Also removes a helper pair in Given_AccessibleTextBox that both sides
added.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/skia ✏️ Categorizes an issue or PR as relevant to Skia kind/documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Skia] A long-lived ImageSource keeps every Image and ImageBrush that used it alive

5 participants