Loading 4-channel texture data in iOS - ios

I want to load 4-channel texture data from a file in iOS, so I consider the texture as a (continuous) map
[0,1]x[0,1] -> [0,1]x[0,1]x[0,1]x[0,1]
If I use the fileformat .png, XCode/iOS consider the file as an image, and so multiplies each component rgb with a (premultiplied alpha), corrupting my data. How should I solve this? Examples may be
use two textures with components rgb (3-channel)
postdivide alpha
use another file format
Of these, I consider the best solution to be to use another file format. The GL-compressed file format (PVRTC?) is not Apple-platform independent and seems to be of low resolution (4 bits) (reference).
EDIT:
If my own answer below is true, it is not possible to get the 4 channel data of png's in iOS. Since OpenGL is about creating images rather than presenting images, it should be possible to load 4-channel data in some way. png is a fileformat for images (and compression depends on all 4 channels but compression of one channel is independent of the other channels), so one may argue that I should use another file format. So which other compressed file formats should I use, which is easy to read/integrated in iOS?
UPDATE: "combinatorial" mentioned a way to load 4-channel non-premultiplied textures, so I had to give him the correct answer. However, that solution had some restrictions I didn't like. My next question is then "Access raw 4-channel data from png files in iOS" :)
I think it is a bad library design not making it possible to read 4 channel png data. I don't like systems trying to be smarter than myself.

As you considered PVRTC then using GLKit could be an option. This includes GLKTextureLoader which allows you to load textures without pre-multiplying alpha. Using for example:
+ (GLKTextureInfo *)textureWithContentsOfFile:(NSString *)fileName options:(NSDictionary *)textureOperations error:(NSError **)outError
and passing an options dictionary containing:
GLKTextureLoaderApplyPremultiplication = NO

You can simply request that Xcode not 'compress' your PNG files. Click your project in the top left, select the 'Build Settings', find 'Compress PNG Files' and set the option to 'No'.
As to your other options, postdividing isn't a bad solution but obviously you'll lose overall precision and I believe both TIFF and BMP are also supported. PVRTC is PowerVR specific so it's not Apple-specific but it's also not entirely platform independent and is specifically designed to be a lossy compression that's trivial to uncompress with little input on the GPU. You'd generally increase your texture resolution to ameliorate for the low bit per pixel count.

You should use libpng to load PNG without premultiplied colors.
It is written in C and should compile for iOS.
I've had similar problems with Android and also had to use third-party library to load PNG files with non-premultiplied colors.

This is an attempt to answer my own question.
It is not possible to load non-premultiplied .png files.
The option kCGImageAlphaLast is a valid option, but does not give a valid combination for CGBitmapContextCreate (reference). It is however a valid option for CGImageRef's.
What the build setting COMPRESS_PNG_FILES in XCode mentioned above does, is to convert .png files into some other file format and also multiply the channels rgb with a (reference). I was hoping that disabling this option would make it possible to reach the channel data in my actual .png files. But I am not sure if this is possible. The following example is an attempt to access the .png data at low level, as a CGImageRef:
void test_cgimage(const char* path)
{
CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename(path);
CGImageRef cg_image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, NO,
kCGRenderingIntentDefault);
CGImageAlphaInfo info = CGImageGetAlphaInfo(cg_image);
switch (info)
{
case kCGImageAlphaNone: printf("kCGImageAlphaNone\n"); break;
case kCGImageAlphaPremultipliedLast: printf("kCGImageAlphaPremultipliedLast\n"); break;
case kCGImageAlphaPremultipliedFirst: printf("kCGImageAlphaPremultipliedFirst\n"); break;
case kCGImageAlphaLast: printf("kCGImageAlphaLast\n"); break;
case kCGImageAlphaFirst: printf("kCGImageAlphaFirst\n"); break;
case kCGImageAlphaNoneSkipLast: printf("kCGImageAlphaNoneSkipLast\n"); break;
case kCGImageAlphaNoneSkipFirst: printf("kCGImageAlphaNoneSkipFirst\n"); break;
default: break;
}
}
which gives "kCGImageAlphaPremultipliedLast" with COMPRESS_PNG_FILES disabled. So I think iOS always convert .png files, even at run-time.

There is better solution, faster(about 3x~5x) and cross platform
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
// force 4 channel rgba, force flipy
// image: first pixel is top left, OpenGL assume first is bottom left, so need flipy
bool flipy = true;
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(flipy);
unsigned char *imageData = stbi_load(path.c_str(), &width, &height, &nrChannels, 4);
// load data to your texture
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bytes);
free(imageData);

