flutter 使用ImageShader抖动着色器遮罩

j0pj023g  于 2023-03-04  发布在  Flutter
关注(0)|答案(2)|浏览(265)

我正在尝试获取一个使用Image作为Shader的ShaderMask。我正在从内存中阅读图像,因此我的图像是File。如何使用内存中的图像创建ImageShader?

File imageFile;
Image image = Image.file(imageFile)

ShaderMask(
   shaderCallback: (bounds) {
      Float64List matrix4 = new Matrix4.identity().storage; // <--- DO I NEED THIS OR THE BOUNDS?
      return ImageShader(image, TileMode.mirror, TileMode.mirror, matrix4);
   },
   child: child
)

ImageShader有一个错误,因为image的类型不对(我需要ui.image,我不知道如何创建)。

如何从File映像创建ImageShader

PS:matrix4是正确的还是我应该以某种方式使用边界?

u4dcyp6a

u4dcyp6a1#

需要知道的重要一点是ImageShader使用的是dart:ui包中的Image,而不是Image小部件的示例,没有直接的操作或构造函数可用于从网络位置、文件或资产创建ui.Image的示例,因此需要一些代码来完成。
在查阅了许多资料并深入研究了Image小部件加载原始图像的代码之后,我想到的最好的通用解决方案是使用ImageProvider作为源代码。抽象的ImageProviderNetworkImageFileImageExactAssetImageMemoryImage这样的实现,可以从任何资源加载图像。
首先,使用ImageProvider.resolve方法得到一个ImageStreamresolve方法接受一个ImageConfiguration类型的参数,该参数应该用代码位置上可用的尽可能多的信息填充。在大多数情况下,可以使用全局createLocalImageConfiguration函数。但请注意,当您在StatefulWidget的initState方法中创建着色器时,这将不起作用。
在解析的ImageStream上,可以附加一个ImageStreamListener,它将ImageListener回调作为第一个参数。加载图像后,将使用ImageInfo调用回调,ImageInfoimage属性上提供请求的图像。
您可以使用两种平铺模式构建ImageShader,如TileMode.clamp和一个简单的单位矩阵,您可以手动创建或使用Matrix4类提供的单位矩阵。如果您需要图像着色器小于所提供图像的大小,您可以将提供程序 Package 在ResizeProvider中,并指定所需的宽度和高度。
下面我实现了一个ImageMask小部件作为参考,它可以用来屏蔽任何类型的小部件。

class ImageMask extends StatefulWidget {
  final ImageProvider image;
  final double width;
  final double height;
  final Widget child;

  const ImageMask({@required this.image, this.width, this.height, @required this.child});

  @override
  _ImageMaskState createState() => _ImageMaskState();
}

class _ImageMaskState extends State<ImageMask> {
  Future<Shader> _shader;

  @override
  void initState() {
    super.initState();
    _shader = _loadShader(context);
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _shader,
      builder: (_, AsyncSnapshot<Shader> snapshot) {
        return snapshot.connectionState != ConnectionState.done || snapshot.hasError
            ? SizedBox(width: widget.width, height: widget.height)
            : ShaderMask(
                blendMode: BlendMode.dstATop,
                shaderCallback: (bounds) => snapshot.data,
                child: widget.child,
              );
      },
    );
  }

  Future<Shader> _loadShader(BuildContext context) async {
    final completer = Completer<ImageInfo>();

    // use the ResizeImage provider to resolve the image in the required size
    ResizeImage(widget.image, width: widget.width.toInt(), height: widget.height.toInt())
        .resolve(ImageConfiguration(size: Size(widget.width, widget.height)))
        .addListener(ImageStreamListener((info, _) => completer.complete(info)));

    final info = await completer.future;
    return ImageShader(
      info.image,
      TileMode.clamp,
      TileMode.clamp,
      Float64List.fromList(Matrix4.identity().storage),
    );
  }
}
6bc51xsx

6bc51xsx2#

这是一个较老的问题,但如果有人仍然在寻找答案,那么有一个简单的变通方案,这是使用第三方包widget_mask

WidgetMask(
  blendMode: BlendMode.difference,
  mask: Center(
    child: Text(
      'Negative',
      style: TextStyle(
        fontSize: 50,
        color: Colors.white,
      ),
    ),
  ),
  child: Image.asset('images/my_image.jpg'),
);

相关问题