11

I am trying to implement a custom painter that can draw an image (scaled down version) on the canvas and the drawn image can be rotated and scaled.

I get to know that to scale the image I have to scale the canvas using scale method.

Now the questions is how to rotate the scaled image on its center (or any other point). The rotate method of canvas allow only to rotate on top left corner.

Here is my implementation that can be extended

4
  • take a look at this example Commented Jul 13, 2018 at 11:15
  • @RaoufRahiche I need rotation with canvas draw Commented Jul 13, 2018 at 11:56
  • were you able to workaround this? @NatwarSingh Commented Oct 15, 2018 at 17:25
  • haven't tried but I was able to solve my problem with Transform.rotate and Transform.translate widget. Commented Dec 21, 2018 at 11:54

3 Answers 3

21

Had the same problem, Solution was simply making your own rotation method in three lines

void rotate(Canvas canvas, double cx, double cy, double angle) {
  canvas.translate(cx, cy);
  canvas.rotate(angle);
  canvas.translate(-cx, -cy);
}

We thus first move the canvas towards the point you want to pivot around. We then rotate along the the topleft (default for Flutter) which in coordinate space is the pivot you want and then put the canvas back to the desired position, with the rotation applied. Method is very efficient, requiring only 4 additions for the translation and the rotation cost is identical to the original one.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks! This worked fine for me!. For those wondering, I was trying to rotate an element using Transform. The transformation I ended up applying was: Matrix4.identity()..translate(Constants.elementCardHalfSize)..rotateY(initialRotateX)..translate(-Constants.elementCardHalfSize)
what is cx and cy?
@Scorb You can call them x and y if you want, I called them cx and cy because I wanted my pivot to be in the center hence center x and center y
Instead of translating back to previous position, I would recommend save() before translate, and restore() when you are done.
@nobody what would be the benefit of that? Seems like a heavier operation
17

figure 1 figure 2

This can achieve by shifting the coordinate space as illustrated in figure 1. The translation is the difference in coordinates between C1 and C2, which are exactly as between A and B in figure 2. With some geometry formulas, we can calculate the desired translation and produce the rotated image as in the method below

ui.Image rotatedImage({ui.Image image, double angle}) {
  var pictureRecorder = ui.PictureRecorder();
  Canvas canvas = Canvas(pictureRecorder);

  final double r = sqrt(image.width * image.width + image.height * image.height) / 2;
  final alpha = atan(image.height / image.width);
  final beta = alpha + angle;
  final shiftY = r * sin(beta);
  final shiftX = r * cos(beta);
  final translateX = image.width / 2 - shiftX;
  final translateY = image.height / 2 - shiftY;
  canvas.translate(translateX, translateY);
  canvas.rotate(angle);
  canvas.drawImage(image, Offset.zero, Paint());

  return pictureRecorder.endRecording().toImage(image.width, image.height);
}

alpha, beta, angle are all in radian.

Here is the repo of the demo app

1 Comment

If you want to use real angels angle = (angle / 360) * 6.28; now you can pass 180 and it will rotate 180 degrees
3

If you don't want to rotate the image around the center of the image you can use this way. You won't have to care about what the offset of the canvas should be in relation to the image rotation, because the canvas is moved back to its original position after the image is drawn.

void rotate(Canvas c, Image image, Offset focalPoint, Size screenSize, double angle) {
  c.save();
  c.translate(screenSize.width/2, screenSize.height/2);
  c.rotate(angle);
  // To rotate around the center of the image, focal point is the
  // image width and height divided by 2
  c.drawImage(image, focalPoint*-1, Paint());
  c.translate(-screenSize.width/2, -screenSize.height/2);
  c.restore();
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.