Re: Rotating an image
Re: Rotating an image
- Subject: Re: Rotating an image
- From: Scott Thompson <email@hidden>
- Date: Fri, 5 Oct 2007 09:14:08 -0500
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/0123694736
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
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden