颤动 - 如何使用drawImage方法在Canvas上绘制图像

时间:2018-04-11 20:48:44

标签: android canvas dart flutter

我正在尝试将图像文件绘制到画布中以在Flutter中构建我的小部件。

我确实关注canvas documentation,但没有成功。 O Image docs,他说:

  

要获取Image对象,请使用instantiateImageCodec。

我确实尝试使用instantiateImageCodec方法,但我得到的是Codec个实例,而不是Image

如何使用canvas.drawImage

获取ui.Image实例在画布上绘制的正确方法

以下是我的代码:

Future<ui.Codec> _loadImage(AssetBundleImageKey key) async {
  final ByteData data = await key.bundle.load(key.name);
   if (data == null)
  throw 'Unable to read data';
   return await ui.instantiateImageCodec(data.buffer.asUint8List());
}

final Paint paint = new Paint()
  ..color = Colors.yellow
  ..strokeWidth = 2.0
  ..strokeCap = StrokeCap.butt
  ..style = PaintingStyle.stroke;

var sunImage = new ExactAssetImage("res/images/grid_icon.png");

sunImage.obtainKey(new ImageConfiguration()).then((AssetBundleImageKey key){
  _loadImage(key).then((ui.Codec codec){
    print("frameCount: ${codec.frameCount.toString()}");
    codec.getNextFrame().then((info){
      print("image: ${info.image.toString()}");
      print("duration: ${info.duration.toString()}");
      canvas.drawImage(info.image, size.center(Offset.zero), paint);
    });
  });
});

3 个答案:

答案 0 :(得分:6)

ui.Codec有一个方法getNextFrame(),它返回一个Future<FrameInfo>(你应该有多少帧的逻辑,但如果你知道它总是正常的图片,你可以跳过它。 )FrameInfo具有image属性,这是您需要的图像。

编辑:查看帖子中的代码,不清楚你在做什么。这都是在CustomPainter.paint方法中定义的吗?如果是这样,您肯定会遇到问题,因为您只能在canvas来电期间使用paint;你不应该在函数之外保留对它的任何引用(包括任何异步调用)。

我建议您使用FutureBuilder,以便在添加图片后只在画​​布上绘图。

这看起来像这样:

Future<Image> _loadImage(AssetBundleImageKey key) async {
  final ByteData data = await key.bundle.load(key.name);
  if (data == null)
    throw 'Unable to read data';
  var codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
  // add additional checking for number of frames etc here
  var frame = await codec.getNextFrame();
  return frame.image;
}

new FutureBuilder<Image>(
  future: loadImage(....), // a Future<String> or null
  builder: (BuildContext context, AsyncSnapshot<Image> snapshot) {
    switch (snapshot.connectionState) {
      case ConnectionState.waiting: return new Text('Image loading...');
      default:
        if (snapshot.hasError)
          return new Text('Error: ${snapshot.error}');
        else
          // ImageCanvasDrawer would be a (most likely) statless widget
          // that actually makes the CustomPaint etc
          return new ImageCanvasDrawer(image: snapshot.data)
    }
  },
)

答案 1 :(得分:0)

class ImagePainter extends CustomPainter {
  List<ui.Image> images = new List<ui.Image>();
  ImagePainter(
      {Key key,
      @required this.noOfSlice,
      @required this.images,
      @required this.rotation,
      this.boxfit = BoxFit.contain})
      :
        // : path = new Path()
        //     ..addOval(new Rect.fromCircle(
        //       center: new Offset(75.0, 75.0),
        //       radius: 40.0,
        //     )),
        tickPaint = new Paint() {
    tickPaint.strokeWidth = 2.5;
  }
  final int noOfSlice;
  final tickPaint;
  final BoxFit boxfit;
  ui.ImageByteFormat img;
  ui.Rect rect, inputSubrect, outputSubrect;
  Size imageSize;
  FittedSizes sizes;
  double radius,
      rotation = 0.0,
      _x,
      _y,
      _angle,
      _radiun,
      _baseLength,
      _imageCircleradius,
      _incircleRadius,
      _imageOffset = 0.0,
      _imageSizeConst = 0.0;

  @override
  void paint(ui.Canvas canvas, ui.Size size) {
    print("image data:: $images");
    radius = size.width / 2;
    _angle = 360 / (noOfSlice * 2.0);
    _radiun = (_angle * pi) / 180;
    _baseLength = 2 * radius * sin(_radiun);
    _incircleRadius = (_baseLength / 2) * tan(_radiun);
    if (noOfSlice == 4) {
      _imageOffset = 30.0;
      _imageSizeConst = 30.0;
      _x = 8.60;
      _y = 4.10;
    } else if (noOfSlice == 6) {
      _imageOffset = 20.0;
      _x = 10.60;
      _y = 5.60;
    } else if (noOfSlice == 8) {
      _imageOffset = 40.0;
      _imageSizeConst = 30.0;
      _x = 12.90;
      _y = 6.60;
    }

    //print("circle radisu:: $_incircleRadius");

    canvas.save();
    canvas.translate(size.width / 2, size.height / 2);
    canvas.rotate(-rotation);
    int incr = 0;
    rect = ui.Offset((size.width / _x), size.width / _y) & new Size(0.0, 0.0);

    imageSize = new Size(size.width * 1.5, size.width * 1.5);
    sizes = applyBoxFit(
        boxfit,
        imageSize,
        new Size(size.width / 2 * .50 + _incircleRadius * .8,
            size.width / 2 * .50 + _incircleRadius * .8));
    inputSubrect =
        Alignment.center.inscribe(sizes.source, Offset.zero & imageSize);
    outputSubrect = Alignment.center.inscribe(sizes.destination, rect);
    if (images.length == noOfSlice && images.isNotEmpty)
      for (var i = 1; i <= noOfSlice * 2; ++i) {
        if (i % 2 != 0) {
          canvas.drawLine(
            new Offset(0.0, 0.0),
            new Offset(0.0, size.width / 2 - 4.2),
            tickPaint,
          );
        } else {
          canvas.save();
          canvas.translate(-0.0, -((size.width) / 2.2));
          ui.Image image = images[incr];
          if (image != null) {
            canvas.drawImageRect(
                image, inputSubrect, outputSubrect, new Paint());
          }

          canvas.restore();
          incr++;
        }
        canvas.rotate(2 * pi / (noOfSlice * 2.0));
      }
    canvas.restore();
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

答案 2 :(得分:0)

这个简单的实用程序方法根据图像资源的路径返回Future<UI.Image>

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

import 'package:flutter/services.dart';

Future<UI.Image> loadUiImage(String imageAssetPath) async {
  final ByteData data = await rootBundle.load(imageAssetPath);
  final Completer<UI.Image> completer = Completer();
  UI.decodeImageFromList(Uint8List.view(data.buffer), (UI.Image img) {
    return completer.complete(img);
  });
  return completer.future;
}