颤动:仅显示图像的特殊部分

时间:2019-01-24 12:32:54

标签: dart flutter

这是我显示图片的方式:

return Scaffold(
   body: Center(
     child: Image.asset('man_face.jpg'),
   ),
);

结果是:https://imgur.com/a/CPrQgvS

我只想显示图片的特殊部分。例如,具有x: 250y: 360以及width: 200height: 150的矩形。

应该是这样的:https://imgur.com/a/p41y3nx

我该怎么做?

3 个答案:

答案 0 :(得分:2)

您可能想研究这个库。 brendan-duncan/image。一个很好的工具,可以处理抖动图像。

答案 1 :(得分:1)

这里的id是我想出来的代码

它接受一个图像 url 和一个 rect 并只显示图像的 rect 部分

import 'dart:async';

import 'package:flutter/material.dart';
import 'dart:ui' as ui;

class PartImagePainter extends StatefulWidget {
  String imageUrl;
  Rect rect;

  PartImagePainter(this.imageUrl, this.rect);

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

class _PartImagePainterState extends State<PartImagePainter> {
  Future<ui.Image> getImage(String path) async {
    Completer<ImageInfo> completer = Completer();
    var img = new NetworkImage(path);
    img
        .resolve(ImageConfiguration())
        .addListener(ImageStreamListener((ImageInfo info, bool _) {
      completer.complete(info);
    }));
    ImageInfo imageInfo = await completer.future;
    return imageInfo.image;
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: getImage(widget.imageUrl),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            // If the Future is complete, display the preview.
            return paintImage(snapshot.data);
          } else {
            // Otherwise, display a loading indicator.
            return Center(child: CircularProgressIndicator());
          }
        });
  }

  paintImage(image) {
    return CustomPaint(
      painter: ImagePainter(image, widget.rect),
      child: SizedBox(
        width: MediaQuery.of(context).size.width,
        height: widget.rect.height,
      ),
    );
  }
}

class ImagePainter extends CustomPainter {
  ui.Image resImage;

  Rect rectCrop;

  ImagePainter(this.resImage, this.rectCrop);

  @override
  void paint(Canvas canvas, Size size) {
    if (resImage == null) {
      return;
    }
    final Rect rect = Offset.zero & size;
    final Size imageSize =
        Size(resImage.width.toDouble(), resImage.height.toDouble());
    FittedSizes sizes = applyBoxFit(BoxFit.fitWidth, imageSize, size);

    Rect inputSubRect = rectCrop;
    final Rect outputSubRect =
        Alignment.center.inscribe(sizes.destination, rect);

    final paint = Paint()
      ..color = Colors.white
      ..style = PaintingStyle.fill
      ..strokeWidth = 4;
    canvas.drawRect(rect, paint);

    canvas.drawImageRect(resImage, inputSubRect, outputSubRect, Paint());
  }

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

答案 2 :(得分:-1)

Flutter 官方文档建议的一种方法是:

<块引用>

要显示图像的子部分,请考虑使用 CustomPainter 和 Canvas.drawImageRect。

参考:https://api.flutter.dev/flutter/painting/DecorationImage/alignment.html

这是我的完整代码。使用 PartImage 显示您想要的内容。

class PartImage extends StatefulWidget {
  const PartImage({
    Key key,
    @required this.imageProvider,
    @required this.transform,
  })  : assert(imageProvider != null),
        super(key: key);

  final ImageProvider imageProvider;
  final Matrix4 transform;

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

class _PartImageState extends State<PartImage> {
  ImageStream _imageStream;
  ImageInfo _imageInfo;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _getImage();
  }

  @override
  void didUpdateWidget(PartImage oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.imageProvider != oldWidget.imageProvider) _getImage();
  }

  void _getImage() {
    final oldImageStream = _imageStream;
    _imageStream = widget.imageProvider.resolve(createLocalImageConfiguration(context));
    if (_imageStream.key != oldImageStream?.key) {
      final listener = ImageStreamListener(_updateImage);
      oldImageStream?.removeListener(listener);
      _imageStream.addListener(listener);
    }
  }

  void _updateImage(ImageInfo imageInfo, bool synchronousCall) {
    setState(() {
      _imageInfo = imageInfo;
    });
  }

  @override
  void dispose() {
    _imageStream.removeListener(ImageStreamListener(_updateImage));
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RawPartImage(
      image: _imageInfo?.image, // this is a dart:ui Image object
      scale: _imageInfo?.scale ?? 1.0,
      transform: widget.transform,
    );
  }
}

/// ref: [RawImage]
class RawPartImage extends StatelessWidget {
  final ui.Image image;
  final double scale;
  final Matrix4 transform;

  const RawPartImage({Key key, this.image, this.scale, this.transform}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: _RawPartImagePainter(
        image: image,
        scale: scale,
        transform: transform,
      ),
    );
  }
}

class _RawPartImagePainter extends CustomPainter {
  final ui.Image image;
  final double scale;
  final Matrix4 transform;

  final painter = Paint();

  _RawPartImagePainter({this.image, this.scale, this.transform});

  @override
  void paint(Canvas canvas, Size size) {
    if (image == null) {
      return;
    }

    final transformInv = Matrix4.inverted(transform);

    final dst = Offset.zero & size;
    final src = Rect.fromPoints(
      transformOffset(transformInv, dst.topLeft),
      transformOffset(transformInv, dst.bottomRight),
    );
    // print('src=$src dst=$dst');

    canvas.drawImageRect(image, src, dst, painter);
  }

  @override
  bool shouldRepaint(covariant _RawPartImagePainter oldDelegate) {
    return oldDelegate.image != image || //
        oldDelegate.scale != scale ||
        oldDelegate.transform != transform;
  }
}

Offset transformOffset(Matrix4 transform, Offset offset) {
  Vector4 vecOut = transform * Vector4(offset.dx, offset.dy, 0, 1);
  return Offset(vecOut.x, vecOut.y);
}


顺便说一下,如果您有兴趣了解 drawImageRect 后面发生的事情:

  1. 进行搜索https://github.com/flutter/engine/search?q=drawImageRect
  2. 这似乎是 drawImageRect(即 _drawImageRect 实际调用的 C++ 代码:https://github.com/flutter/engine/blob/6bc70e4a114ff4c01b60c77bae754bace5683f6d/lib/ui/painting/canvas.cc#L330
  3. 它调用 canvas_->drawImageRect。什么是canvas_?从头文件中我们看到它的类型为 SkCanvas* canvas_;
  4. 然后我们进入 Skia 的世界(不再是 Flutter 或 Dart)。 https://skia.org/user/api/skcanvas_overview 了解 SkCanvas。和 https://api.skia.org/classSkCanvas.html#a680ab85c3c7b5eab23b853b97f914334 用于实际的 SkCanvas.drawImageRect 文档。