Keep resized Web cursors within a proportional minimum

This commit is contained in:
fufesou
2026-09-13 10:58:34 +08:00
parent bbc8ee3bdb
commit 70c50402a2
3 changed files with 66 additions and 17 deletions

View File

@@ -2906,6 +2906,9 @@ class CursorData {
}
}
// Web's long-edge minimum can round a thin axis below one raster pixel.
final webWidth = max(1, (width * scale).round());
final webHeight = max(1, (height * scale).round());
if (resizeImage && _doubleToInt(oldScale) != _doubleToInt(scale)) {
if (isWindows) {
data = img2
@@ -2921,8 +2924,8 @@ class CursorData {
img2.encodePng(
img2.copyResize(
image,
width: isWeb ? (width * scale).round() : (width * scale).toInt(),
height: isWeb ? (height * scale).round() : (height * scale).toInt(),
width: isWeb ? webWidth : (width * scale).toInt(),
height: isWeb ? webHeight : (height * scale).toInt(),
interpolation: img2.Interpolation.average,
),
),
@@ -2935,8 +2938,8 @@ class CursorData {
hoty = hotyOrigin * scale;
if (isWeb) {
// CSS hotspots must follow the actual rounded PNG dimensions.
hotx = hotxOrigin * (width * scale).round() / width;
hoty = hotyOrigin * (height * scale).round() / height;
hotx = hotxOrigin * webWidth / width;
hoty = hotyOrigin * webHeight / height;
}
return scale;
}

View File

@@ -1,5 +1,6 @@
import 'dart:convert';
import 'dart:js' as js;
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@@ -52,8 +53,9 @@ class CursorManager {
'cursor',
jsonEncode({
'url': cursorData.url,
'hotx': cursorData.hotX.round(),
'hoty': cursorData.hotY.round(),
// Rounding must keep the hotspot inside even a one-pixel raster.
'hotx': cursorData.hotX.round().clamp(0, cursorData.width - 1),
'hoty': cursorData.hotY.round().clamp(0, cursorData.height - 1),
})
]);
}
@@ -104,7 +106,9 @@ MouseCursor buildCursorOfCache(
if (cache == null) {
return MouseCursor.defer;
} else {
final key = cache.updateGetKey(scale);
// A short-edge minimum can enlarge thin artwork beyond CSS cursor limits.
// Keep unzoomed images unchanged and use the long edge when resizing.
final key = cache.updateGetKey(scale, useLegacyMinimum: scale == 1.0);
if (!cursor.cachedKeys.contains(key)) {
// data should be checked here, because it may be changed after `updateGetKey()`
final data = cache.data;
@@ -116,8 +120,8 @@ MouseCursor buildCursorOfCache(
CursorManager.instance.registerCursor(CursorData(
key: key,
url: 'data:image/rgba;base64,${base64Encode(data)}',
width: (cache.width * cache.scale).round(),
height: (cache.height * cache.scale).round(),
width: max(1, (cache.width * cache.scale).round()),
height: max(1, (cache.height * cache.scale).round()),
hotX: cache.hotx,
hotY: cache.hoty));
cursor.addKey(key);