3

I am currently trying to draw an image on a flutter canvas and then draw rectangles over the image to cover specific data. I am running into an issue where the the rectangles are drawing fine but the image is not being drawn behind. It appears as if it is not being drawn at all.

I am loading the image into a Dart UI Image object from bytes. Once the image is loaded I create a PictureRecorder and pass it into a Canvas object. I then attempt to draw the image object first, followed by the rectangles over top. The complete code is below:

import 'dart:async';
import 'dart:typed_data';

import 'dart:ui' as ui;

import 'package:common/ocr/scraped_card_field.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class ObfuscatedCardRenderer {
  ui.Image _backgroundImage;

  ///
  ///
  ///
  Future<ByteData> renderImage(
      Uint8List imageBytes, List<ScrapedCardField> fields) async {
    await _loadImageBackground(imageBytes);
    var obfuscatedImage = await _obfuscateImage(fields);

    var byteD =
        await obfuscatedImage.toByteData(format: ui.ImageByteFormat.png);

    return byteD;
  }

  ///
  ///
  ///
  Future<ui.Image> _obfuscateImage(List<ScrapedCardField> fields) async {
    ui.PictureRecorder recorder = ui.PictureRecorder();
    ui.Canvas c = ui.Canvas(recorder);

    var paint = ui.Paint();

    c.drawImage(_backgroundImage, ui.Offset(0.0, 0.0), new Paint());
    for (ScrapedCardField field in fields) {
      paint.color = Colors.white;
      c.drawRect(field.boundingBox, paint);
    }

    var picture = recorder.endRecording();
    return picture.toImage(_backgroundImage.width, _backgroundImage.height);
  }

  ///
  ///
  ///
  Future<void> _loadImageBackground(Uint8List imageBytes) async {
    if (imageBytes != null) {
      _backgroundImage = await _getImageFromBytes(imageBytes);
    } else {
      return null;
    }
  }

  ///
  ///
  ///
  Future<ui.Image> _getImageFromBytes(Uint8List imageBytes) async {
    var completer = Completer<ui.Image>();
    ui.decodeImageFromList(imageBytes, (image) {
      completer.complete(image);
    });

    return completer.future;
  }
}

Here is the result:

enter image description here

As you can see the rectangles are drawn perfectly fine, however, the image in the background is not visible. Is there something else I should be doing to draw the image, or is there another way to do this?

Thanks!

magee05
  • 459
  • 1
  • 4
  • 14
  • add `print(_backgroundImage)` - what do you see in the logs? – pskink Mar 05 '20 at 14:24
  • Looks like it prints the dimensions of the image: [1019×713] – magee05 Mar 05 '20 at 14:29
  • ok and the image is not in any way transparent? – pskink Mar 05 '20 at 14:30
  • Nope. As a test to see if the image was okay I skipped the canvas and just wrote the bytes to a file and the image came out fine. It only comes out bad when I draw it to the canvas. – magee05 Mar 05 '20 at 14:32
  • i would doble check the code with `CustomPainter` - a sample is here: https://api.flutter.dev/flutter/rendering/CustomPainter-class.html#rendering.CustomPainter.1 – pskink Mar 05 '20 at 14:35
  • Getting the same result after switching the code to use CustomPainter. Here is the code: https://gist.github.com/jdmagee05/2a6109f47ffc2324dff8c5cb85b0df4f – magee05 Mar 05 '20 at 15:10
  • run this https://gist.github.com/pskink/481825d2e4504479e1c585fdbcbc8d0a - pass some "assets/foo.jpg" to `BlurView` constructor – pskink Mar 05 '20 at 15:11
  • That seems like it is able to display my image! I notice you don't use draw image in that. What exactly draws the image on the canvas? – magee05 Mar 05 '20 at 15:43
  • It's a imageShader, ok now remove everything from my paint method and call just canvas.drawImage – pskink Mar 05 '20 at 15:52
  • That works. I'm not trying to display the image in the app in this case though. I need to draw rects over the image and then save and upload to server. I'll try this out of a widget to see if I get the same result. – magee05 Mar 05 '20 at 16:01
  • `void foo(String path, String out) async { var bytes = await rootBundle.load(path); var image = await decodeImageFromList(bytes.buffer.asUint8List()); var recorder = ui.PictureRecorder(); var canvas = Canvas(recorder); canvas.drawImage(image, Offset.zero, Paint()); canvas.drawRect(Rect.fromLTWH(10, 10, 200, 100), Paint()..color = Colors.red); var picture = recorder.endRecording(); var outputImage = await picture.toImage(image.width, image.height); var data = await outputImage.toByteData(format: ui.ImageByteFormat.png); await File(out).writeAsBytes(data.buffer.asInt8List()); }` – pskink Mar 05 '20 at 16:38
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/209104/discussion-between-magee05-and-pskink). – magee05 Mar 05 '20 at 17:52

1 Answers1

1

I am posting below a working code from my app which loads an image and show it in the flutter Canvas. Hope it helps the future readers.

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

class ImageInsideCanvasPage extends StatefulWidget {
  @override
  _ImageInsideCanvasPageState createState() => _ImageInsideCanvasPageState();
}

class _ImageInsideCanvasPageState extends State<ImageInsideCanvasPage> {
  ui.Image? image;

  @override
  void initState() {
    // Add your own asset image link
    _load('assets/img.png');
    super.initState();
  }

  void _load(String path) async {
    var bytes = await rootBundle.load(path);
    image = await decodeImageFromList(bytes.buffer.asUint8List());
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          child: CustomPaint(
            painter: ImageInsideCanvas(image: image),
            child: SizedBox.expand(),
          ),
        ),
      ),
    );
  }
}

class ImageInsideCanvas extends CustomPainter {
  ImageInsideCanvas({required this.image});
  ui.Image? image;

  @override
  void paint(Canvas canvas, Size size) async {
    Paint greenBrush = Paint()..color = Colors.greenAccent;
    canvas.save();
    canvas.drawImage(image!, Offset(0, 0), greenBrush);
    canvas.restore();
  }

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

Zihan
  • 668
  • 8
  • 19