views:

396

answers:

1

I have a subclass of NSView, and in that I'm drawing an NSImage. I'm unsing NSAffineTransforms to rotate, translate and scale the image.

Most of it works fine. However, sometimes, the transforms just don't seem to get activated.

For example, when I resize the window, the rotate transform doesn't happen.

When I zoom in on the image, it puts the lower left of the image in the correct place, but doesn't zoom it, but it does zoom the part of the image that would be to the right of the original sized image. If I rotate this, it zooms correctly, but translates wrong. (The transation may be a calculation error on my part)

Here is the code of my drawRect: (sorry for the long code chunk)

- (void)drawRect:(NSRect)rect 
{
    // Drawing code here.
    double rotateDeg = -90* rotation;
    NSAffineTransform *afTrans = [[NSAffineTransform alloc] init];
    NSGraphicsContext *context = [NSGraphicsContext currentContext];
    NSSize sz;
    NSRect windowFrame = [[self window] frame];
    float deltaX, deltaY;
    NSSize superSize = [[self superview] frame].size;
    float height, width, sHeight, sWidth;

    NSRect imageRect;

    if(image)
    {
     sz = [ image size];
     imageRect.size = sz;
     imageRect.origin = NSZeroPoint;
     imageRect.size.width *= zoom;
     imageRect.size.height *= zoom;

     height = sz.height * zoom  ;
     width = sz.width *zoom ;
     sHeight = superSize.height;
     sWidth = superSize.width;
    }

I need to grab the sizes of everything early so that I can use them later when I rotate. I am not sure that I need to protect any of that, but I'm paranoid from years of C...

    [context saveGraphicsState];

// rotate
    [afTrans rotateByDegrees:rotateDeg];
// translate to account for window size;
    deltaX = 0;
    deltaY = 0;

// translate to account for rotation

// in 1 and 3, X and Y are reversed because the entire FRAME
// (inculding axes) is rotated!
    switch (rotation)
    {
     case 0:
//    NSLog(@"No rotation ");
      break;
     case 1: 
      deltaY -= (sHeight - height); 
      deltaX -= sHeight ;
      break;
     case 2:
      deltaX -= width;
      deltaY -= ( 2*sHeight - height); 
      // it's rotating around the lower left of the FRAME, so,
      // we need to move it up two frame hights, and then down
      // the hieght of the image
      break;
     case 3:
      deltaX += (sHeight - width);
      deltaY -= sHeight;
      break;
    }

Since I'm rotating around the lower left corner, and I want the image to be locked to the upper left corner, I need to move the image around. When I rotate once, the image is in the +- quadrant, so I need to shift it up one view-height, and to the left a view-height minus an image height. etc.

    [afTrans translateXBy:deltaX yBy:deltaY];

// for putting image in upper left

// zoom
    [afTrans scaleBy: zoom];
    printMatrix([afTrans  transformStruct]);
    NSLog(@"zoom %f", zoom);
    [afTrans concat];

    if(image)
    {
     NSRect drawingRect = imageRect;
     NSRect frame = imageRect;

     frame.size.height = MAX(superSize.height, imageRect.size.height) ;
     [self setFrame:frame];

     deltaY =  superSize.height - imageRect.size.height;
     drawingRect.origin.y += deltaY;

This makes the frame the correct size so that the image is in the upper left of the frame. If the image is bigger than the window, I want the frame to be big enough so scroll bars appear. If it isn't I want the frame to be big enough that it reaches the top of the window.

     [image drawInRect:drawingRect
         fromRect:imageRect
         operation:NSCompositeSourceOver
         fraction:1];

     if((rotation %2) )
     {
      float tmp;
      tmp = drawingRect.size.width;
      drawingRect.size.width = drawingRect.size.height;
      drawingRect.size.height = tmp;
     }

This code may be entirely historical, now that I look at it... the idea was to swap height andwidth if I rotated 90 or 270 degs.

    }
    else
     NSLog(@"no image"); 

    [afTrans release];
    [context restoreGraphicsState];

}
A: 

Why do you use the superview's size? That's something you should almost never need to worry about. You should make the view work on its own without dependencies on being embedded in any specific view.

Scaling the size of imageRect is probably not the right way to go. Generally when calling -drawImage you want the source rect to be the bounds of the image, and scale the destination rect to zoom it.

The problems you're reporting kind of sound like you're not redrawing the entire view after changing the transformation. Are you calling -setNeedsDisplay: YES?

How is this view embedded in the window? Is it inside an NSScrollView? Have you made sure the scroll view resizes along with the window?

Jens Alfke
I need the superview's size because otherwise I have no idea where the upper left corner is. I want my image locked to the upper left, not the lower left. I can PROBABLY get away from this using the isFlipped method but thought this way was cleaner when I started... I can try doing the scaling entirely with the drawRect, that's worth trying at least, but I AM using needsDisplay:YES and the the view is in a scroll which is locked to the size of the window.
Brian Postow