Welcome to the MacNN Forums.

If this is your first visit, be sure to check out the FAQ by clicking the link above. You may have to register before you can post: click the register link above to proceed. To start viewing messages, select the forum that you want to visit from the selection below.

You are here: MacNN Forums > Software - Troubleshooting and Discussion > Developer Center > Create bitmap & display in a view (Cocoa Newbie :)

Create bitmap & display in a view (Cocoa Newbie :)
Thread Tools
Professional Poster
Join Date: Nov 2000
Location: Tasmania, Australia
Status: Offline
Reply With Quote
Jun 16, 2002, 11:17 PM
 
How do I create a bitmap graphic (ie, from scratch by specifying each pixel programatically, not from an existing graphic file or any other data), and then make it appear in an NSView (or custom view)?

I've had a look through a lot of documentation (NSImage, NSView, Core Graphics, etc) and every time I think I've nearly put all the pieces together, I find I'm missing something important.

I figured out how I should be able to build a bitmap using Core Graphics and store it in a memory address space, but I couldn't figure out how to display that in an NSView. Similarly, I could figure out how I should be able to display in image in an NSView from a file, but not from a bitmap I build on the fly.

I read the last chapter of "Learning Cocoa" (O'Reilly), but that just didn't explain enough for me. It only covered beziers (NSBezierPath and the Core Graphics equivalents). It didn't even touch on bitmaps.

From what I gather, I need to use an NSImage with an NSBitmapImageRep. But how do I tell the NSBitmapImageRep what each individual pixel should look like?

Can someone explain this to me in a very basic way (I'm new to all this).

Any advice would be greatly appreciated.

<small>[ 06-17-2002, 12:35 AM: Message edited by: Brass ]</small>
     
Mac Elite
Join Date: Sep 2000
Location: Tempe, AZ
Status: Offline
Reply With Quote
Jun 16, 2002, 11:57 PM
 
Create an NSBitmapImageRep using the

- (id)initWithBitmapDataPlanesunsigned char **)planes pixelsWideint)width pixelsHighint)height bitsPerSampleint)bps samplesPerPixelint)spp hasAlphaBOOL)alpha isPlanarBOOL)isPlanar colorSpaceNameNSString *)colorSpaceName bytesPerRowint)rowBytes bitsPerPixelint)pixelBits

initializer. Pass null for planes so that it allocates its own memory. Use bitmapData to get a pointer to the data space. If you passed 8 for bps and 4 for spp, this will be in RGBA format. Write your values in, and then addRepresentation it into an NSImage. You've got a constructed-on-the-fly bitmap.
Geekspiff - generating spiffdiddlee software since before you began paying attention.
     
Brass  (op)
Professional Poster
Join Date: Nov 2000
Location: Tasmania, Australia
Status: Offline
Reply With Quote
Jun 17, 2002, 12:13 AM
 
</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">quote:</font><hr /><font size="1" face="Geneva, Verdana, Arial, sans-serif">Originally posted by smeger:
<strong>Create an NSBitmapImageRep using the

- (id)initWithBitmapDataPlanesunsigned char **)planes pixelsWideint)width pixelsHighint)height bitsPerSampleint)bps samplesPerPixelint)spp hasAlphaBOOL)alpha isPlanarBOOL)isPlanar colorSpaceNameNSString *)colorSpaceName bytesPerRowint)rowBytes bitsPerPixelint)pixelBits

initializer. Pass null for planes so that it allocates its own memory. Use bitmapData to get a pointer to the data space. If you passed 8 for bps and 4 for spp, this will be in RGBA format. Write your values in, and then addRepresentation it into an NSImage. You've got a constructed-on-the-fly bitmap.</strong></font><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">Excellent! Thanks for this pointer. I couldn't make complete sense of your post (like where on earth "bitmapData" comes from) but after looking up the doco on this method it all makes sense now. I don't know why I couldn't find this method in my own search of the doco earlier (maybe my brain saw the number of arguments and skipped over it in fear). Thanks again for pointing me in the right direction - I really appreciate it!
     
Mac Elite
Join Date: Sep 2000
Location: Tempe, AZ
Status: Offline
Reply With Quote
Jun 17, 2002, 03:27 PM
 
My pleasure, hope it works out.

P.S. Sorry 'bout the cryptic post, my girlfriend was hanging over my shoulder telling me to hurry up
Geekspiff - generating spiffdiddlee software since before you began paying attention.
     
Brass  (op)
Professional Poster
Join Date: Nov 2000
Location: Tasmania, Australia
Status: Offline
Reply With Quote
Jun 17, 2002, 06:16 PM
 
</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">quote:</font><hr /><font size="1" face="Geneva, Verdana, Arial, sans-serif">Originally posted by smeger:
<strong>My pleasure, hope it works out.

P.S. Sorry 'bout the cryptic post, my girlfriend was hanging over my shoulder telling me to hurry up </strong></font><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">If it doesn't work out, I'll be sure to post back (when I eventually get another chance to try it).

I actually miss my girlfriend hanging over my shoulder, so I understand (she's been on the other side of the world for nearly a year... only 6 weeks to go! I'm going insane!).
     
Brass  (op)
Professional Poster
Join Date: Nov 2000
Location: Tasmania, Australia
Status: Offline
Reply With Quote
Jun 17, 2002, 10:27 PM
 
Okay, I'm getting there, but I'm stuck again. I've got:

• an NSImage with one NSBitmapImageRep all allocated and initialised
• 5 pointers pointing to the memory which should hold the pixel components (one for each plane) for the NSBitmapImageRep
• a window containing an NSImageView
• the NSImageView has it's image object set to the NSImage

How do I get the image represented by the NSBitmapImageRep (or NSImage) to appear in the NSImageView? I've had a look at NSImageView's "draw..." and "composite..." methods, as well as NSView's "setNeedsDisplay", but I can't seem to get them to do anything useful.

If anyone can help me to understand how this works, I'd be very grateful.

<small>[ 06-17-2002, 11:32 PM: Message edited by: Brass ]</small>
     
Mac Elite
Join Date: Sep 2000
Location: Tempe, AZ
Status: Offline
Reply With Quote
Jun 18, 2002, 02:28 AM
 
You set the NSImage to use the NSBitmapImageRep by doing [myNSImage addRepresentation: myNSBitmapImageRep]. You set the NSImageView to use the NSImage by doing [myNSImageView setImage: myNSImage]. That's it. You can force a redraw by minimizing your window to the dock and maximizing it again.

If you're not seeing anything, your image is probably screwy. You could try using an image that you loaded from a file as a test (see the NSImage initializers).

You mentioned five data planes. I've always set isPlanar to NO so I have a single data plane. If you do this and SPP=4 and BPS=8, each 32 bit value of your single data plane consists of an 8-bit R, 8-bit G, 8-bit B, and 8-bit Alpha.

I'm not sure what kind of color scheme would have five data planes?
Geekspiff - generating spiffdiddlee software since before you began paying attention.
     
Brass  (op)
Professional Poster
Join Date: Nov 2000
Location: Tasmania, Australia
Status: Offline
Reply With Quote
Jun 18, 2002, 05:21 PM
 
</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">quote:</font><hr /><font size="1" face="Geneva, Verdana, Arial, sans-serif">Originally posted by smeger:
<strong>You set the NSImage to use the NSBitmapImageRep by doing [myNSImage addRepresentation: myNSBitmapImageRep]. You set the NSImageView to use the NSImage by doing [myNSImageView setImage: myNSImage]. That's it. You can force a redraw by minimizing your window to the dock and maximizing it again.

If you're not seeing anything, your image is probably screwy. You could try using an image that you loaded from a file as a test (see the NSImage initializers).

You mentioned five data planes. I've always set isPlanar to NO so I have a single data plane. If you do this and SPP=4 and BPS=8, each 32 bit value of your single data plane consists of an 8-bit R, 8-bit G, 8-bit B, and 8-bit Alpha.

I'm not sure what kind of color scheme would have five data planes?</strong></font><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">Thanks for the tips. I seem to be doing it mostly right. My image itself must be screwy I'll try an existing or known image like you suggested, I think.

From what I've looked at in the doco, I think the 5-planes are for CMYK with alpha (does alpha make sense with CMYK? maybe not on a printer, but maybe it's for theoretical devices).
     
Brass  (op)
Professional Poster
Join Date: Nov 2000
Location: Tasmania, Australia
Status: Offline
Reply With Quote
Jun 18, 2002, 08:34 PM
 
aha! There's something wrong with my pointers. After running the "initWithBitmapDataPlanes..." method, my pointers are all NULL. I'm not sure why that is, but I guess I've done something wrong in setting up or passing the pointers. I'll go through it and see if I can make sense of it. The whole pointer-to-pointers thing always confuses me.
     
