使用自定义 Painter 在 Flutter 中遮盖两个图像

2023-11-29

你好,谁能告诉我为什么这个掩蔽尝试的背景是黑色的。这一定很接近,但我就是无法消除背景。我看到其他人提到过saveLayer(rect, paint)是这里的关键,因为它将整个画布矩形推入遮罩操作。这个问题(无屏蔽操作)和this one(没有实际答案)类似,但对我来说没有用。

main.dart

import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  ui.Image mask;
  ui.Image image;

  @override
  void initState() {
    super.initState();
    load('images/squircle.png').then((i) {
      setState(() {
        mask = i;
      });
    });
    load('images/noodlejpg.jpg').then((i) {
      setState(() {
        image = i;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(backgroundColor: Colors.blue, title: Text('I am a title')),
      body: SafeArea(
        child: SizedBox(
          width: 200.0,
          height: 200.0,
          child: CustomPaint(painter: OverlayPainter(mask, image)),
        ),
      ),
    );
  }

  Future<ui.Image> load(String asset) async {
    ByteData data = await rootBundle.load(asset);
    ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
    ui.FrameInfo fi = await codec.getNextFrame();
    return fi.image;
  }
}

class OverlayPainter extends CustomPainter {
  ui.Image mask;
  ui.Image image;

  OverlayPainter(this.mask, this.image);

  @override
  void paint(Canvas canvas, Size size) {
    var paint = new Paint();
    paint.isAntiAlias = true;

    if (image != null) {
      var rect = Rect.fromLTRB(0, 0, 200, 200);
      Size outputSize = rect.size;
      Size inputSize = Size(image.width.toDouble(), image.height.toDouble());
      final FittedSizes fittedSizes =
          applyBoxFit(BoxFit.cover, inputSize, outputSize);
      final Size sourceSize = fittedSizes.source;

      canvas.save();
      final Rect sourceRect = Alignment.center.inscribe(
        sourceSize,
        Offset.zero & inputSize,
      );
      canvas.drawImageRect(image, sourceRect, rect, paint);
      canvas.restore();
    }

    if (mask != null) {
      var rect = Rect.fromLTRB(0, 0, 200, 200);
      Size outputSize = rect.size;
      Size inputSize = Size(mask.width.toDouble(), mask.height.toDouble());
      final FittedSizes fittedSizes =
          applyBoxFit(BoxFit.cover, inputSize, outputSize);
      final Size sourceSize = fittedSizes.source;

      canvas.saveLayer(rect, Paint()..blendMode = BlendMode.dstIn);

      final Rect sourceRect = Alignment.center.inscribe(
        sourceSize,
        Offset.zero & inputSize,
      );
      canvas.drawImageRect(mask, sourceRect, rect, paint);
      canvas.restore();
    }
  }

  @override
  bool shouldRepaint(OverlayPainter oldDelegate) {
    return mask != oldDelegate.mask || image != oldDelegate.image;
  }
}

面条jpg.jpg

enter image description here

松鼠.jpg

enter image description here

result

enter image description here


关键是什么时候打电话saveLayer以及何时致电restore.

From here:

当使用Canvas.saveLayer和Canvas.restore时,当调用 Canvas.restore 时,将应用给予 Canvas.saveLayer 的 Paint 的混合模式。每次调用 Canvas.saveLayer 都会引入一个新层,在该层上绘制形状和图像;当调用 Canvas.restore 时,该图层将被合成到父图层上,源是最近绘制的形状和图像,目标是父图层。 (对于第一个 Canvas.saveLayer 调用,父层是画布本身。)

工作代码

  @override
  void paint(Canvas canvas, Size size) {
    if (image != null && mask != null) {
      var rect = Rect.fromLTRB(0, 0, 200, 200);
      Size outputSize = rect.size;
      Paint paint = new Paint();

      //Mask
      Size maskInputSize = Size(mask.width.toDouble(), mask.height.toDouble());
      final FittedSizes maskFittedSizes =
          applyBoxFit(BoxFit.cover, maskInputSize, outputSize);
      final Size maskSourceSize = maskFittedSizes.source;

      final Rect maskSourceRect = Alignment.center
          .inscribe(maskSourceSize, Offset.zero & maskInputSize);

      canvas.saveLayer(rect, paint);
      canvas.drawImageRect(mask, maskSourceRect, rect, paint);

      //Image
      Size inputSize = Size(image.width.toDouble(), image.height.toDouble());
      final FittedSizes fittedSizes =
          applyBoxFit(BoxFit.cover, inputSize, outputSize);
      final Size sourceSize = fittedSizes.source;
      final Rect sourceRect =
          Alignment.center.inscribe(sourceSize, Offset.zero & inputSize);

      canvas.drawImageRect(
          image, sourceRect, rect, paint..blendMode = BlendMode.srcIn);
      canvas.restore();
    }
  }

Result:

enter image description here

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用自定义 Painter 在 Flutter 中遮盖两个图像 的相关文章

随机推荐