Not 100% what you want, but I got around the problem using this approach: Put the alpha channel into a separate black & white png and save the original png without alpha. So space taken is about the same. Then in my texture loader load both images and combine into one texture.
I know this is a only a workaround, but at least it gives the correct result. And yes, it is very annoying, that iOS does not allow you to load textures from PNG without premultiplied alpha.

Related

Which program or format to use for texture compression

I have big image textures (4-12MB) and have resized them as much as I can without losing too much detail and would like to further compress them. Online compressors compress the 12.6MB image to 6MB without any problems, but Xcode doesn't support it (even though the compressed image is still .png), apparently because indexed color spaces are not supported for bitmap graphics contexts (according to CGBitmapContextCreate documentation and another post) and the compressed image uses them. If I try to use it I get an error:
CGBitmapContextCreate: unsupported color space.
I was looking through Apple documentation about compression and supported formats, but all I can find is information about how to compress images in code. I want to be able to apply an compressed (or otherwise reduced in size) image in the editor itself and not later in code. Is this possible?
If it is not possible to do this using a program, how should this be done via code in Swift? I found a lot of objective-C posts about this, but none that uses Swift.
Important: iOS does not support device-independent or generic color spaces. iOS applications must use device color spaces instead.
Use CGColorSpaceCreateDeviceRGB as the color space.

How can I convert PNG to GIF keeping the transparency?

How can I convert PNG to GIF keeping the transparency?
I would have hoped that using the Assign( ) method would work but it doesn't seem to migrate transparency. In the GIF, it's represented as black.
png:=TPngImage.Create;
try
png.LoadFromFile(sFile);
// comes from file: png.TransparencyMode;
// comes from file: png.Transparent
// didn't help: gif.Transparent:=true;
gif.Assign(png);
// didn't help: gif.Transparent:=true;
gif.SaveToFile('e:\tmp\out.gif');
finally
png.Free;
end;
I haven't found a way to handle this in Delphi...
thanks!
It is possible to transfer an image from a PNG to a GIF. However, I don't recommend that you do so. The GIF format is substantially less capable than PNG. PNG supports RGBA color channels and partial transparency. GIF uses a 256 color palette and no support for partial transparency.
There are many libraries available that will make the best of a bad job and attempt to produce a GIF image that is close to the PNG image, but information will be lost.
The GIF format dates from the late 1980s and time has moved on. It has long since served its purpose. PNG is modern, capable and well supported. If it is possible to do so you should switch from GIF to using PNG.

iOS - Save UIImage as a greyscale JPEG

In my app, I convert and process images.
from colour to greyscale, then doing operations such as histogram-equalisation, filtering, etc.
that part works fine.
my UIImage display correctly, I also save them to jpeg files and it works.
The only problem is that, although my images are now greyscales, they are still saved as RGB jpegs. that is the red, green and blue value for each pixel are the same but it still waste space to keep the duplicated value, making the file size higher than it could be.
So when i open the image file in photoshop, it is black & white but when I check "Photoshop > Image > Mode", it still says "RGB" instead of "Greyscale".
Anyone know how to tell iOS that the UIImageJPEGRepresentation call should create data with one channel per pixel instead of 4?
Thanks in advance.
You should do an explicit conversion of your image using CGColorSpaceCreateDeviceGray() as color space which is 8 bits per component, 1 channel.

OpenGL ES, how do you render PVR textures?

Edit:ok, sorry, I had a simple programming error, is there a way to delete this question?
I have some compressed textures that are PVR files, but I cant seem to draw them in my iPad application using OpenGL ES.
I can draw PNG files just fine, I know the PVR files are being loaded correctly.
Are there some special OpenGL draw functions that I need to be calling to draw the PVR files?
Edit:All I get is a white image.
Any info is appreciated.
Drawing PVRTC textures should be exactly the same as any other texture format - it looks more likely that your loading code is the problem. Are any GL errors being reported during loading?
The major difference to loading uncompressed textures are in the line:
glCompressedTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, width, height, 0, size, data);
or
glCompressedTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, width, height, 0, size, data);
Make sure that you're not setting a GL filter mode to use MIPmaps if they're not in the texture as well.
Searching for PVRTC in Apple's docs brings up a decent summary of how to use these textures.
After upload, PVR textures are no different from other formats. Did you forgot to skip header during data upload, or used wrong parameters for glCompressedTexImage2D? It is even possible that compression tool was unable to convert images because of wrong size or color format.

