Drawing bitmaps in OpenGL

Apprentice
Posts: 11
Joined: 2007.01
Post: #1
I'm going absolutely nuts trying to figure out how to draw an image to the screen through OpenGL. I'm reading the red book and I've spent a great deal of time looking for examples and reading code and yet I can't seem to make it work. Can anyone take a look at this and point me in the right direction? I have a PNG 200x200 pixel image. I'm just trying to code a simple example that will place that PNG ('scarlett.png') onto the screen. I drew a red backdrop just to make sure that the other command were getting flushed properly. File and image reps seem to load fine when checked. But I get no compiling errors, just a big window filled with red.

any help would be **greatly** appreciated

Code:
    glClearColor(1.0,0.0,0.0,1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    
    
    NSData *raw;
    NSBitmapImageRep *rep;
    raw = nil;
    raw = [NSData dataWithContentsOfFile:@"/Users/matt/Desktop/Projects/Tests/Golden Triangle/scarlett.png"];
    if (raw!=nil)
    {
        rep = [NSBitmapImageRep imageRepWithData: raw];
        glShadeModel(GL_FLAT);
        glRasterPos2i(10,10);
        glDrawPixels(200,200,GL_RGBA,GL_UNSIGNED_BYTE,[rep bitmapData]);
    }
    else
    {NSLog(@"err");}
    
    glFlush();
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #2
Why dont you load the image data into an array then pass it to GL via glTexImage2D then draw a texture mapped quad instead of using drawpixels.

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Moderator
Posts: 133
Joined: 2008.05
Post: #3
NSBitmapImageRep's bitmapData method will not return data suitable for OpenGL's default settings for image data. You can find the functions to change the way OpenGL reads image data, but from everything I've heard, NSBitmapImageRep is not guarenteed to work with OpenGL. Look into libpng or some other image loading library.

And if you plan to go with unknown's route(using the image as a texture), the above applies, and your image's dimensions must also be a power of 2.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #4
search the forums for "texture loading" and you'll turn up a QuickTime texture loader and an ImageIO texture loader. If you need cross-platform, there's always libjpeg and libpng, too.

200x200 is a slightly tricky size, being non-power-of-two. You'll either need to draw a sub-area of a 256x256 texture, or use ARB_texture_rectangle, or [implicitly] use OpenGL 2.0/ARB_texture_non_power_of_two, if your video card is recent enough.
Quote this message in a reply
Apprentice
Posts: 11
Joined: 2007.01
Post: #5
Thanks for the pointers, I'll do some more searching and keep at it~
Quote this message in a reply
Apprentice
Posts: 11
Joined: 2007.01
Post: #6
just an FYI for anyone who might want it, here was the code that finally worked. the key was changing the PNG to a 256x256 image file. Although I'm not sure how I'm going to re-do my current game in OpenGL if I have to make all the image files power of 2 sizes. Sad

Anyways this code ended up working fine tho. Apparently also changing the GL_RGBA to GL_RGB was necessary, I thought that my PNG had contained a byte for Alpha but when loaded, apparently it was discarded.

the final thing was that my coordinates were wrong, i changed the raster drawing position to 0,0

with those things done, this seems to work fine:

Code:
glClearColor(1.0,0.0,0.0,1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    
    NSData *raw;
    NSBitmapImageRep *rep;
    raw = nil;
    raw = [NSData dataWithContentsOfFile:@"/Users/matt/Desktop/Projects/Tests/Golden Triangle/scarlett.png"];
    if (raw!=nil)
    {
        rep = [NSBitmapImageRep imageRepWithData: raw];
        glShadeModel(GL_FLAT);
        glRasterPos2i(0,0);
        glDrawPixels(256,256,GL_RGB,GL_UNSIGNED_BYTE,[rep bitmapData]);
    }
    else
    {NSLog(@"err");}
    
    glFlush();
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #7
glDrawPixels simply puts the pixels on the framebuffer, and shouldn't be limited to powers of 2. However, the data may have been stored in such a way that it works using NSBitmapImageRep's methods work with OpenGL with those dimensions.
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #8
Right. Drawpixels should work with any dimensions as long as all of the pixel state is correct (i.e. you need to query the NSBitmapImageRep to find out how many components it really has and what the rowbytes really are.)

Also RasterPos is transformed by the usual MVP. Use glWindowPos if you want to bypass that.
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #9
As a note, you may need to use glPixelStorei to set the GL_UNPACK_ALIGNMENT to however NSBitmapImageRep stores it. (and possibly some other parameters as well)
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #10
Bottom line, you should *not* use NSBitmapImageRep for image loading, unless you create the object yourself with the initializer that takes about 17 arguments, passing sane values for each. There are many pixel formats that NSBitmapImageRep supports that OpenGL does not, and no guarantee on any API about what pixel format it will actually give you.

QuickTime, ImageIO, libpng and libjpeg are all safe. Use one of them instead.
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #11
I would recommend libpng: it's cross platform, and though it looks daunting at first, it's actually surprisingly simple.
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #12
OneSadCookie Wrote:unless you create the object yourself with the initializer that takes about 17 arguments, passing sane values for each
Here's a code snippet that demonstrates a way to do this. It's likely to be slower than loading the image with something that puts it into a known format (because it makes a copy of the image in a potentially different format), but it does the job.
Code:
unsigned char * NSBitmapImageRepToRGBAPixelArray(NSBitmapImageRep * bitmap, int targetWidth, int targetHeight) {
  unsigned char * pixels;
  static long systemVersion = 0x0000;
  
  if (systemVersion == 0x0000) {
    Gestalt(gestaltSystemVersion, &systemVersion);
  }
  
  pixels = (unsigned char *) malloc(4 * targetWidth * targetHeight);
  
  if (systemVersion >= 0x1040) {
    NSBitmapImageRep * bitmap2;
    NSGraphicsContext * context;
    
    bitmap2 = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: &pixels
                                        pixelsWide: targetWidth
                                        pixelsHigh: targetHeight
                                        bitsPerSample: 8
                                        samplesPerPixel: 4
                                        hasAlpha: YES
                                        isPlanar: NO
                                        colorSpaceName: NSDeviceRGBColorSpace
                                        bitmapFormat: NSAlphaNonpremultipliedBitmapFormat
                                        bytesPerRow: (targetWidth * 4)
                                        bitsPerPixel: 32];
    
    context = [NSGraphicsContext graphicsContextWithBitmapImageRep: bitmap2];
    [NSGraphicsContext saveGraphicsState];
    [NSGraphicsContext setCurrentContext: context];
    [bitmap drawInRect: NSMakeRect(0, 0, targetWidth, targetHeight)];
    [NSGraphicsContext restoreGraphicsState];
    
    [bitmap2 release];
  } else {
    memcpy(pixels, [bitmap bitmapData], (4 * targetWidth * targetHeight));
  }
  
  return pixels;
}
If you don't need pre-10.4 support, you can take out the Gestalt stuff and the else.
Quote this message in a reply
Apprentice
Posts: 11
Joined: 2007.01
Post: #13
Thank you for the info, everyone~
Quote this message in a reply
socisub
Unregistered
 
Post: #14
Hi there, this thread matches exactly with what I've been fighting for the last 3 hours. Mad
As I found a different (and easy) solution I thought it would be worth registering and posting it for the community's sake. ( Hello google users that finished here like me! hope this helps ! )

My findings are that there is a way to keep using PNGs while using NSBitmapImageRep. First you build an NSImage with your png, then build an NSBitmapImageRep with the TIFFRepresentation of that image and then as usual extract the bitmapData. It magically works ! And the best, it preserves the png alpha channel for transparency. No CoreGraphics nor external libraries needed. See my code below:


Code:
// aPath is a string url to your png
NSImage *image = [[NSImage alloc] initWithContentsOfFile: aPath];
NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithData:[image TIFFRepresentation]];
unsigned char *data = [bitmap bitmapData];
[image release];
[bitmap release];

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
      [bitmap size].width, [bitmap size].height, 0,
    GL_RGBA, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

