Rotating an image

  • Ok, I can rotate the view of an image easily by setting the rotation
    of the NSImageView. but I need to be able to actually rotate the
    image in the view and then adjust the size of the view and redisplay.
    I thought that this would be done by NSBezierPath and
    NSAffineTransform. I still think this is the case but my code only
    produces either completely blank images OR completely black images or
    an image with a black rectangle offset to the right by some number of
    pixels or another. I've spent the past two hours on the developer
    site and in google but the answer has not been apparent to me. Below
    is the flawed code. I have subclassed NSImage view and added this
    code to the subclass:

    -(NSImage*)rotateImage:(float)degrees
    {
    NSRect sourceImageRect = [[self cell] rectCoveredByImageInBounds:
    [self bounds]];
    NSAffineTransform * xform = [NSAffineTransform transform];
    NSBezierPath * xformedPath = [NSBezierPath
    bezierPathWithRect:sourceImageRect];
    [xform rotateByDegrees:degrees];
    xformedPath = [xform transformBezierPath:[self imagePath]];
    NSRect newRect =[xformedPath bounds];
    NSImage * newImage = [[NSImage alloc]initWithSize:newRect.size];
    [newImage lockFocus];
      [[NSColor blackColor] set];
      [xformedPath fill];
      [[self image] drawInRect:newRect fromRect:sourceImageRect
    operation:NSCompositeSourceIn fraction:1.0];
    [newImage unlockFocus];
    return newImage;
    }

    - (NSBezierPath *) imagePath {
    NSRect sourceImageRect = [[self cell] rectCoveredByImageInBounds:
    [self bounds]];
    return [NSBezierPath bezierPathWithRect:sourceImageRect];
    }
  • Here's a link to an old project of mine. It's a 2D
    sprite engine that uses Quartz. I abandoned it in
    favor of OpenGL, mainly due to how slow it is. It has
    code for rotating images. Hope this helps.

    http://www.howleware.com/games/osx/sprite_engine_quartz.zip

    --- Development <development...> wrote:

    > Ok, I can rotate the view of an image easily by
    > setting the rotation
    > of the NSImageView. but I need to be able to
    > actually rotate the
    > image in the view and then adjust the size of the
    > view and redisplay.
    > I thought that this would be done by NSBezierPath
    > and
    > NSAffineTransform. I still think this is the case
    > but my code only
    > produces either completely blank images OR
    > completely black images or
    > an image with a black rectangle offset to the right
    > by some number of
    > pixels or another. I've spent the past two hours on
    > the developer
    > site and in google but the answer has not been
    > apparent to me. Below
    > is the flawed code. I have subclassed NSImage view
    > and added this
    > code to the subclass:
    >
    > -(NSImage*)rotateImage:(float)degrees
    > {
    > NSRect sourceImageRect = [[self cell]
    > rectCoveredByImageInBounds:
    > [self bounds]];
    > NSAffineTransform * xform = [NSAffineTransform
    > transform];
    > NSBezierPath * xformedPath = [NSBezierPath
    > bezierPathWithRect:sourceImageRect];
    > [xform rotateByDegrees:degrees];
    > xformedPath = [xform transformBezierPath:[self
    > imagePath]];
    > NSRect newRect =[xformedPath bounds];
    > NSImage * newImage = [[NSImage
    > alloc]initWithSize:newRect.size];
    > [newImage lockFocus];
    > [[NSColor blackColor] set];
    > [xformedPath fill];
    > [[self image] drawInRect:newRect
    > fromRect:sourceImageRect
    > operation:NSCompositeSourceIn fraction:1.0];
    > [newImage unlockFocus];
    > return newImage;
    > }
    >
    > - (NSBezierPath *) imagePath {
    > NSRect sourceImageRect = [[self cell]
    > rectCoveredByImageInBounds:
    > [self bounds]];
    > return [NSBezierPath
    > bezierPathWithRect:sourceImageRect];
    > }
    >


    ____________________________________________________________________________________
    Be a better Heartthrob. Get better relationship answers from someone who knows. Yahoo! Answers - Check it out.
    http://answers.yahoo.com/dir/?link=list&sid=396545433
  • In your posts, and in your code, you seem to be struggling with the
    drawing model.  I don't want to sound like a commercial, but I would
    like to suggest two books on Quartz 2D that may help you come to grips
    with the graphics model and may help you understand how to use things
    like affine transforms.  The first book is one that I wrote:

    http://www.amazon.com/Quartz-2D-Graphics-Mac-Developers/dp/0321336631

    And the second is an excellent book by David Gelphman, an member of
    the the Quartz 2D engineering team:

    http://www.amazon.com/Programming-Quartz-Graphics-Kaufmann-Computer/dp/0123
    694736


    Either of which should help you come to grips with the drawing model
    and how it works.  I don't have it at my fingertips, but I suspect
    that David's book would cover the Cocoa wrappers to Quartz 2D more
    thoroughly than my own does. His book is much better as reference
    material than my own.  In contrast, my book is intended to be a slow
    and gentle introduction to the drawing model that some people find
    helpful when they are first starting out (or so I've been told).

    > Ok, I can rotate the view of an image easily by setting the rotation
    > of the NSImageView. but I need to be able to actually rotate the
    > image in the view and then adjust the size of the view and redisplay.

    What do you mean by "adjust the size of the view and redisplay".  How
    are you trying to adjust the view's size?  Are you trying to make the
    view "just fit" the rotated image?

    > I thought that this would be done by NSBezierPath and
    > NSAffineTransform. I still think this is the case but my code only
    > produces either completely blank images OR completely black images
    > or an image with a black rectangle offset to the right by some
    > number of pixels or another. I've spent the past two hours on the
    > developer site and in google but the answer has not been apparent to
    > me. Below is the flawed code. I have subclassed NSImage view and
    > added this code to the subclass:
    >
    > -(NSImage*)rotateImage:(float)degrees
    > {
    > NSRect sourceImageRect = [[self cell] rectCoveredByImageInBounds:
    > [self bounds]];

    The first odd thing I note about your code is that you are using
    "rectCoveredByImageInBounds" to determine the size of the image.  This
    is an unusual way to obtain the bounds of an image. It would be very
    specific to the context that the view is being drawn in and would not
    take into account the actual image's dimensions.  The image has it's
    own size independent of how large it appears on the display and if you
    want to rotate the original image you would be better served by using
    that size rather than the size at which it is draw in the user's window.

    If I were to write this code, I might write it as:

    NSSize imageSize = [[self image] size];
    NSRect sourceImageRect = NSMakeRect(0, 0, imageSize.width,
    imageSize.height);

    that way you are manipulating the actual size of the image, not just
    how the image appears in the current view.  This is a very important
    distinction.

    > NSAffineTransform * xform = [NSAffineTransform transform];
    > NSBezierPath * xformedPath = [NSBezierPath
    > bezierPathWithRect:sourceImageRect];
    > [xform rotateByDegrees: degrees];
    > xformedPath = [xform transformBezierPath:[self imagePath]];
    > NSRect newRect =[xformedPath bounds];

    There's not really a good reason to construct a bezier path at this
    point.  All you really want to know is how big the image will be after
    it is rotated.  You could get that information from the transformSize
    method on the affine transform.

    This brings up the point that (at least at this point) you don't
    really even need to know the "sourceImageRect".  You could probably
    get by with the "imageSize" above.  There is not really a good reason
    to construct a rect :-)

    Another interesting thing to note about your code here is that you are
    calculating the rotation as if you were rotating the image around it's
    bottom, left corner.  That is fine for this rotation because you are
    only interested in calculating the size of the resulting image.
    However, when you want to draw the image actually rotated, my guess
    would be that you want the image rotated about it's center.  To do
    that, you are going to have to move the image so that it's center
    corresponds with the origin, rotate the image, then put the image back
    where you found it.  This is Computer Graphics 101 type stuff for
    using Affine Transformations so I encourage you to look on the net for
    resources to help you with that.

    > NSImage * newImage = [[NSImage alloc]initWithSize:newRect.size];
    > [newImage lockFocus];
    > [[NSColor blackColor] set];
    > [xformedPath fill];
    > [[self image] drawInRect:newRect fromRect:sourceImageRect
    > operation:NSCompositeSourceIn fraction:1.0];

    Here you are looking to draw the new image, but you haven't done
    anything to take the rotation angle you'd like into account.  Your
    xFormedPath is rotated so it would probably draw something vaguely
    diamond-shaped (instead of filling the entire image in black).  The
    new image you draw is basically axis aligned so it's not going to draw
    as rotated at all. This is where you're going to have to use your
    Computer Graphics 101 transformation discussed above. You want to move
    the origin of the current context to the center of the new image,
    rotate user space, then draw the original image centered around the
    origin.

    Your choice of a composite operation here seems unusual to me too.  I
    would expect that you could leave off the black rectangle and just
    draw the original image using something like SourceCopy mode.  But I'm
    not sure what effect you might be trying to achieve.

    > [newImage unlockFocus];
    > return newImage;
    > }

    Finally, I have a stylistic comment.  Personally, I would not create a
    subclass of NSImageView to contain this method.  This method, to me,
    feels more like something that should be the responsibility of a
    controller and not a view.  However, keep in mind that I have not seen
    your code and don't know the full context.  Take that suggestion with
    a grain of salt and do the right thing for your situation :-)

    Scott
  • I have gotten a solution to all of the image problems I've been
    having with the NSImageView and drawing etc... if any one is
    interested I can post the information for future reference.
previous month october 2007 next month
MTWTFSS
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
Go to today