Why '.png' files produced by ImageMagick are so much bigger than '.jpg' & '.gif' files? [duplicate]

This question already has answers here:
What are the different usecases of PNG vs. GIF vs. JPEG vs. SVG? [closed]
(14 answers)
Closed 9 years ago.
I'm using ImageMagick to convert some files from one format to another. I was always under the impression that .png files were supposed to be as big/small as .jpg if not smaller, and definitely smaller than .gif.
However when I run
convert photo.jpg photo.png
The files I'm getting out is about 6 times bigger than the original jpg.
Original jpg is a regular photo about 300x500 px, 52 kb. Output is a proper png of the same dimensions, but size is about 307 kb?
Does anyoone know what the hack is going on? Am I doing something wrong?
P.S.:
I tried both on Debian and Windows with the same results.
P.P.S.:
Also when I add resize option to this and resize to 10000 x 10000. Converting and resizing to jpg takes a few seconds, but it works, if I do the same of png, I jsut strt running out of memory altogether
P.P.P.S.:
For people who keep marking this question as duplicate of PNG vs. GIF vs. JPEG vs. SVG - When best to use? . Please read carefully and understand the question. It's not a duplicate, since this question asks' about files produced programmatically with specific application (image magick). The question you marking as duplicate, is asking which image format is better to use on the web. Two DIFFERENT questions.
The key thing to note here is that each image file format is best for specific purposes.
JPEG stands for “Joint Photographic Experts Group.” That right there should tell you that the .JPG file format is optimized for photographs. It doesn’t work so well for line-art, logos, gradient or tiled / patterned backgrounds, and the like. .JPG’s DCT (Discreet Cosine Transform) artifacts (the “blocky” artifacts we all know and loathe) are much more noticeable with line art and logos than with photos.
PNG has replaced GIF for everything that GIF was ever good for, except one, and that only because no clear standard has emerged: animations. Animated .GIFs are well known on the Web. There are two competing standards for animated .PNGs: APNG and MNG.
APNG is supported in most modern browsers, and is also fully backwards compatible (the file extension is .PNG, not .APNG, and if any program that can display .PNGs but doesn’t know APNG is “fed” an APNG, it will display either a stand-in image of the developer’s choice, or the first frame of the animation if no such stand-in was provided — as far as the older program is concerned, it’s just an ordinary single-frame .PNG with that one image in it — the rest is safely ignored). Gecko (FF, etc.) and Presto (Opera) support it natively, and Google Chrome (using Webkit) can with an add-on.
MNG has the backing of the actual PNG-format development team, but is its own format and is not backwards compatible, but is more powerful and flexible. Right now, only KHTML-based browsers (Konquerer) support it: not Trident (IE), Gecko, Webkit (Chrome, Chromium, Safari), nor Presto.
PNG does everything (except animations unless enhanced with APNG) that GIF does, and better. All else being equal, a .PNG will almost always be smaller than a .GIF at the same resolution and bit depth. Like .GIF, .PNG can support color depths up to 8 bits per pixel in indexed-color (paletted) mode, but unlike .GIF (yet like .JPEG) it also supports direct-color mode at 24 bits per pixel.
In either mode it can add 8 bits of alpha transparency, unlike .GIF (which can only do indexed color transparency [pick a color out of the palette to be replaced with 100% transparency, aka invisibility] — .PNG can do that, too). Alpha transparency produces much better results than indexed transparency, because the pixels can be partially transparent, whereas with indexed transparency (the only kind available in .GIF) your choices are either opaque or invisible. This makes for “halos” around non-rectangular objects when placed against background colors other than the one the .GIF or indexed .PNG was originally “matted” against. It also inhibits being able to do effects such as glows, drop shadows, and, of course, see-through colored objects (without dithering). Alpha transparency can do all of those things with ease, against almost any background (glows would be largely invisible on a white background, and drop shadows would be invisible on black, but you know what I mean).
Yes, you can do 8-bit alpha transparency in an indexed-color .PNG! And guess what? Even Microsoft Internet Explorer 6 can display those just fine, complete with transparency! It’s only the 32-bit .PNGs (24-bit RGB color + 8-bit alpha) that IE6 choked on and displayed as gray!
The most well-known program that can export PNG8 (indexed color) with alpha transparency is Adobe (formerly Macromedia) Fireworks. The Photoshop “Save for Web and Devices” (at least as of CS3) could not do that, despite having basically lifted the feature from Fireworks when Adobe bought out Macromedia. It can save PNG8, but only with indexed-color transparency.
Anyway, full 32-bit (or even 24-bit) PNGs will be pretty large, though usually much smaller than the nearest equivalent .BMP or .TGA or uncompressed .TIFF or some such (unless you’re trying to do a photograph with it — that’s what JPEG is for!). It will usually be somewhat smaller even than .RLE (losslessly compressed .BMP) or losslessly compressed .TIF, all else being equal.
Unlike most of these other formats, PNG also supports 48-bit RGB color, with optional 16 bits of alpha transparency, for extremely high quality (much higher than most monitors can display). These are best used as an intermediate storage format, to retain information from a high bit depth scanner or camera (RAW mode) or some such. Their file sizes would be quite large, despite the lossless compression.
One thing that .PNG cannot currently do is handle non-RGB color spaces such as CMYK or L*a*b.
In short:
For photographs, use .JPG.
For line art and logos of limited color, use indexed-color .PNG (PNG8), with alpha transparency if needed.
For line art and logos of extensive color (e.g. lots of gradient fills, metallic chrome-type reflection effects, etc.), use direct-color .PNG (PNG24) with alpha if needed, if you want the best quality or need transparency (and don’t mind it not working in IE6 or use one of a variety of IE6 workarounds for transparent PNGs), and don’t mind the larger files and bandwidth usage. Otherwise, use .JPG, but be aware that the quality will be degraded. You may need to crank the JPEG quality up pretty high, especially for logos or other graphics with “text” in them, which would reduce your file size savings.
For non-Flash/Silverlight/video/HTML5 Canvas animations, .GIF if the main choice at present, but be prepared to switch to APNG (I don’t think MNG is going to beat it, despite the more official support from the JPEG developers).
JPG is a lossy compression algorithm while PNG is a lossless one.
This fact alone will (in general) make JPG images smaller than PNG ones. You can tweak the compression ratios for each format, so it could also be that you're not compressing your PNG files as much as your JPG ones.
For a photographic image saving as JPG will usually produce a smaller file than PNG as there's more noise or randomness in the image for the compression to work with. Images created by graphic art tools will tend to have more hard edges and areas of solid colour which will compress better in PNG.
If you have text in your image then PNG is going to produce a better quality image as the harder edges of the characters won't be blurred like they would be if JPG is used.
GIF is smaller because it's based on an colour palette (of 256 colours) rather than the separate RGB values for each pixel (or group of pixels) in JPG and PNG.
The PNG format can provide images of 24 bits per pixel or 8 bits per pixel. JPG is a 24 bit format, but it uses lossy compression to reduce the file size significantly. PNG and GIF both use lossless compression, but GIF only works with 8 bits so it requires your image to have 256 colors or less - this often results in a grainier picture.
Try the -colors and -dither options in ImageMagick to reduce the number of bits in your PNG output. At that point it should be comparable to the GIF file size. If you need to reduce it further, there are utilities to optimize the PNG.
The .jpeg file format is a lossy format, it throws information away. Which makes for good compression ratios. The .gif file format is not lossy but loses other information, it only supports 256 colors. The .png file format is not lossy and preserves the color range.
It depends on the kind of image. JPEG's lossy compression works very well on images with lots of color gradients (i.e. photos).
Try it on an image consisting of areas with the same color separated by sharp edges (screenshots are good, unless they show a photo desktop background, and the fancy window borders and task bar of Vista and Windows 7 also tend to mess this up). JPEG is bad at that kind of thing, and you'll probably find PNG to compress better (probably better than GIF too).
Replying to :
Does anyone know what the hack is going on? Am I doing something wrong?
yeah I study this, you aren't doing nothing wrong, you produced 8-bit/color RGB PNG
I solve it with reduce or compress png with pngnq ( http://pngnq.sourceforge.net/ ) that produce 8-bit/color colormap PNG.
pngnq seems to me the most improved project, since is a fork of others like pngquant etc, and is normally in main stream of Linux distros so I just do this:
convert photo.jpg png:- | pngnq -s 1 > photo.png
For the last upvoter (2016): pngnq as been deprecated in favor of pngquant (https://github.com/pornel/pngquant)

Resources