fix cursor (hotx,hoty) && add default remote cursor image
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
5a25d0c9f7
commit
a3afb03109
@ -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),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user