objective-ccocoansimageimage-rotationnsaffinetransform

Rotating an NSImage with or without NSAffineTransform


I've got an NSImage being drawn on a subclass of NSView. In a previous question, I was helped to draw it upright in the upper left corner. Now, I want to be able to rotate the image. I have a button that increments a rotation variable %4, and then I multiply this by -90 to find the rotation angle. I then use an NSAffineTransform to rotate the image, and translate it back onto the screen. However, it doesn't seem to be working the way I expect it to. I have two problems.

1) When I rotate, the portion of the image that is in an area that wasn't in the previous frame gets drawn correctly. However, the potion that was previously there remains as the original image. This means that after several rotations, there's a square of the original upright image and then a rectangle below or to the left of rotate image.

2) When I re-size the window, the image redraws (as it should) in the original upright orientation (as it should not).

Basically, I'm coming to the conclusion that NSAffineTransform doesn't work the way I think it does. Is there some other way to rotate (and translate) the image? thanks

large code chunk: (code between "WORKS" and "end works" is working code to just draw the image. It came from my previous question).

[OLD CODE DELETED, replaced lower with new code]

thanks

EDIT: A little more research finds that instead of doing an NSAffineTtransform I can rotate the view. This seems to work better. However, I can't get the translation to work quite right. new code below (original code deleted to save space)

- (void)drawRect:(NSRect)rect
{

//WORKS
    NSRect frame;
    frame.origin = NSZeroPoint;
    frame.size = [image size];
// end works

    float deltaX, deltaY, height, width;

// if the rotate button was just clicked, we need to rotate by 90 deg, otherwise not
    double rotateDeg = justRot ? -90.0 : 0;

    justRot = NO;

// rotate

    deltaX = 0;
    deltaY = 0;

// translate to account for rotation

    height = [image size].height;
    width = [image size].width;

    switch (rotation)
    {
        case 0:
            NSLog(@"No rotation ");
            break;
        case 1: 
            deltaX += width;
            break;
        case 2:
            deltaX += width;
            deltaY +=  height;  
            break;
        case 3:
            deltaY += height;
            break;
    }


    NSPoint orig;
    if (rotation != 0)
    {
        orig.x = -100;
        orig.y = -100;
    }
    [self rotateByAngle: rotateDeg];
    NSLog(@"orig %f %f", orig.x, orig.y);
//  WORKS
    [self setFrame: frame];
    [image drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1];
// end works
    [self translateOriginToPoint: orig];    
}

Solution

  • Ok, for history, in case anyone else has this question here's the answer I've come up with:

    The frame rotation stays in the drawRect, everything else moves to the rotate method:

    -(void)rotate
    {
        float deltaX, deltaY, height, width;
    
        rotation = (rotation +1) % 4 ;
    
        deltaX = 0;
        deltaY = 0;
    
    // translate to account for rotation
    
        height = [image size].height;
        width = [image size].width;
    
        switch (rotation)
        {
            case 0:
                NSLog(@"No rotation ");
                deltaY -= width;
            break;
            case 1: 
                deltaY -= height;
                break;
            case 2:
                deltaX += height-width;
                deltaY -= height ;  
                break;
            case 3:
                deltaX += height-width;
                deltaY -= width;
                break;
        }
    
        NSPoint orig;
        orig.x = deltaX;
        orig.y = deltaY;
    
        [self rotateByAngle: 90.0];
        [self translateOriginToPoint: orig];
    
        [self setNeedsDisplay:YES];
    }