Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas drawImage scale height and width in CustomPainter

I am developing an application in Flutter where I am using CustomPainter to draw an image which the user picks from gallery/camera. In addition to this the use can draw lines as well as change the stroke value, opacity colour and colour on it its own. For this I have created 2 classes DrawEditor and DrawingPainter the code for those two classes can be found below. Once the user picks an image the image is passed to the DrawingPainter class where paint() is called and I draw my lines and image. The issue is in _paintBackgroundImage() in this method I draw the image by using canvas.drawImage(paintedImage, Offset.zero, Paint()); which does not scale the image.

Earlier I tried a different approach instead of drawing the image with canvas.drawImage(paintedImage, Offset.zero, Paint()) I used canvas.drawImageRect(paintedImage, inputSubRect, outputSubRect, Paint()); as can be seen below. However with this approach the draw picture Is pixelated so I prefer canvas.drawImage(paintedImage, Offset.zero, Paint()) as this does not damage the picture.

Any help with scaling the image will be greatly appreciated.

  //Example 1 : Code with canvas.drawImageRect but image pixelated
   final UI.Rect rect = UI.Offset.zero & _canvasSize;
       final Size imageSize =Size(paintedImage.width.toDouble(),      paintedImage.height.toDouble());
    FittedSizes sizes = applyBoxFit(BoxFit.contain, imageSize, _canvasSize);
    final Rect inputSubRect =
    Alignment.center.inscribe(sizes.source, Offset.zero & imageSize);
final Rect outputSubRect =
    Alignment.center.inscribe(sizes.destination, rect);

canvas.drawImageRect(paintedImage, inputSubRect, outputSubRect, Paint());

   //Example 2 : Code with canvas.drawImageRect but image pixelated
   canvas.drawRect(Rect.fromPoints(blurStartOffset, blurIndicatorOffset),
      blurPaintSettings)

class DrawingPainter extends CustomPainter {
   static int blurColor = 0xFFB3E5FC;
   UI.Image paintedImage;
   List<DrawingPoints> pointsList;
   List<DrawingPoints> blurPointsList;
   List<Offset> offsetPoints = List();
   Size _canvasSize;
   Offset blurIndicatorOffset;
   Offset blurStartOffset;
   bool isBlur;
   List<BlurIndicatorOffsetWrapper> wrapperList = new List();

   /// To blur an image we need a [MaskFilter]
   Paint blurPaintSettings = new Paint()
      ..style = PaintingStyle.fill
      ..color = Color(blurColor)
      ..maskFilter = MaskFilter.blur(BlurStyle.normal, 3.0);

   DrawingPainter(
      {this.pointsList,
      this.paintedImage,
      this.blurPointsList,
      this.blurIndicatorOffset,
      this.blurStartOffset}) {
      isBlur = blurIndicatorOffset != null;
 }

 @override
 void paint(Canvas canvas, Size size) {
      _canvasSize = size;
      _paintBackgroundImage(canvas);
      _drawPoints(canvas);
      _drawBlurIndicator(canvas);
 }

   /// Paints the image onto the canvas
   void _paintBackgroundImage(Canvas canvas) {
      if (paintedImage == null) {
      return;
     }
     final UI.Rect rect = UI.Offset.zero & _canvasSize;
     final Size imageSize =
    Size(paintedImage.width.toDouble(), paintedImage.height.toDouble());
     FittedSizes sizes = applyBoxFit(BoxFit.contain, imageSize, _canvasSize);
     final Rect inputSubRect =
    Alignment.center.inscribe(sizes.source, Offset.zero & imageSize);
     final Rect outputSubRect =
    Alignment.center.inscribe(sizes.destination, rect);

     canvas.drawImageRect(paintedImage, inputSubRect, outputSubRect, Paint());

    }

    /// Paints the lines onto the canvas
    void _drawPoints(Canvas canvas) {
      for (int i = 0; i < pointsList.length - 1; i++) {
      if (pointsList[i] != null && pointsList[i + 1] != null) {
      canvas.drawLine(pointsList[i].points, pointsList[i + 1].points,
        pointsList[i].paint);
    }
   }
  }

    /// Paints the blur indicator onto the canvas
    void _drawBlurIndicator(Canvas canvas) {
       if (blurStartOffset != null && blurIndicatorOffset != null) {
        canvas.drawRect(Rect.fromPoints(blurStartOffset,  blurIndicatorOffset),
      blurPaintSettings);
    }
  }

  void setBlurIndicator(Offset localOffset) {
      blurIndicatorOffset = localOffset;
   }

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

  Future<Uint8List> save() async {
     //Create canvas
     // Set PictureRecorder on the canvas and start recording
     UI.PictureRecorder recorder = UI.PictureRecorder();
     Canvas canvas = Canvas(recorder);

     //Draw image on new canvas
     if (paintedImage != null) {
     final Size imageSize = Size(paintedImage.width.toDouble(), paintedImage.height.toDouble());

      //Here image is the problem
      canvas.drawImage(paintedImage, Offset.zero, Paint());
   }

   //Draw points on new canvas
      for (int i = 0; i < pointsList.length - 1; i++) {
      if (pointsList[i] != null && pointsList[i + 1] != null) {
      canvas.drawLine(
             pointsList[i].points,
             pointsList[i + 1].points,
             pointsList[i].paint,
      );
     }
   }

//End recording
final resultImage = await recorder.endRecording().toImage(
      _canvasSize.width.floor(),
      _canvasSize.height.floor(),
    );

final imageBytes =
    await resultImage.toByteData(format: UI.ImageByteFormat.png);

return imageBytes.buffer.asUint8List();
 }


 }

class DrawingPoints {
     Paint paint;
     Offset points;

     DrawingPoints({this.points, this.paint});
}

enum SelectedMode { StrokeWidth, Opacity, Color, Blur }
like image 236
Boris Avatar asked Jun 23 '19 08:06

Boris


People also ask

What is canvas flutter?

Like an artist's canvas is a physical surface to draw on, a Canvas in Flutter is a virtual surface for drawing. But unlike a regular art canvas, you can't paint on the Flutter canvas with physical brushes. Flutter Canvas uses a two-point (x and y) coordinate system to determine the position of a point on the screen.

How do you draw a picture on canvas flutter?

Finally, you can draw the image with the CustomPainter. This step is probably the easiest step because the CustomPainter provides a method to draw images. You can then call this method with the image, and this will draw the image on the canvas! This way, you can draw your images with the CustomPainter.


1 Answers

I had a very similar requirement and the comment about using paintImage was exactly what I was looking for, so I figured I'd share what I ended up with.

I needed to scale down an image and draw overlays on top of that image. image is my original (unscaled) Image object.

var recorder = ui.PictureRecorder();
var imageCanvas = new Canvas(recorder);
var painter = _MarkupPainter(_overlays);

//Paint the image into a rectangle that matches the requested width/height.
//This will handle rescaling the image into the rectangle so that it will not be clipped.
paintImage(
  canvas: imageCanvas, 
  rect: Rect.fromLTWH(0, 0, scaledWidth, scaledHeight),
  image: image,
  fit: BoxFit.scaleDown,
  repeat: ImageRepeat.noRepeat,
  scale: 1.0,
  alignment: Alignment.center,
  flipHorizontally: false,
  filterQuality: FilterQuality.high
  );

//Add the markup overlays.
painter.paint(imageCanvas, Size(scaledWidth, scaledHeight));

var picture = recorder.endRecording();

return picture.toImage(scaledWidth.toInt(), scaledHeight.toInt());
like image 160
Jason Tyler Avatar answered Oct 02 '22 10:10

Jason Tyler