fix(cursor): address sizing and capture review findings

This commit is contained in:
fufesou
2026-09-12 00:19:10 +08:00
parent be5fb304d4
commit a7f1eb4c25
12 changed files with 434 additions and 50 deletions

View File

@@ -1121,21 +1121,23 @@ class _ImagePaintState extends State<ImagePaint> {
// changes, so read it live to follow the window across monitors.
final dpr = MediaQuery.devicePixelRatioOf(context);
bool isViewAdaptive() => c.viewStyle.style == kRemoteViewStyleAdaptive;
bool isViewScaled() =>
c.viewStyle.style == kRemoteViewStyleAdaptive ||
c.viewStyle.style == kRemoteViewStyleCustom;
bool isViewOriginal() => c.viewStyle.style == kRemoteViewStyleOriginal;
mouseRegion({child}) => Obx(() {
final useLocalSize = !isWeb &&
(isLinux || isMacOS || isWindows) &&
!zoomCursor.value &&
isViewAdaptive();
isViewScaled();
if (useLocalSize) _localCursorSize.ensureLoaded(dpr);
double getCursorScale() {
var c = Provider.of<CanvasModel>(context);
var cursorScale = 1.0;
if (isWindows) {
// debug win10
if (zoomCursor.value && isViewAdaptive()) {
if (zoomCursor.value && isViewScaled()) {
cursorScale = s * c.devicePixelRatio;
}
} else {
@@ -1296,7 +1298,6 @@ class _ImagePaintState extends State<ImagePaint> {
{bool useLocalSize = false}) {
final cursor = Provider.of<CursorModel>(context);
final cache = cursor.cache ?? preDefaultCursor.cache;
if (useLocalSize && _localCursorSize.value == null) return MouseCursor.defer;
cache?.localSize = useLocalSize ? _localCursorSize.value : null;
return buildCursorOfCache(cursor, scale, cache);
}
@@ -1305,7 +1306,6 @@ class _ImagePaintState extends State<ImagePaint> {
{bool useLocalSize = false}) {
final cursor = Provider.of<CursorModel>(context);
final cache = preForbiddenCursor.cache;
if (useLocalSize && _localCursorSize.value == null) return MouseCursor.defer;
cache?.localSize = useLocalSize ? _localCursorSize.value : null;
return buildCursorOfCache(cursor, scale, cache);
}

View File

@@ -2900,6 +2900,15 @@ class CursorData {
scale = _visibleSize == 0
? 1.0
: max(localSize!, kMinCursorSize) / _visibleSize;
// Sparse peer artwork must not amplify the native bitmap allocation.
const maxNativeCursorBitmapSize = 512;
final maxScale = maxNativeCursorBitmapSize / max(width, height);
if (scale > maxScale) {
if (oldScale != maxScale) {
debugPrint('Cursor $id exceeds the native bitmap limit; reducing scale');
}
scale = maxScale;
}
} else if (scale != 1.0) {
// Update data if scale changed.
final tgtWidth = (width * scale).toInt();
@@ -3461,6 +3470,8 @@ class CursorModel with ChangeNotifier {
if (await _updateCache(rgba, image, id, hotx, hoty, width, height)) {
_images[id]?.item1.dispose();
_images[id] = Tuple3(image, hotx, hoty);
} else {
image.dispose();
}
// Update last cursor data.
@@ -3484,7 +3495,8 @@ class CursorModel with ChangeNotifier {
final pixels =
await image.toByteData(format: ui.ImageByteFormat.rawStraightRgba);
if (pixels == null) {
throw StateError('Could not read straight-alpha cursor pixels');
debugPrint('Could not read straight-alpha cursor pixels: $id');
return false;
}
imgOrigin = img2.Image.fromBytes(
width: w, height: h, bytes: pixels.buffer, order: img2.ChannelOrder.rgba);
@@ -3493,13 +3505,18 @@ class CursorModel with ChangeNotifier {
ByteData? imgBytes =
await image.toByteData(format: ui.ImageByteFormat.png);
if (imgBytes == null) {
debugPrint('Could not encode cursor PNG: $id');
return false;
}
data = imgBytes.buffer.asUint8List();
if (isLinux || isMacOS) {
// Preserve the PNG's straight-alpha colors when resizing native cursors.
imgOrigin = img2.decodePng(data) ??
(throw const FormatException('Invalid native cursor PNG'));
final decoded = img2.decodePng(data);
if (decoded == null) {
debugPrint('Invalid native cursor PNG: $id');
return false;
}
imgOrigin = decoded;
}
}
final cache = CursorData(