diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 33cb672aa..a6e052634 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -3461,9 +3461,10 @@ class CursorModel with ChangeNotifier { List colors = json.decode(evt['colors']); final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); final ui.Image? image; + final platform = parent.target?.ffiModel.pi.platform; if (!isWeb && - parent.target?.ffiModel.pi.platform == kPeerPlatformMacOS) { - image = await _decodeMacCursor(rgba, width, height); + (platform == kPeerPlatformMacOS || platform == kPeerPlatformWindows)) { + image = await _decodeStraightAlphaCursor(rgba, width, height); } else { image = await img.decodeImageFromPixels( rgba, width, height, ui.PixelFormat.rgba8888); @@ -3484,10 +3485,10 @@ class CursorModel with ChangeNotifier { _updateCurData(); } - Future _decodeMacCursor( + Future _decodeStraightAlphaCursor( Uint8List rgba, int width, int height) async { - // macOS packets keep straight alpha, regardless of density metadata. - // Premultiply only for native ui.Image; the PNG cache needs straight colors. + // macOS and Win32 capture send straight alpha; XFixes/DRM are premultiplied. + // Convert a copy for native ui.Image, preserving the wire and PNG cache colors. final source = img2.Image.fromBytes( width: width, height: height, bytes: rgba.buffer, order: img2.ChannelOrder.rgba); for (final pixel in source) { diff --git a/flutter/test/cursor_native_alpha_test.dart b/flutter/test/cursor_native_alpha_test.dart index a871db530..35c2a1e56 100644 --- a/flutter/test/cursor_native_alpha_test.dart +++ b/flutter/test/cursor_native_alpha_test.dart @@ -69,6 +69,8 @@ void main() { (kPeerPlatformMacOS, '0', [64, 32, 16, 128]), (kPeerPlatformMacOS, '1', [64, 32, 16, 128]), (kPeerPlatformMacOS, '2', [64, 32, 16, 128]), + (kPeerPlatformWindows, null, [64, 32, 16, 128]), + (kPeerPlatformWindows, '0', [64, 32, 16, 128]), (kPeerPlatformLinux, '0', [32, 16, 8, 128]), ]) { for (final dpr in [1.0, 2.0]) { @@ -119,6 +121,8 @@ Future _checkCursor((String, String?, List) testCase, double dpr, bytes: straight!.buffer, bytesOffset: straight.offsetInBytes, order: img.ChannelOrder.rgba)); + // Real sessions own FFI strongly throughout asynchronous cursor decoding. + expect(cursor.parent.target, same(ffi)); } void _checkRegistration(Map args, double dpr) {