Pixel bug in Mac OS X |
pixelman
|
Hey all.
I'm working on a game on Mac OS X (Snow Leopard, SDL 1.2.14). I'm using PNG images (created in Gimp) to store the levels and then build the level based on each pixel's color -- the problem is the colors I'm getting are slightly inaccurate, which is breaking my program. I'm using the getpixel() method from the SDL website. Each color in the image is off by just a few integers. For example (126, 126, 180) is being read as (127, 127, 178). Also, for the record, I took a screenshot of my program and compared it to the colors to my sprites in Gimp, and the screenshot was slightly off as well. I think I have an idea of what's going on though. When I change the level image to a .BMP, the pixels are read 100% accurately, so I'm assuming it's just a bug in SDL_image. I did a little digging, and I found this: http://playcontrol.net/ewing/jibberjabber/big_behind-the-scenes_chang.html#SDL_image "We are changing the backend of SDL_image to use Apple's ImageIO API" I had this exact same problem when using ImageIO a few months ago. I can't remember exactly how I was doing it before, but when I load images like below the pixels are read correctly: - (CGImageRef)loadImageFromFile:(NSString *)pathToResource { NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; [[NSFileManager defaultManager] changeCurrentDirectoryPath:resourcePath]; NSURL* url = [NSURL fileURLWithPath:pathToResource]; CGImageRef imageRef = NULL; CGImageSourceRef sourceRef; sourceRef = CGImageSourceCreateWithURL((CFURLRef)url, NULL); if(sourceRef) { imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, NULL); CFRelease(sourceRef); } else { NSLog(@"Failed to load image\n"); } return imageRef; } // Example getting the pixel data (it's read-only though -- figures this is how Apple recommends it) CGImageRef imageRef = [loadImageFromFile:@"example.png"]; CFDataRef imageDataRef = CGDataProviderCopyData(CGImageGetDataProvider(imageRef)); const UInt8 *pixelData = CFDataGetBytePtr(imageDataRef); Any chance someone could fix this? BMPs are so huge. :) Cheers, - pixelman |
|||||||||||
|
Pixel bug in Mac OS X |
eclectocrat
|
This is a persistent problem with OSX, I have the same issue with any program that uses Apple's Image IO, such as Pixen or Seashore. Just on a whim, you might want to take a look at the Color Space (NSColorSpace, CGColorSpace?) settings, or the Color Calibration settings. Alternatively, download a different image loading library like LodePNG, a single source file library for PNG access, it works great for me.
Good luck. On Wed, Sep 21, 2011 at 9:53 AM, pixelman wrote:
|
|||||||||||||
|
Pixel bug in Mac OS X |
Ryan C. Gordon
Guest
|
We should just disable this, because it constantly causes three problems: - Slightly different colors when you need pixel-perfect decoding. - No 8-bit support (which is bad when you use an 8-bit grayscale .png as, say, a heightmap, and actually wanted an 8-bit surface from SDL_image). - The first two problems are completely non-obvious to track down when you realize you have a problem, and a pain to resolve, because now you have to build your own SDL_image at a minimum. The benefits to using ImageIO are a smaller compiled library, no need for us to manage the external dependencies, and no need to worry about updating the dependencies for security issues. I'm not sure this is the _worst_ thing, though. Eric Wing, if you're around: can the color thing be resolved? Maybe with an NSColorSpace or something? For the other issue, I imagine that, for correctness, we can convert the final SDL_Surface to 8-bit if the original PNG is 8-bit too. Maybe? --ryan. _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||
|
Pixel bug in Mac OS X |
pixelman
|
@Jeremy: Haha trying to write a pixel art editor on the Mac is a nightmare. o_O
I'm reading the SDL_image source right now, and I see in Create_SDL_Surface_From_CGImage they're creating a CGBitmapContext with the surface->pixels and drawing the loaded image to the context. I'm 99% sure this is what I was doing before and getting headaches over. Check this out: http://developer.apple.com/library/mac/#qa/qa1509/_index.html
So basically, it is possible to get the unaltered pixel data directly from the image with ImageIO. I think the alpha-premultiplication is what's screwing everything up -- would it be possible to copy the raw pixel data into the SDL_Surface? - pixelman On Wed, Sep 21, 2011 at 1:04 AM, Ryan C. Gordon wrote:
|
|||||||||||||||||
|
Pixel bug in Mac OS X |
Eric Wing
Guest
|
Sorry all. I'm still around, but just been really busy.
I'm going from distant memory here. I thought I recall calling some memory calibration stuff in the code already. I think I was unsure which way to go so I think there may have been two lines, one commented out to go the other way. I thought the current way would not do color calibration because I thought that would alter the pixel values. I recall people were having problems with premulitplied alpha related issues. Was it Sam that added the code to 'fix' that? Is this always run? Are these cases orthogonal? Maybe this can be skipped when not needed and thus avoiding any accidental pixel color changes? I never did figure out grayscale, but I never deeply pursued this. Maybe some ambitious person would like to ask on the Quartz mailing list or something or if they have a spare DTS incident to throw away... As a workaround for grayscale, I was thinking it could be done the brute force way. Check the bitdepth for 8-bit and then go through all the pixels after blitting to 24/32-bit and check for r==g==b. If it passes the check, create a SDL_Surface that looks like what you might get from the other IMG_Load backends. -Eric On 9/21/11, pixelman wrote:
-- Beginning iPhone Games Development http://playcontrol.net/iphonegamebook/ _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||||||||
|
Pixel bug in Mac OS X |
pixelman
|
Hola,
For convenience, here's the code where the CGImage is copied into the SDL_Surface->pixels http://pastebin.com/6NJ19HP0 Like I said before, I think the inaccuracy happens when the CGImage is drawn into a CGContext, because I've had this same issue before. If you read the raw pixel data of a loaded CGImage (without changing it in any way) then the pixel data is accurate. - pixelman On Fri, Sep 30, 2011 at 1:38 PM, Eric Wing wrote:
|
|||||||||||||||||||||||||||||||
|
Pixel bug in Mac OS X |
Eric Wing
Guest
|
This is a quick experiment to see if CGDataProviderCopyData helps
solve the problem any. My code is not endian clean and I don't know what it is going to do about alpha. No greyscale either. I only tested with two basic images, a png and jpg. In my specific png case, my CGImageGetBitmapInfo always came out as 0. I was kind of expecting some info about endianess. Anyway, can you test the following function by replacing the one in the implementation to see if it improves the pixel value problem any. If so, maybe we can can fix/finish this implementation, though I'm not sure what to do about Tiger (10.4) since this is only 10.5+. -Eric // Once we have our image, we need to get it into an SDL_Surface static SDL_Surface* Create_SDL_Surface_From_CGImage(CGImageRef image_ref) { /* This code is adapted from Apple's Documentation found here: * http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/index.html * Listing 9-4††Using a Quartz image as a texture source. * Unfortunately, this guide doesn't show what to do about * non-RGBA image formats so I'm making the rest up. * All this code should be scrutinized. */ size_t w = CGImageGetWidth(image_ref); size_t h = CGImageGetHeight(image_ref); CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image_ref); SDL_Surface* surface; Uint32 Amask; Uint32 Rmask; Uint32 Gmask; Uint32 Bmask; if (alpha == kCGImageAlphaNone || alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast) { Amask = 0x00000000; } else { Amask = 0xFF000000; } Rmask = 0x00FF0000; Gmask = 0x0000FF00; Bmask = 0x000000FF; surface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, Rmask, Gmask, Bmask, Amask); if (surface) { /* CGDataProviderCopyData will give us a copy of the raw data */ CFDataRef data_copy = CGDataProviderCopyData(CGImageGetDataProvider(image_ref)); const UInt8* data_byte_ptr = CFDataGetBytePtr(data_copy); Uint8 *p = (Uint8 *)surface->pixels; size_t i; size_t j; size_t width = CGImageGetWidth(image_ref); size_t height = CGImageGetHeight(image_ref); size_t bpr = CGImageGetBytesPerRow(image_ref); size_t bpp = CGImageGetBitsPerPixel(image_ref); size_t bpc = CGImageGetBitsPerComponent(image_ref); size_t bytes_per_pixel = bpp / bpc; CGBitmapInfo info = CGImageGetBitmapInfo(image_ref); fprintf(stderr, "\n" "CGImageGetHeight: %d\n" "CGImageGetWidth: %d\n" // "CGImageGetColorSpace: %@\n" "CGImageGetBitsPerPixel: %d\n" "CGImageGetBitsPerComponent: %d\n" "CGImageGetBytesPerRow: %d\n" "CGImageGetBitmapInfo: 0x%.8X\n" " kCGBitmapAlphaInfoMask = %s\n" " kCGBitmapFloatComponents = %s\n" " kCGBitmapByteOrderMask = %s\n" " kCGBitmapByteOrderDefault = %s\n" " kCGBitmapByteOrder16Little = %s\n" " kCGBitmapByteOrder32Little = %s\n" " kCGBitmapByteOrder16Big = %s\n" " kCGBitmapByteOrder32Big = %s\n", (int)width, (int)height, // CGImageGetColorSpace(image_ref), (int)bpp, (int)bpc, (int)bpr, (unsigned)info, (info & kCGBitmapAlphaInfoMask) ? "YES" : "NO", (info & kCGBitmapFloatComponents) ? "YES" : "NO", (info & kCGBitmapByteOrderMask) ? "YES" : "NO", (info & kCGBitmapByteOrderDefault) ? "YES" : "NO", (info & kCGBitmapByteOrder16Little) ? "YES" : "NO", (info & kCGBitmapByteOrder32Little) ? "YES" : "NO", (info & kCGBitmapByteOrder16Big) ? "YES" : "NO", (info & kCGBitmapByteOrder32Big) ? "YES" : "NO" ); switch(bytes_per_pixel) { case 3: { for(i = 0, j=0; i < CFDataGetLength(data_copy); i += 3, j+=4) { p[j] = data_byte_ptr[i+2]; p[j+1] = data_byte_ptr[i+1]; p[j+2] = data_byte_ptr[i]; } break; } case 4: { // Take away the red pixel, assuming 32-bit RGBA for(i = 0; i < CFDataGetLength(data_copy); i += 4) { p[i] = data_byte_ptr[i+2]; p[i+1] = data_byte_ptr[i+1]; p[i+2] = data_byte_ptr[i]; p[i+3] = data_byte_ptr[i+3]; } break; } } CFRelease(data_copy); } return surface; } _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
|||||||||||
|