Skip to content Skip to sidebar Skip to footer

Pycairo : How To Resize & Rotate An Image By Its Center To A Final Canvas

Using PyCairo, I want to be able to have a method that can put, resize & rotate a given ImageSurface on a context, but rotating by the center of the image (not the top-left) Ok

Solution 1:

Well, I finally made it ! (thanks to my 14 year old son who made me revise my trigonometry)

I'm trying to explain here my solution. First, I AM NOT A MATHEMATICIAN. So there is probably a best way, and surely my explanation have errors, but I'm just explaining the logical way I have used to get the good result.

The best idea for that is to first draw a circle around the rectangle, because we need to move the top-left corner of this rectangle, around its own circle, according to the desired angle. So to get the radius of the rectangle circle, we need to compute its hypothenuse, then to divide by 2 :

hypothenuse = math.hypot(layerWidth,layerHeight)
radius = hypothenuse / 2

Then we will be able to draw a circle around the rectangle.

Second, we need to know at which angle, on this circle, is the actual top-left corner of the rectangle. So for that, we need compute the invert tangent of the rectangle, which is arc-tan(height/width). But because we want to know how many degrees are we far from 0°, we need to compute the opposite so arc-tan(width/height).

Finally, another singularity is that Cairo 0° is in fact at 90°, so we will have to rotate again.

This can be shown by this simple graphic : enter image description here

So finally, what is necessary to understand ? If you want to draw a layer, with an angle, rotated by its center, the top-left point will move around the circle according to the desired angle. The top-left position with a given angle of 0 needs to be "the reference".

So we need to get the new X-Y position where to start putting the layer to be able to rotate it : enter image description here

Now, we can write a function that will return the X-Y pos of the top left rectangle where to draw it with a given angle :

defgetTopLeftForRectangleAtAngle(layerLeft,layerTop,layerWidth,layerHeight,angleInDegrees):
    # now we need to know the angle of the top-left corner# for that, we need to compute the arc tangent of the triangle-rectangle:
    layerAngleRad = math.atan((layerWidth / layerHeight))
    layerAngle = math.degrees(layerAngleRad)

    # 0° is 3 o'clock. So we need to rotate left to 90° first# Then we want that 0° will be the top left corner which is "layerAngle" far from 0if (angleInDegrees >= (90 + layerAngle)):
        angleInDegrees -= (90 + layerAngle)
    else:
        angleInDegrees = 360 - ((90 + layerAngle) - angleInDegrees)
    
    angle = (angleInDegrees * math.pi / 180.0)

    centerLeft = layerLeft + (layerWidth / 2)
    centerTop  = layerTop  + (layerHeight / 2)

    # hypothenuse will help us knowing the circle radius
    hypothenuse = math.hypot(layerWidth,layerHeight)
    radius = hypothenuse / 2

    pointX = centerLeft + radius * math.cos(angle)
    pointY = centerTop  + radius * math.sin(angle)

    return (pointX,pointY)

And finally, here is how to use it with an image we want to resize, rotate and write on a context:

def draw_rotated_image(ctx, image_surface, left, top, width, height, angle=0.0, alpha=1.0):
    ctx.save()
    w = image_surface.get_width()
    h = image_surface.get_height()

    # get the new top-left position according to the given angle
    newTopLeft = getTopLeftForRectangleAtAngle(left, top, width, height, angle)

    # translate
    ctx.translate(newTopLeft[0], newTopLeft[1])
    # rotate
    ctx.rotate(angle * math.pi / 180)
    # scale & write
    ctx.scale(width/w, height/h)
    ctx.set_source_surface(image_surface, 0, 0)
    ctx.paint_with_alpha(alpha)

    ctx.restore()
    return

Post a Comment for "Pycairo : How To Resize & Rotate An Image By Its Center To A Final Canvas"