4
\$\begingroup\$

I am making a game in which I use the alpha value, in some cases for transparency, in others for occlusion, and for HDR in others.

I'm loading the images like this (this is MonoTouch, but objective-c can be easily converted to c#):

CGImage bitmap = new UIImage(resourcePath).CGImage;
width = bitmap.Width;
height = bitmap.Height;
IntPtr pixels = Marshal.AllocHGlobal(bitmap.Width * bitmap.Height * 4);
using (var context = new CGBitmapContext(pixels, bitmap.Width, bitmap.Height, 8, bitmap.Width * 4, CGColorSpace.CreateDeviceRGB(), CGImageAlphaInfo.PremultipliedLast))
{
    var dest = new RectangleF(0, 0, bitmap.Width, bitmap.Height);
    context.ClearRect(dest);
    context.DrawImage(dest, bitmap);
    GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, pixels);
}
Marshal.FreeHGlobal(pixels);
bitmap.Dispose();

However, this butchers all my pixels by premultiplying them by the alpha value.

The reason behind this is that I'm creating CGBitmapContext with CGImageAlphaInfo.PremultipliedLast. I tried with CGImageAlphaInfo.Last, but creating the context fails. Core Graphics only works with premultiplied alpha, as explained here.

I tried using CGDataProviderCopyData, as well as pngcs. However, they both simply decode the bitmap, and don't convert the pixels to RGBA, which is relevant when you're storing 8-bit, 24-bit PNGs, or indexed PNGs (I have lots of these, which are the result of optimizing my PNG files for size).

So I'm lost. I need a way to decode PNG files in a way that they are converted to RGBA without premultiplying my colors by the alpha value. Is that too much to ask? GDI, GDI+, System.Drawing, DirectX, Mono for Android as well as PSM do it just fine...

Also, why does iOS development has to be so picky?

\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

I tried using CGDataProviderCopyData, as well as pngcs. However, they both simply decode the bitmap, and don't convert the pixels to RGBA, which is relevant when you're storing 8-bit, 24-bit PNGs, or indexed PNGs.

Did you look at ImageLineHelper.Palette2rgb()? What functionality are you missing?

(disclaimer: I'm the PngCs developer)

\$\endgroup\$
5
  • \$\begingroup\$ Well, I could of course process all fifteen possible PNG pixel formats and convert them to 32 bit non-premultiplied RGBA. It would however be a minor pain to create files with all possible formats and then test them. Since you're the PngCS dev, I would like to propose you add a helper class/function that decodes the entire file into memory (and optimally converts it to a user-defined pixel format) instead of having to do it line by line. Line-by-line is certainly useful, but IMO only for very specific applications with huge files or very little memory. \$\endgroup\$ Commented Jan 23, 2013 at 2:25
  • \$\begingroup\$ Certainly, I'm open to proposals. But 1) to load the full image as a matrix instead of a series of rows takes three lines of code 2) to convert to a format like "32 bit RGBA" is quite easy also, what is difficult is to provide a general helper function, because there are potentially tons of user defined pixel formats, and strategies (what to do with indexed images, with low-depth or high-depth images, etc). I might consider implementing a particular convertion function (RGBA32) with some ad-hoc strategy, that could serve as template. \$\endgroup\$
    – leonbloy
    Commented Jan 23, 2013 at 2:41
  • \$\begingroup\$ That's precisely my point. I would prefer a general solution, at least for all standard pixel formats. As a library user I appreciate it when it allows me to do the most common thing (in this case loading a whole image into memory) in just one line, which is exactly one third of what you propose (when I did it, it took me like 8 or 9 lines though, but certainly the author can do a better job than somebody who just downloaded the library and tried to make it work). It's just a suggestion to make the library more user-friendly, but feel free to disregard it if it doesn't align with your views. \$\endgroup\$ Commented Jan 23, 2013 at 3:22
  • \$\begingroup\$ "for all standard pixel formats" And these would be...? Bear in mind that PngCs/PngJ is a general low level PNG library, uncoupled from high level/platform-specific image classes (.Net, IOS, Android). I'm willing to provide a method that returns the full image as a matrix of bytes or integers (perhaps preallocated) in some format but : 1) which format or formats would you suggest? (I'm doubting among bytes ARGB, integer packed ARGB32 or RGBA32...) and 2) are you sure that this would not require another allocation+copy to fill the high level image class? \$\endgroup\$
    – leonbloy
    Commented Jan 23, 2013 at 17:13
  • \$\begingroup\$ I am not a PNG expert, so I am most likely wrong; but as I understand the IHDR chunk, there are five different color types defined, and considering the allowed bit depths for each color type, I can count 15 different pixel formats. Now, if you convert the pixels as you read them, instead of reading the whole image and then converting it, you do everything in a single pass, so you can save the allocations for the raw data, which may be interesting. As for the converstion format, I think 32-bit ARGB is the most common, but ABGR, RGBA, BGRA, XRGB and so on may also be interesting. \$\endgroup\$ Commented Jan 23, 2013 at 19:23
0
\$\begingroup\$

C code:

CFURLRef url = CFURLCreateWith...("path/to/file.png");
CGDataProviderRef dp = CGDataProviderCreateWithURL(url);
CGImageRef img = CGImageCreateWithPNGDataProvider(dp, nil, NO, kCGRenderingIntentDefault);
CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(img));
UInt8* pixelData = CFDataGetBytePtr(data);  // not premultiplied RGBA8 data
// use pixelData

CFRelease(data);
CGImageRelease(img);
CGDataProviderRelease(dp);
CFRelease(url);
\$\endgroup\$
2
  • \$\begingroup\$ While a block of code may solve the issue, we like to see a description of what is the train of though or an explanation behind these blocks of code. \$\endgroup\$
    – Vaillancourt
    Commented Mar 24, 2016 at 14:15
  • \$\begingroup\$ This did not work for me. The alpha channel is pre-multiplied into the data regardless of the value of rendering intent. \$\endgroup\$ Commented Jun 24, 2022 at 23:59

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .