fix cursor (hotx,hoty) && add default remote cursor image

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2022-11-13 23:41:07 +08:00
parent 5a25d0c9f7
commit a3afb03109
3 changed files with 83 additions and 58 deletions

View File

@ -340,10 +340,8 @@ class ImagePaint extends StatelessWidget {
return FlutterCustomMemoryImageCursor( return FlutterCustomMemoryImageCursor(
pixbuf: cache.data, pixbuf: cache.data,
key: key, key: key,
// hotx: cache.hotx, hotx: cache.hotx,
// hoty: cache.hoty, hoty: cache.hoty,
hotx: 0,
hoty: 0,
imageWidth: (cache.width * cache.scale).toInt(), imageWidth: (cache.width * cache.scale).toInt(),
imageHeight: (cache.height * cache.scale).toInt(), imageHeight: (cache.height * cache.scale).toInt(),
); );
@ -488,11 +486,19 @@ class CursorPaint extends StatelessWidget {
final m = Provider.of<CursorModel>(context); final m = Provider.of<CursorModel>(context);
final c = Provider.of<CanvasModel>(context); final c = Provider.of<CanvasModel>(context);
// final adjust = m.adjustForKeyboard(); // final adjust = m.adjustForKeyboard();
double hotx = m.hotx;
double hoty = m.hoty;
if (m.image == null) {
if (m.defaultCache != null) {
hotx = m.defaultCache!.hotx;
hoty = m.defaultCache!.hoty;
}
}
return CustomPaint( return CustomPaint(
painter: ImagePainter( painter: ImagePainter(
image: m.image, image: m.image ?? m.defaultImage,
x: m.x - m.hotx + c.x / c.scale, x: m.x - hotx + c.x / c.scale,
y: m.y - m.hoty + c.y / c.scale, y: m.y - hoty + c.y / c.scale,
scale: c.scale), scale: c.scale),
); );
} }

View File

@ -862,11 +862,19 @@ class CursorPaint extends StatelessWidget {
final c = Provider.of<CanvasModel>(context); final c = Provider.of<CanvasModel>(context);
final adjust = gFFI.cursorModel.adjustForKeyboard(); final adjust = gFFI.cursorModel.adjustForKeyboard();
var s = c.scale; var s = c.scale;
double hotx = m.hotx;
double hoty = m.hoty;
if (m.image == null) {
if (m.defaultCache != null) {
hotx = m.defaultCache!.hotx;
hoty = m.defaultCache!.hoty;
}
}
return CustomPaint( return CustomPaint(
painter: ImagePainter( painter: ImagePainter(
image: m.image, image: m.image ?? m.defaultImage,
x: m.x * s - m.hotx + c.x, x: m.x * s - hotx + c.x,
y: m.y * s - m.hoty + c.y - adjust, y: m.y * s - hoty + c.y - adjust,
scale: 1), scale: 1),
); );
} }

View File