Mac Elite
Join Date: Sep 2000
Location: Tempe, AZ
Status: Offline
Reply With Quote
Jun 18, 2002, 09:45 PM
 
If you pass NULL for planes in initWithBitmapDataPlanes..., the routine will allocate the memory for you. You can then do
</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">unsigned char *planes[5];
[myNSBitmapImageRep getBitmapDataPlanes: planes];
// planes[0] is the first plane, etc.</pre><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">No allocating necessary.
Geekspiff - generating spiffdiddlee software since before you began paying attention.
     
Brass  (op)
Professional Poster
Join Date: Nov 2000
Location: Tasmania, Australia
Status: Offline
Reply With Quote
Jun 18, 2002, 11:01 PM
 
hmmm... I did have my pointers declared and assigned a bit differently but I fixed that now, but I'm still having no luck. I'm setting pixel values like this (for RGB, no alpha, colour grey for all 100x100 pixels):

</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">
for ( y = 0; y &lt; 100; y++ )
{
for ( x = 0; x &lt; 100; x++ )
{
bitmapDataPlanes[0][100 * y + x] = (unsigned char) 64;
bitmapDataPlanes[1][100 * y + x] = (unsigned char) 64;
bitmapDataPlanes[2][100 * y + x] = (unsigned char) 64;
}
}
</pre><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">is this right? It's taking me a while to catch on, I think

When I use the debugger, it shows that the value 64 is actually at those locations (at least at the three I can check easily, for the pixel at point (0, 0) ).

<small>[ 06-19-2002, 01:31 AM: Message edited by: Brass ]</small>
     
Mac Elite
Join Date: Sep 2000
Location: Tempe, AZ
Status: Offline
Reply With Quote
Jun 20, 2002, 03:27 PM
 
Sorry, I'm out of ideas. As I said, I haven't done it with isPlanar=YES. But your assignment looks fine. What are you using for BPP, SPP, BPS, etc?
Geekspiff - generating spiffdiddlee software since before you began paying attention.
     
Brass  (op)
Professional Poster
Join Date: Nov 2000
Location: Tasmania, Australia
Status: Offline
Reply With Quote
Jun 20, 2002, 05:05 PM
 
Thanks for you patience and persistence. Here's almost the entire code (doesn't do much, just for testing for me to get this working so far). In the mean time, I might try it with isPlanar:NO.

</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">
- (id)init
{
[super init];

bitmapRep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL pixelsWide:100 pixelsHigh:100
bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:YES
colorSpaceName:NSCalibratedRGBColorSpace
bytesPerRow:0 bitsPerPixel:0];

[bitmapRep getBitmapDataPlanes:bitmapDataPlanes];

//bitmapDataRed = bitmapDataPlanes[0];
//bitmapDataGreen = bitmapDataPlanes[1];
//bitmapDataBlue = bitmapDataPlanes[2];

graphImage = [[NSImage alloc] initWithSize:imageSize];
[graphImage addRepresentation:bitmapRep];
[graphImageView setImage:graphImage];

return self;
}

- (void)dealloc
{
[graphImage release];
[bitmapRep release];

[super release];
}

- (void)setAllPixels
{
int x, y;

for ( y = 0; y &lt; 100; y++ )
{
for ( x = 0; x &lt; 100; x++ )
{
bitmapDataPlanes[0][100 * y + x] = (unsigned char)64;
bitmapDataPlanes[1][100 * y + x] = (unsigned char)64;
bitmapDataPlanes[2][100 * y + x] = (unsigned char)64;
}
}
}

- (IBAction)drawGraphid)sender
{
[self setAllPixels];

[graphImageView setNeedsDisplay:YES];
}
</pre><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">EDIT: While I'm at it, I may as well post the interface part as well:

</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">
{
IBOutlet NSImageView *graphImageView;

NSSize imageSize;
NSImage *graphImage;
NSBitmapImageRep *bitmapRep;

unsigned char *bitmapDataPlanes[5];

unsigned char *bitmapDataRed;
unsigned char *bitmapDataGreen;
unsigned char *bitmapDataBlue;
unsigned char *bitmapDataAlpha;
unsigned char *bitmapDataOther;
}

- (IBAction)drawGraphid)sender;

- (id)init;
- (void)dealloc;
</pre><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">In Interface Builder, there's just one small window which contains a small NSImageView (CustomView) and one button. The button and image view are the IBAction and IBOutlet respectively, and the object for which the code is above is instantiated in Image Builder.