I found out how to do this by looking at the GLSLShowpiece example at developer.apple.com. The interesting code is in exhibit.m, look for the method
Code:
NSBitmapImageRep *LoadImage(NSString *path, int shouldFlipVertical
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #15
This is a bit tricky.

That code will probably work fine for a simple app where you control every image that you're going to open. But try passing it various types of images, and it will crash:

* [bitmap size] is measured in points, while [bitmap pixelsWide] and [bitmap pixelsHigh] are really in pixels. Try opening an image with dpi != 72.

* your client format and type are assuming RGBA, UNSIGNED_BYTE. Try opening an image without alpha, or a greyscale image. You can use [bitmap samplesPerPixel] to figure out how many channels there are, and [bitmap bitmapFormat] to see if it is RGBA or ARGB (which unfortunately, may change for the same image depending which OS version you're on.)

* you're assuming the image is tightly packed, i.e. a 256 pixel wide RGBA image has 1024 bytes per scanline. This isn't always true, sometimes the rows are padded. Use [bitmap bytesPerRow] and GL_PACK_ROW_LENGTH to skip padding.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  opengl text/font drawing from CGContext fretmunky 11 13,058 Dec 7, 2008 11:29 AM
Last Post: fretmunky
  OpenGL ES - Drawing a simple cube help. MattCairns 7 12,601 Oct 10, 2008 05:26 PM
Last Post: Frogblast
  Loading textures from bitmaps tehqin 5 4,002 Feb 26, 2007 01:58 AM
Last Post: unknown
  OpenGL, SDL, and Transparent Bitmaps RyanA 4 4,098 Jun 26, 2006 04:26 PM
Last Post: RyanA
  Loading Bitmaps Jones 3 2,593 May 3, 2006 07:57 PM
Last Post: OneSadCookie