【问题标题】:Poor image quality in saving image from a screenshot ( flutter )从屏幕截图中保存图像的图像质量差(颤动)
【发布时间】:2021-09-01 18:55:53
【问题描述】:

我有一个屏幕,可以让用户导入图像并拖放它们以重新定位、调整大小和旋转它们。每个对象都是一个小部件,所有这些对象的父级是一个Stack 小部件。我使用matrix_gesture_detector 0.1.0 库来处理转换。我使用screenshot 库将这个Stack 小部件保存为图像。但是,如果pixelRatio 低,我会得到像素化图像,但如果它高,图像大小会增加很多(大约 25MB)。我想知道如何在不增加图像大小的情况下提高图像质量。如果还有其他方法,我也想了解它们。我的图片可以是AssetImageNetWorkImage

我的截图代码:

....
....

Screenshot(
   controller: screenshotController,
   child: Container(
      key: Key("canvas"),
      decoration: BoxDecoration(
        color: currentColor,
        borderRadius: BorderRadius.all(
          Radius.circular(dp2),
        ),
      ),
      child: Stack(
        alignment: Alignment.center,
        children: _selectedWidgets, // here selected widgets is a lis of widgets
      ),
    ),
  )

//save image method
saveImage() async {
    

    String fileName = DateTime.now().toIso8601String();
    var path = '$directoryPath/$fileName.png';

    screenshotController
        .capture(
      pixelRatio: 5,
      path: path,
      delay: Duration(milliseconds: 10),
    ).then((File image) async {
      image = await compressImage(image);
     
      ........
      .........
      
    }).catchError((onError, stack) {
      log("stack = $stack");
    });
  }

// compressing function compress based on the size of the image
Future<File> compressImage(File image) async {

  int fileSize = image.lengthSync();

  int percentage = getPercentage(fileSize);

  return FlutterNativeImage.compressImage(image.path, quality: 25, percentage: percentage);
}

int getPercentage(int fileSize) {
  if(fileSize <= 1073741824)
    return 75;
  if(fileSize <= 2147483648)
    return 60;
  if(fileSize <= 3221225472)
    return 50;
  if(fileSize <= 4294967296)
    return 40;
  if(fileSize <= 5368709120)
    return 25;

  return 25;
}


【问题讨论】:

    标签: image flutter screenshot


    【解决方案1】:

    所以你的应用程序的整个目标是创建一个拼贴图像并将它们保存到磁盘? (如果你先解释一下可能会有用)

    我建议要么限制“画布”区域的大小,要么在保存之前调整它的大小。您还可以尝试各种压缩算法,例如 JPEG 及其参数。不确定FlutterNativeImage 的质量。也许你可以看看其他一些包?我看到flutter_image_compress 包有很多赞。

    【讨论】:

      【解决方案2】:

      你可以在这里找到官方的 toImage 示例。但它似乎在按钮点击和 toImage 调用之间没有延迟。

      官方仓库有问题: https://github.com/flutter/flutter/issues/22308

      造成这种情况的原因:您的点击初始化了按钮的动画,并且 RenderObject.markNeedsPaint 被递归调用,包括父级,因此您应该等待 debugNeedsPaint 再次为 false。在这种情况下,toImage 函数只会抛出断言错误:

      Future<ui.Image> toImage({ double pixelRatio = 1.0 }) {
          assert(!debugNeedsPaint);
          final OffsetLayer offsetLayer = layer;
          return offsetLayer.toImage(Offset.zero & size, pixelRatio: pixelRatio);
        }
      

      https://github.com/flutter/flutter/blob/f0553ba58e6455aa63fafcdca16100b81ff5c3ce/packages/flutter/lib/src/rendering/proxy_box.dart#L2857

      bool get debugNeedsPaint {
          bool result;
          assert(() {
            result = _needsPaint;
            return true;
          }());
          return result;
        }
      

      https://github.com/flutter/flutter/blob/0ca5e71f281cd549f1b5284e339523ad93544c60/packages/flutter/lib/src/rendering/object.dart#L2011

      实际上assert函数只在开发中使用,所以你可以看到你不会在生产中遇到错误的麻烦。但我不知道你会遇到什么样的麻烦,可能不会)。

      下一个代码不是很好,但它可以工作:

      class _MyHomePageState extends State<MyHomePage> {
        GlobalKey globalKey = GlobalKey();
      
        Future<Uint8List> _capturePng() async {
          RenderRepaintBoundary boundary = globalKey.currentContext.findRenderObject();
      
          if (boundary.debugNeedsPaint) {
            print("Waiting for boundary to be painted.");
            await Future.delayed(const Duration(milliseconds: 20));
            return _capturePng();
          }
      
          var image = await boundary.toImage();
          var byteData = await image.toByteData(format: ImageByteFormat.png);
          return byteData.buffer.asUint8List();
        }
      
        void _printPngBytes() async {
          var pngBytes = await _capturePng();
          var bs64 = base64Encode(pngBytes);
          print(pngBytes);
          print(bs64);
        }
      
        @override
        Widget build(BuildContext context) {
          return RepaintBoundary(
            key: globalKey,
            child: Center(
              child: FlatButton(
                color: Color.fromARGB(255, 255, 255, 255),
                child: Text('Capture Png', textDirection: TextDirection.ltr),
                onPressed: _printPngBytes
              ),
            ),
          );
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2022-01-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-19
        • 1970-01-01
        相关资源
        最近更新 更多