<small>[ 06-20-2002, 06:18 PM: Message edited by: Brass ]</small>
     
Mac Elite
Join Date: Sep 2000
Location: Tempe, AZ
Status: Offline
Reply With Quote
Jun 21, 2002, 04:10 PM
 
Your 'imageSize' class variable is never initialized, so it defaults to NSZeroSize,meaning that you've got an image that's 0 by 0 pixels. Also, it's good practive to wrap your init in a
</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">if (self = [super init]) {
}
return self;</pre><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">block in case super can't init.

Everything else seems fine.
Geekspiff - generating spiffdiddlee software since before you began paying attention.
     
Brass  (op)
Professional Poster
Join Date: Nov 2000
Location: Tasmania, Australia
Status: Offline
Reply With Quote
Jun 23, 2002, 07:00 PM
 
oh yes, I'd cut them out during some earlier fiddling around. I've put it back in now:

</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">code:</font><hr /><pre style="font-size:x-small; font-family: monospace;"> imageSize.width = 100.0;
imageSize.height = 100.0; </pre><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">But it doesn't help. No image ever appears in the view <img border="0" title="" alt="[Frown]" src="frown.gif" />
     
Junior Member
Join Date: Jul 2001
Location: Mos Eisley Cantina
Status: Offline
Reply With Quote
Jun 24, 2002, 03:02 AM
 
In your setAllPixels function, try putting a [graphImage recache] after you set the pixel values.
     
Forum Regular
Join Date: Sep 2000
Status: Offline
Reply With Quote
Jun 24, 2002, 10:15 AM
 
Brass, in an attempt to help and teach myself a couple things at the same time, I took your code and used it as the basis for a subclass of NSView called "PixelView". I placed this custom view on a window, set it to auto-resize with the window, and implemented the code as follows:

Interface (PixelView.h):
</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">#import &lt;Cocoa/Cocoa.h&gt;

@interface PixelView : NSView
{
NSImage *pixelImage;
NSBitmapImageRep *bitmapRep;
unsigned char *bitmapDataPlanes[5];
}
- (void)setupImageWithSizeNSSize)size;
- (void)setPixelsWithSizeNSSize)size;
@end</pre><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">Implementation (PixelView.m):
</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">#import &quot;PixelView.h&quot;

@implementation PixelView

- (id)initWithFrameNSRect)frame
{
// initWithFrame: is the designated initializer for the NSView class
if (self = [super initWithFrame:frame]) {
[self setupImageWithSize:frame.size];
[self setPixelsWithSize:frame.size];
}
return self;
}

- (void)dealloc
{
[pixelImage release];
[bitmapRep release];
[super dealloc];
}

- (void)setupImageWithSizeNSSize)size
{
// Create a new bitmap of the appropriate size to place pixel data in
[bitmapRep release];
bitmapRep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:size.width pixelsHigh:size.height
bitsPerSample:8 samplesPerPixel:3
hasAlpha:NO isPlanar:YES
colorSpaceName:NSCalibratedRGBColorSpace
bytesPerRow:0 bitsPerPixel:0];

// Get the array of pointers to the bitmap data
[bitmapRep getBitmapDataPlanes:bitmapDataPlanes];

// Create a new NSImage to contain this data
[pixelImage release];
pixelImage = [[NSImage alloc] initWithSize:size];
[pixelImage addRepresentation:bitmapRep];
}

- (void)setPixelsWithSizeNSSize)size
{
int width, height; // Integer versions of width/height
unsigned char rgbValues[3]; // RGB values for the current row
int x, y, pixelIndex; // Loop indices and current pixel index

width = (int)size.width;
height = (int)size.height;

// Set up the color values for the pixels to be drawn
for (y = 0; y &lt; height; y++) {
rgbValues[0] = (unsigned char)(y / size.height * 64);
rgbValues[1] = (unsigned char)(y / size.height * 128);
rgbValues[2] = (unsigned char)(y / size.height * 192);
for (x = 0; x &lt; width; x++) {
pixelIndex = width * y + x;
bitmapDataPlanes[0][pixelIndex] = rgbValues[0];
bitmapDataPlanes[1][pixelIndex] = rgbValues[1];
bitmapDataPlanes[2][pixelIndex] = rgbValues[2];
}
}
}

- (void)setFrameNSRect)frameRect
{
// This gets called by AppKit when the view is resized
[super setFrame:frameRect];
[self setupImageWithSize:frameRect.size];
[self setPixelsWithSize:frameRect.size];
}