@ -721,8 +721,11 @@ class CursorData {
height: (height * scale).toInt(), height: (height * scale).toInt(),
) )
.getBytes(format: img2.Format.bgra); .getBytes(format: img2.Format.bgra);
hotx = (width * scale) / 2; if (hotx > 0 && hoty > 0) {
hoty = (height * scale) / 2; // default cursor data
hotx = (width * scale) / 2;
hoty = (height * scale) / 2;
}
} }
} }
this.scale = scale; this.scale = scale;
@ -737,6 +740,7 @@ class CursorData {
class CursorModel with ChangeNotifier { class CursorModel with ChangeNotifier {
ui.Image? _image; ui.Image? _image;
ui.Image? _defaultImage;
final _images = <int, Tuple3<ui.Image, double, double>>{}; final _images = <int, Tuple3<ui.Image, double, double>>{};
CursorData? _cache; CursorData? _cache;
final _defaultCacheId = -1; final _defaultCacheId = -1;
@ -756,6 +760,7 @@ class CursorModel with ChangeNotifier {
WeakReference<FFI> parent; WeakReference<FFI> parent;
ui.Image? get image => _image; ui.Image? get image => _image;
ui.Image? get defaultImage => _defaultImage;
CursorData? get cache => _cache; CursorData? get cache => _cache;
CursorData? get defaultCache => _getDefaultCache(); CursorData? get defaultCache => _getDefaultCache();
@ -771,30 +776,45 @@ class CursorModel with ChangeNotifier {
DateTime.now().difference(_last_peer_mouse).inMilliseconds < DateTime.now().difference(_last_peer_mouse).inMilliseconds <
kMouseControlTimeoutMSec; kMouseControlTimeoutMSec;
CursorModel(this.parent); CursorModel(this.parent) {
_getDefaultImage();
_getDefaultCache();
}
Set<String> get cachedKeys => _cacheKeys; Set<String> get cachedKeys => _cacheKeys;
addKey(String key) => _cacheKeys.add(key); addKey(String key) => _cacheKeys.add(key);
Future<ui.Image?> _getDefaultImage() async {
if (_defaultImage == null) {
final defaultImg = defaultCursorImage!;
// This function is called only one time, no need to care about the performance.
Uint8List data = defaultImg.getBytes(format: img2.Format.rgba);
_defaultImage = await img.decodeImageFromPixels(
data, defaultImg.width, defaultImg.height, ui.PixelFormat.rgba8888);
}
return _defaultImage;
}
CursorData? _getDefaultCache() { CursorData? _getDefaultCache() {
if (_defaultCache == null) { if (_defaultCache == null) {
Uint8List data;
if (Platform.isWindows) { if (Platform.isWindows) {
Uint8List data = defaultCursorImage!.getBytes(format: img2.Format.bgra); data = defaultCursorImage!.getBytes(format: img2.Format.bgra);
_hotx = defaultCursorImage!.width / 2; } else {
_hoty = defaultCursorImage!.height / 2; data = Uint8List.fromList(img2.encodePng(defaultCursorImage!));
_defaultCache = CursorData(
peerId: id,
id: _defaultCacheId,
image: defaultCursorImage?.clone(),
scale: 1.0,
data: data,
hotx: _hotx,
hoty: _hoty,
width: defaultCursorImage!.width,
height: defaultCursorImage!.height,
);
} }
double scale = 1.0;
_defaultCache = CursorData(
peerId: id,
id: _defaultCacheId,
image: defaultCursorImage?.clone(),
scale: scale,
data: data,
hotx: (defaultCursorImage!.width * scale) / 2,
hoty: (defaultCursorImage!.height * scale) / 2,
width: defaultCursorImage!.width,
height: defaultCursorImage!.height,
);
} }
return _defaultCache; return _defaultCache;
} }
@ -926,13 +946,15 @@ class CursorModel with ChangeNotifier {
var height = int.parse(evt['height']); var height = int.parse(evt['height']);
List<dynamic> colors = json.decode(evt['colors']); List<dynamic> colors = json.decode(evt['colors']);
final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); final rgba = Uint8List.fromList(colors.map((s) => s as int).toList());
var pid = parent.target?.id;
final image = await img.decodeImageFromPixels( final image = await img.decodeImageFromPixels(
rgba, width, height, ui.PixelFormat.rgba8888); rgba, width, height, ui.PixelFormat.rgba8888);
if (parent.target?.id != pid) return;
_image = image; _image = image;
_images[id] = Tuple3(image, _hotx, _hoty); if (await _updateCache(image, id, width, height)) {
await _updateCache(image, id, width, height); _images[id] = Tuple3(image, _hotx, _hoty);
} else {
_hotx = 0;
_hoty = 0;
}
try { try {
// my throw exception, because the listener maybe already dispose // my throw exception, because the listener maybe already dispose
notifyListeners(); notifyListeners();
@ -941,44 +963,33 @@ class CursorModel with ChangeNotifier {
} }
} }
_updateCache(ui.Image image, int id, int w, int h) async { Future<bool> _updateCache(ui.Image image, int id, int w, int h) async {
Uint8List? data; ui.ImageByteFormat imgFormat = ui.ImageByteFormat.png;
img2.Image? image2;
if (Platform.isWindows) { if (Platform.isWindows) {
ByteData? data2 = imgFormat = ui.ImageByteFormat.rawRgba;
await image.toByteData(format: ui.ImageByteFormat.rawRgba);
if (data2 != null) {
data = data2.buffer.asUint8List();
image2 = img2.Image.fromBytes(w, h, data);
} else {
data = defaultCursorImage?.getBytes(format: img2.Format.bgra);
image2 = defaultCursorImage?.clone();
_hotx = defaultCursorImage!.width / 2;
_hoty = defaultCursorImage!.height / 2;
}
} else {
ByteData? data2 = await image.toByteData(format: ui.ImageByteFormat.png);
if (data2 != null) {
data = data2.buffer.asUint8List();
} else {
data = Uint8List.fromList(img2.encodePng(defaultCursorImage!));
_hotx = defaultCursorImage!.width / 2;
_hoty = defaultCursorImage!.height / 2;
}
} }
ByteData? imgBytes = await image.toByteData(format: imgFormat);
if (imgBytes == null) {
return false;
}
Uint8List? data = imgBytes.buffer.asUint8List();
_cache = CursorData( _cache = CursorData(
peerId: this.id, peerId: this.id,
id: id, id: id,
image: image2, image: Platform.isWindows ? img2.Image.fromBytes(w, h, data) : null,
scale: 1.0, scale: 1.0,
data: data, data: data,
hotx: _hotx, hotx: 0,
hoty: _hoty, hoty: 0,
// hotx: _hotx,
// hoty: _hoty,
width: w, width: w,
height: h, height: h,
); );
_cacheMap[id] = _cache!; _cacheMap[id] = _cache!;
return true;
} }
updateCursorId(Map<String, dynamic> evt) async { updateCursorId(Map<String, dynamic> evt) async {