web-dev-qa-db-ja.com

Flutter-DrawImageメソッドを使用してCanvasに画像を描画する方法

Flutterでウィジェットを作成するために、キャンバスに画像ファイルを描画しようとしています。

私は canvas documentation に従いましたが、成功しませんでした。 O Image docs 、次のように言います:

Imageオブジェクトを取得するには、instantiateImageCodecを使用します。

私はinstantiateImageCodecメソッドを使用しようとしましたが、 Image ではなくCodecインスタンスを取得するだけです

どのように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);
    });
  });
});
8

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)
    }
  },
)
6
rmtmckenzie

この単純なユーティリティメソッドは、画像アセットのパスを指定すると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;
}
2
bonnyz
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;
  }
}
0
satish