- (void)drawRectNSRect)rect
{
// This gets called by AppKit when the image needs to be drawn
[pixelImage compositeToPoint:NSMakePoint(0.0, 0.0) operation:NSCompositeCopy];
}

- (BOOL)isOpaque
{
// Tell AppKit this view is opaque to increase performance
return YES;
}

@end</pre><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">The result is a manually-created gradient-filled rectangle that always fills the view. It probably isn't the most efficient code in the world, but it does work. Hope it helps.

[Edit: Fixed the dealloc method]

<small>[ 06-24-2002, 05:29 PM: Message edited by: Marshall ]</small>
     
Brass  (op)
Professional Poster
Join Date: Nov 2000
Location: Tasmania, Australia
Status: Offline
Reply With Quote
Jun 24, 2002, 05:23 PM
 
Guys,

Thanks for all your tips and help so far. It's really encouraging to have this kind of support.

PipelineStall - unfortunately the "recache" didn't help me in this case.

Marshall - your code reminds me that I've got a lot to learn about good Objective C style. It's a completely different approach to the way I was doing things. I'll give it a go and when I get that working, I'll try to adapt it to what I was originally aiming for (which was drawing fractals!).

Out of curiosity, why did you subclass NSView rather than NSImageView?

<small>[ 06-24-2002, 06:26 PM: Message edited by: Brass ]</small>
     
Forum Regular
Join Date: Sep 2000
Status: Offline
Reply With Quote
Jun 24, 2002, 05:57 PM
 
</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">quote:</font><hr /><font size="1" face="Geneva, Verdana, Arial, sans-serif">Originally posted by Brass:
<strong>Out of curiosity, why did you subclass NSView rather than NSImageView?</strong></font><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">NSImageView strikes me as the sort of thing I'd use for holding static images, like an icon on a dialog, whereas if I were modifying pixel data directly it would probably be for some direct-to-screen animated effect or custom UI element. Since most of the examples I've seen of this sort of thing subclass NSView (including Apple's DotView, CircleView, CompositeView, etc. examples), I figured I'd follow the pattern. It just seems like a more lightweight solution since I didn't need the extra features of the NSImageView just to get pixels to the screen.
     
Forum Regular
Join Date: Sep 2000
Status: Offline
Reply With Quote
Jun 25, 2002, 08:16 AM
 
A bit more info that I just found out: NSBitmapImageRep can draw itself without needing to be wrapped in an NSImage. It won't alpha-blend the image or do any other compositing effects, but if you're just trying to get a block of pixel data to the view, it seems to be a more efficient way of doing so.

To draw from the NSBitmapImageRep using the code I posted above, replace the line in drawRect:
</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">[pixelImage compositeToPoint:NSMakePoint(0.0, 0.0) operation:NSCompositeCopy];</pre><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">With this:
</font><blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">[bitmapRep draw];</pre><hr /></blockquote><font size="1" face="Geneva, Verdana, Arial, sans-serif">That will draw the bitmap data at (0.0, 0.0) in the view. NSImageRep also defines drawAtPoint: and drawInRect: methods if you want to place the bitmap at a different location.

You can then remove all references to the NSImage from the interface and implementation.
     
Brass  (op)
Professional Poster
Join Date: Nov 2000
Location: Tasmania, Australia
Status: Offline
Reply With Quote
Jun 25, 2002, 05:01 PM
 
Marshall,

Thanks for all the info. It's been great. I've got the application working fairly well now, drawing nice fractals (greyscale so far, colour will be next). I might try the bitmap image representation without the NSImage like you suggested (I've already completely changed the code you gave above). I was just about to change the drawRect to only draw the actual rectange given as the parameter, but I might leave that until after I've removed NSImage from the equation.

I'm a bit dissappointed I couldn't figure out why my original implementation didn't work, but the current solution is a better one anyway.

If I ever release the software I'll be sure to give you and "smeger" credit for helping get on my feet with Cocoa and bitmap image creation.
     
   
Thread Tools
Forum Links
Forum Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On
Top
Privacy Policy
All times are GMT -5. The time now is 01:25 PM.
All contents of these forums © 1995-2011 MacNN. All rights reserved.
Branding + Design: www.gesamtbild.com
vBulletin v.3.8.7 © 2000-2011, Jelsoft Enterprises Ltd., Content Relevant URLs by vBSEO 3.3.2