| 
									
										
										
										
											2022-09-10 19:50:48 -07:00
										 |  |  | import 'dart:typed_data'; | 
					
						
							|  |  |  | import 'dart:ui' as ui; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-09 23:00:34 +09:00
										 |  |  | import 'package:flutter/widgets.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-28 11:38:11 +08:00
										 |  |  | import 'package:flutter_hbb/common.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-27 09:27:30 +08:00
										 |  |  | Future<ui.Image?> decodeImageFromPixels( | 
					
						
							| 
									
										
										
										
											2022-09-10 19:50:48 -07:00
										 |  |  |   Uint8List pixels, | 
					
						
							|  |  |  |   int width, | 
					
						
							|  |  |  |   int height, | 
					
						
							|  |  |  |   ui.PixelFormat format, { | 
					
						
							|  |  |  |   int? rowBytes, | 
					
						
							|  |  |  |   int? targetWidth, | 
					
						
							|  |  |  |   int? targetHeight, | 
					
						
							| 
									
										
										
										
											2024-06-12 01:40:54 +08:00
										 |  |  |   VoidCallback? onPixelsCopied, // must ensure onPixelsCopied is called no matter this function succeeds
 | 
					
						
							| 
									
										
										
										
											2022-09-10 19:50:48 -07:00
										 |  |  |   bool allowUpscaling = true, | 
					
						
							|  |  |  | }) async { | 
					
						
							|  |  |  |   if (targetWidth != null) { | 
					
						
							|  |  |  |     assert(allowUpscaling || targetWidth <= width); | 
					
						
							| 
									
										
										
										
											2024-05-27 09:27:30 +08:00
										 |  |  |     if (!(allowUpscaling || targetWidth <= width)) { | 
					
						
							|  |  |  |       print("not allow upscaling but targetWidth > width"); | 
					
						
							| 
									
										
										
										
											2024-06-12 01:40:54 +08:00
										 |  |  |       onPixelsCopied?.call(); | 
					
						
							| 
									
										
										
										
											2024-05-27 09:27:30 +08:00
										 |  |  |       return null; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-10 19:50:48 -07:00
										 |  |  |   } | 
					
						
							|  |  |  |   if (targetHeight != null) { | 
					
						
							|  |  |  |     assert(allowUpscaling || targetHeight <= height); | 
					
						
							| 
									
										
										
										
											2024-05-27 09:27:30 +08:00
										 |  |  |     if (!(allowUpscaling || targetHeight <= height)) { | 
					
						
							|  |  |  |       print("not allow upscaling but targetHeight > height"); | 
					
						
							| 
									
										
										
										
											2024-06-12 01:40:54 +08:00
										 |  |  |       onPixelsCopied?.call(); | 
					
						
							| 
									
										
										
										
											2024-05-27 09:27:30 +08:00
										 |  |  |       return null; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-10 19:50:48 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-27 09:27:30 +08:00
										 |  |  |   final ui.ImmutableBuffer buffer; | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     buffer = await ui.ImmutableBuffer.fromUint8List(pixels); | 
					
						
							|  |  |  |     onPixelsCopied?.call(); | 
					
						
							|  |  |  |   } catch (e) { | 
					
						
							| 
									
										
										
										
											2024-06-12 01:40:54 +08:00
										 |  |  |     onPixelsCopied?.call(); | 
					
						
							| 
									
										
										
										
											2024-05-27 09:27:30 +08:00
										 |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   final ui.ImageDescriptor descriptor; | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     descriptor = ui.ImageDescriptor.raw( | 
					
						
							|  |  |  |       buffer, | 
					
						
							|  |  |  |       width: width, | 
					
						
							|  |  |  |       height: height, | 
					
						
							|  |  |  |       rowBytes: rowBytes, | 
					
						
							|  |  |  |       pixelFormat: format, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     if (!allowUpscaling) { | 
					
						
							|  |  |  |       if (targetWidth != null && targetWidth > descriptor.width) { | 
					
						
							|  |  |  |         targetWidth = descriptor.width; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (targetHeight != null && targetHeight > descriptor.height) { | 
					
						
							|  |  |  |         targetHeight = descriptor.height; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-09-10 19:50:48 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-05-27 09:27:30 +08:00
										 |  |  |   } catch (e) { | 
					
						
							|  |  |  |     print("ImageDescriptor.raw failed: $e"); | 
					
						
							|  |  |  |     buffer.dispose(); | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   final ui.Codec codec; | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     codec = await descriptor.instantiateCodec( | 
					
						
							|  |  |  |       targetWidth: targetWidth, | 
					
						
							|  |  |  |       targetHeight: targetHeight, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } catch (e) { | 
					
						
							|  |  |  |     print("instantiateCodec failed: $e"); | 
					
						
							|  |  |  |     buffer.dispose(); | 
					
						
							|  |  |  |     descriptor.dispose(); | 
					
						
							|  |  |  |     return null; | 
					
						
							| 
									
										
										
										
											2022-09-10 19:50:48 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-27 09:27:30 +08:00
										 |  |  |   final ui.FrameInfo frameInfo; | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     frameInfo = await codec.getNextFrame(); | 
					
						
							|  |  |  |   } catch (e) { | 
					
						
							|  |  |  |     print("getNextFrame failed: $e"); | 
					
						
							|  |  |  |     codec.dispose(); | 
					
						
							|  |  |  |     buffer.dispose(); | 
					
						
							|  |  |  |     descriptor.dispose(); | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-09-10 19:50:48 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   codec.dispose(); | 
					
						
							|  |  |  |   buffer.dispose(); | 
					
						
							|  |  |  |   descriptor.dispose(); | 
					
						
							|  |  |  |   return frameInfo.image; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-02-09 23:00:34 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | class ImagePainter extends CustomPainter { | 
					
						
							|  |  |  |   ImagePainter({ | 
					
						
							|  |  |  |     required this.image, | 
					
						
							|  |  |  |     required this.x, | 
					
						
							|  |  |  |     required this.y, | 
					
						
							|  |  |  |     required this.scale, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ui.Image? image; | 
					
						
							|  |  |  |   double x; | 
					
						
							|  |  |  |   double y; | 
					
						
							|  |  |  |   double scale; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void paint(Canvas canvas, Size size) { | 
					
						
							|  |  |  |     if (image == null) return; | 
					
						
							|  |  |  |     if (x.isNaN || y.isNaN) return; | 
					
						
							|  |  |  |     canvas.scale(scale, scale); | 
					
						
							|  |  |  |     // https://github.com/flutter/flutter/issues/76187#issuecomment-784628161
 | 
					
						
							|  |  |  |     // https://api.flutter-io.cn/flutter/dart-ui/FilterQuality.html
 | 
					
						
							|  |  |  |     var paint = Paint(); | 
					
						
							|  |  |  |     if ((scale - 1.0).abs() > 0.001) { | 
					
						
							|  |  |  |       paint.filterQuality = FilterQuality.medium; | 
					
						
							|  |  |  |       if (scale > 10.00000) { | 
					
						
							|  |  |  |         paint.filterQuality = FilterQuality.high; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-03-28 11:38:11 +08:00
										 |  |  |     // It's strange that if (scale < 0.5 && paint.filterQuality == FilterQuality.medium)
 | 
					
						
							|  |  |  |     // The canvas.drawImage will not work on web
 | 
					
						
							|  |  |  |     if (isWeb) { | 
					
						
							|  |  |  |       paint.filterQuality = FilterQuality.high; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-02-09 23:00:34 +09:00
										 |  |  |     canvas.drawImage( | 
					
						
							|  |  |  |         image!, Offset(x.toInt().toDouble(), y.toInt().toDouble()), paint); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   bool shouldRepaint(CustomPainter oldDelegate) { | 
					
						
							|  |  |  |     return oldDelegate != this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |