Rotate NSImage in an NSImageView
I didn’t want to half-implement the new tab feature in Slider – I want people to have almost as much control as if they were coding the site by hand.
This has meant I’ve had to rethink Slider’s UI many times, and I’ve had (re: couldn’t face shipping without) to implement many small features that I feel are required, from a usability standpoint.
When I decided that the user may choose to place tabs in 12 different positions, I needed to consider how this would effect the arrows and sliding animations. I came to the conclusion that if the user elects to place the tabs on the top or bottom, the arrows will be on the left and right, with the animation going right/left. If the user chooses to place tabs on the left or right, the arrows will be positioned on the top and bottom, with the animation going up/down.
Pictures make it easier:


As soon as I implemented this I realised how much of a pain it would be to copy out the arrow images, rotate them in an external program, then copy them back in. So I put some buttons next to the arrow image wells that allow the user to rotate the arrows within Slider itself.
Here is the code I used to rotate the images (thanks to Jerry Krinock and Steve Christensen and for posting this solution to the Cocoa-dev list):
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #import <Cocoa/Cocoa.h> @interface NSImage (Transform) /*! @brief Rotates an image around its center by a given angle in degrees and returns the new image. @details The width and height of the returned image are, respectively, the height and width of the receiver. I have not yet tested this with a non-square image. authors: Jerry Krinock and Steve Christensen */ - (NSImage*)imageRotatedByDegrees:(CGFloat)degrees ; @end #import "NSImage+Transform.h" @implementation NSImage (Transform) - (NSImage*)imageRotatedByDegrees:(CGFloat)degrees { // calculate the bounds for the rotated image NSRect imageBounds = {NSZeroPoint, [self size]}; NSBezierPath* boundsPath = [NSBezierPath bezierPathWithRect:imageBounds]; NSAffineTransform* transform = [NSAffineTransform transform]; [transform rotateByDegrees:degrees]; [boundsPath transformUsingAffineTransform:transform]; NSRect rotatedBounds = {NSZeroPoint, [boundsPath bounds].size}; NSImage* rotatedImage = [[NSImage alloc] initWithSize:rotatedBounds.size]; // center the image within the rotated bounds imageBounds.origin.x = NSMidX(rotatedBounds) - (NSWidth (imageBounds) / 2); imageBounds.origin.y = NSMidY(rotatedBounds) - (NSHeight (imageBounds) / 2); // set up the rotation transform transform = [NSAffineTransform transform]; [transform translateXBy:+(NSWidth(rotatedBounds) / 2) yBy:+ (NSHeight(rotatedBounds) / 2)]; [transform rotateByDegrees:degrees]; [transform translateXBy:-(NSWidth(rotatedBounds) / 2) yBy:- (NSHeight(rotatedBounds) / 2)]; // draw the original image, rotated, into the new image [rotatedImage lockFocus]; [transform concat]; [self drawInRect:imageBounds fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0] ; [rotatedImage unlockFocus]; return [rotatedImage autorelease]; } @end |
I had to change (from/to):
NSImage* rotatedImage = [[[NSImage alloc] initWithSize:rotatedBounds.size] autorelease];
NSImage* rotatedImage = [[NSImage alloc] initWithSize:rotatedBounds.size];
return rotatedImage;return [rotatedImage autorelease];
[transform set];
[transform concat];
For it to work flawlessly with Slider.
Thanks guys!
Like this post? Move it on along with:
Email |
delicious |
Digg |
Tweet |
Reddit |
Newsvine |
Furl |
Google |
Stumble |
HaoHao
| Trackback: |
Scroll to post title
Drag and Drop Reordering of NSTableView’s Rows
I just had to re-implement drag-and-drop-reordering of Slider cells. I was lucky enough to find my post on the Cocoa-dev list – I just had to copy-paste the code.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | #define MyPrivateTableViewDataType @"NSMutableDictionary" - (void)awakeFromNib { [table registerForDraggedTypes:[NSArray arrayWithObject:MyPrivateTableViewDataType]]; } - (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexestoPasteboard:(NSPasteboard*)pboard{ // Copy the row numbers to the pasteboard. NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes]; [pboard declareTypes:[NSArray arrayWithObject:MyPrivateTableViewDataType] owner:controller]; [pboard setData:data forType:MyPrivateTableViewDataType]; return YES; } - (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op{ // Add code here to validate the drop NSLog(@"validate Drop"); return NSDragOperationEvery; } - (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info row:(int)to dropOperation:(NSTableViewDropOperation)operation{ //this is the code that handles dnd ordering - my table doesn't need to accept drops from outside! Hooray! NSPasteboard* pboard = [info draggingPasteboard]; NSData* rowData = [pboard dataForType:MyPrivateTableViewDataType]; NSIndexSet* rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData]; int from = [rowIndexes firstIndex]; NSMutableDictionary *traveller = [[controller arrangedObjects] objectAtIndex:from]; [traveller retain]; int length = [[controller arrangedObjects] count]; NSMutableArray *replacement = [NSMutableArray new]; int i; for (i = 0; i <= length; i++){ if(i == to){ if(from > to){ [controller insertObject:traveller atArrangedObjectIndex:to]; [controller removeObjectAtArrangedObjectIndex:from+1]; } else{ [controller insertObject:traveller atArrangedObjectIndex:to]; [controller removeObjectAtArrangedObjectIndex:from]; } } } } |
Posting replies to the lists helps more than just the receiver – keep in mind that source code can be lost. Time is saved when tedious sections can be copy-pasted in during rewriting!
Like this post? Move it on along with:
Email |
delicious |
Digg |
Tweet |
Reddit |
Newsvine |
Furl |
Google |
Stumble |
HaoHao
| Trackback: |
Scroll to post title
How to Prevent Noisy NSBitmapImageReps

Images generated for Slider’s background and tabs contained a lot of noise – random ugly pixels throughout the images.
I was unsure why this was happening, so I asked the Cocoa-Dev mailing list.
Rob Keniger kindly replied with the following solution:
“An NSBitmapImageRep is not guaranteed to be empty when you create it and in general it’s just full of random bits. You need to clear it explicitly if you are drawing non-opaque content”
He included the following snippet that solved my problem nicely:
1 2 3 4 5 6 7 | - (void) clearBitmapImageRep:(NSBitmapImageRep *)bitmap { unsigned char* bitmapData = [bitmap bitmapData]; if (bitmapData != NULL) { bzero(bitmapData, [bitmap bytesPerRow] * [bitmap pixelsHigh]); } } |
I call it as soon as I’ve created a new NSBitmapImageRep, like so:
1 2 3 4 5 | NSBitmapImageRep *theImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:imageSize.width pixelsHigh:imageSize.height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:0 bitsPerPixel:0]; [self clearBitmapImageRep:theImage]; |
This results in noise-free images.

Thanks Rob!
Like this post? Move it on along with:
Email |
delicious |
Digg |
Tweet |
Reddit |
Newsvine |
Furl |
Google |
Stumble |
HaoHao
| Trackback: |
Scroll to post title
The Mutations of Slider’s Tab Option Pane
During the past week, I’ve been working on Slider’s new Tab option pane.
I took screen shots before making changes to the layout.
Here they are:
I hope I’m not tempted to change things around again… I really need to get the backend going now!
Like this post? Move it on along with:
Email |
delicious |
Digg |
Tweet |
Reddit |
Newsvine |
Furl |
Google |
Stumble |
HaoHao
| Trackback: |
Scroll to post title
Accessing Primitives From NSDictionarys in Cocoa…
This “gotcha” had me scratching my head for a little while.
Edit: this “gotcha” stemmed more from my inexperience with Cocoa than anything else. NSDictionaries store only objects, one shouldn’t try to insert int/float/bool etc into one. Instead use NSNumber for these values. Duh.
When storing various NSObjects in an NSMutableDictionary, I assumed one would access (in this case the object being stored is an NSColor) them like:
1 | [[dictionary objectForKey:idle_TabBorderAllColourKey] color] |
I also assumed that one would access “int”, “float” and other primitives like:
1 | [dictionary objectForKey:idle_TabBorderAllWidthKey] |
After writing, compiling and attempting run my code, however, I discovered that I was wrong.
It should be done like so:
1 | [dictionary objectForKey:idle_TabBorderAllColourKey] |
For NSObjects, and:
1 | [[dictionary objectForKey:idle_TabBorderAllWidthKey] intValue] |
for primitives.
Cocoa noob learns more about Cocoa with each passing minute!
Like this post? Move it on along with:
Email |
delicious |
Digg |
Tweet |
Reddit |
Newsvine |
Furl |
Google |
Stumble |
HaoHao
| Trackback: |
Scroll to post title
Fill a Path With A Scaled Image, Ignoring Image Proportions
While continuing to rebuild Slider’s background image features, I was forced to rewrite the code that scales an image up/down to Slider’s portal size, ignoring the image’s original proportions. The image also had to be painted within a path, as Slider may have differently shaped corners compared to the image to be scaled.
The below code accomplishes the scaling and painting of the image.
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 | //theImage = the resulting image, that will have a scaled (proportions ignored) version of "bgImage" painted onto it NSBitmapImageRep *theImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:imageSize.width pixelsHigh:imageSize.height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:0 bitsPerPixel:0]; [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:theImage]]; NSImage bg = [[NSImage alloc] initWithSize: imageSize]; //bgImage can be any NSImage NSSize originalSize = [bgImage size]; [bg lockFocus]; [bgImage drawInRect: NSMakeRect(0, 0, imageSize.width, imageSize.height) fromRect: NSMakeRect(0, 0, originalSize.width, originalSize.height) operation: NSCompositeSourceOver fraction: 1.0]; [bg unlockFocus]; NSColor *c = [NSColor colorWithPatternImage:bg]; [c set]; [path appendBezierPathWithRoundedRect:NSMakeRect(0, 0, imageSize.width, imageSize.height) xRadius:10.0 yRadius:10.0]; [aPath fill]; [NSGraphicsContext restoreGraphicsState]; |
“path” could be any NSBezierPath.
Like this post? Move it on along with:
Email |
delicious |
Digg |
Tweet |
Reddit |
Newsvine |
Furl |
Google |
Stumble |
HaoHao
| Trackback: |
Scroll to post title
Feel Like A Cup of Cocoa?
I released the Smart Buttons last night, and displayed them with my new store layout.
GreyBox is so good, I keep thinking of new ways to use it.
Now that the Smart Buttons have finished, I feel it is time to move onto another RW related idea. This will be a stand-alone program – a RW helper if you will.
And that’s about all I have to say on the subject at this time.
I’m off to read the Cocoa manual.
Like this post? Move it on along with:
Email |
delicious |
Digg |
Tweet |
Reddit |
Newsvine |
Furl |
Google |
Stumble |
HaoHao
| Trackback: |
Scroll to post title



























Recent Comments
The Best Photographer In Shanghai
When we pick them up :( Don't know when that will be yet!
Chose another 49 today
Wed, 13 Jan 2010 14:13:21 +0000
The Best Photographer In Shanghai
When do we get to see them!!!!!
Tue, 12 Jan 2010 19:08:51 +0000
Installing Jdownloader In Ubuntu
Thank You!!!!
It works like a charm!!!!!!!!!!!!
Tue, 05 Jan 2010 13:02:35 +0000
Arrived In Shanghai
Done, look left!
Sun, 03 Jan 2010 15:37:03 +0000
Arrived In Shanghai
Hi :)
I'll see what I can do!
Sun, 03 Jan 2010 14:23:07 +0000
Arrived In Shanghai
Hi Mike. It would be very useful to have the time date and weather conditions in Shanghai, on your site. Be seeing you soon. Love NZMum.
Sat, 02 Jan 2010 23:30:41 +0000