Loading an uncompressed DDS to GL texture - opengl

I've this DDS file. I wrote a simple DDS reader to read a DDS header and print its details based on the MSDN specification. It says that the it's an RGB DDS with 32 bytes per pixel bit depth and the alpha is ignored i.e. pixel format is X8R8G8B8 (or A8R8G8B8). To verify this, I also opened this file in a hex editor which shows the first (i.e. from the data start) 4 bytes as BB GG RR 00 (replace them with the first pixel's right hex colour values). I read that OpenGL's texture copy functions act on bytes (atleast conceptually) and thus from its viewpoint, this data is B8G8R8A8. Please correct me if my understanding is wrong here.
Now to glTexImage2D internal format I pass RGBA8 and to external format and type I pass BGRA and UNSIGNED_BYTE. This leads to a blue tint in the rendered output. In my fragment shader, just to verify, I did a swizzle to swap R and B and it renders correctly.
I reverted the shader code and then replaced the type from UNSIGNED_BYTE with UNSIGNED_INT_8_8_8_8_REV (based on this suggestion) and it still renders the blue tint. Now changing the external format to RGBA and with either type (UNSIGNED_BYTE or UNSIGNED_INT_8_8_8_8_REV) it renders fine!
Since OpenGL doesn't support ARGB, giving BGRA is understandable. But how come RGBA is working correctly here? This seems wrong.
Why does the type have no effect on the ordering of the channels?
Does the GL_UNPACK_ALIGNMENT have a bearing in this? I left it as the default (4). If I read the manual right, this should have no effect on how the client memory is read.
Details
OpenGL version 3.3
Intel HD Graphics that supports upto OpenGL 4.0
Used GLI to load the DDS file and get the data pointer

I finally found the answers myself! Posting it here so that it may help someone in future.
Since OpenGL doesn't support ARGB, giving BGRA is understandable. But how come RGBA is working correctly here? This seems wrong.
By inspecting the memory pointed to by void* data that GLI returns when a pointer to the image's binary data is asked for, it can be seen that GLI had already reordered the bytes when transferring data from the file to client memory. The memeory window shows, from lower to higher address, data in the form RR GG BB AA. This explains why passing GL_RGBA works. However, the wrong on GLI's part is that when external format is queried for it returns GL_BGRA instead of GL_RGBA. A bug to address this has been raised.
Why does the type have no effect on the ordering of the channels?
No, it has an effect. The machine that I'm trying this experiment on is an Intel x86_64 little endian machine. OpenGL Wiki clearly states that the client pixel data is always in client byte ordering. Now when GL_UNSIGNED_BYTE or GL_UNSIGNED_INT_8_8_8_8_REV is passed, the underlying base type (not the component type) is an unsigned int for both; thus reading an int from data, on a little-endian machine would mean, the variable in register would end up with the bytes swapped i.e. RR GG BB AA in the RAM would reach the VRAM asAA BB GG RR; when addressed by a texture of type RGBA (RR GG BB AA), reading AA would actually give RR. To correct it, the OpenGL implementation swaps the bytes to neutralise the endianness of the machine, in the case of GL_UNSIGNED_BYTE type. While for GL_UNSIGNED_INT_8_8_8_8_REV we explicitly instruct OpenGL to swap the byte order and thus it renders correctly. However, if the type is passed as GL_UNSIGNED_INT_8_8_8_8 then the rendering is screwed up, since we instruct OpenGL to copy the bytes as it was read on the machine.
Does the GL_UNPACK_ALIGNMENT have a bearing in this? I left it as the default (4). If I read the manual right, this should have no effect on how the client memory is read.
It does have a bearing on the unpacking of texture data from client memory to server memory. However, that's to account for the padding bytes present in an image's rows to compute the stride (pitch) correctly. But to this issue specifically it doesn't have a bearing since it's pitch flag is 0 i.e. there're no padding bits in the DDS file in question.
Related material: https://www.opengl.org/wiki/Pixel_Transfer#Pixel_type

Related

Can I rely on SDL_Surface::pitch being constant?

I'm working on a project which utilises SDL 1.2.15. The application constructs a SDL_Surface whose frame buffer is then retreived via getDisplaySurface()->pixels and sent via serial line.
I learned, that the pixel buffer pointed to by SDL_Surface::pixels is not necessarily continuous. The byte sequence might be interrupted by blocks of data which are not part of the visible image area.
That means the image is of size 320×240, but the pixel buffer could be of size, let's say, 512×240. (I imagine speedups possible due to memory alignment could be a valid reason. That's just my assumption which is not backed by actual knowledge, though.)
Question:
In my case, I happen to be lucky and the the pixel buffer has exactly the dimensions of my image. Can I trust that the pixel buffer dimensions wouldn't change?
That way I could just send the pixel buffer content to the serial interface and don't have to write code dealing with removal of those invalid blocks.
SDL uses 4-byte alignment for rows. It also matches OpenGL's default alignment.

libjpeg/libjpeg-turbo RGBA/32-bit int decompression

When using libjpeg to feed images into OpenCL, to be able to treat channels as normalized uint8's with CL_UNORM_INT8 (floats in the range [0.0, 1.0]), you can only feed it buffers with 4 channel components. This is problematic, because libjpeg only outputs 3 (by default in RGB order) since JPEG has no notion of opacity.
The only workaround I see is to scanlines with libjpeg and then make a duplicate buffer of the appropriate length (with the fourth channel component added for each pixel in the scanlines) and then memcpy the values over, setting the alpha component to 255 for each. You could even do this in place if you are tricky and initialize the buffer to be of row_stride * 4 initially and then walk backwards from index row_stride * 3 - 1 to 0, moving components to the proper places in the full buffer (and adding 255 for alpha where necessary).
However, this feels hacky and if you're dealing with large images (I am), it's unacceptable to have this extra pass over (what will be in aggregate) the entire image.
So, is there a way to get libjpeg to just extend the number of components to 4? I've tried setting properties on cinfo like output_components to no avail. I've read that the only workaround is to compile a special version of libjpeg with the constant RGB_COMPONENTS = 4 set in jmorecfg.h, but this certainly doesn't feel portable or for that matter necessary for such a (common) change of output.
So it turns out that the best solution (at least, the one that doesn't require any custom builds of libs or extra passes through the buffer) is to use libjpeg-turbo. As of 1.1.90 they provide a colorspace constant JCS_EXT_RGBX that adds a fake alpha channel. To my knowledge this is only documented in the release notes of a beta version on SourceForge so barring that this URL changes or no longer exists (read: the internet revolts against sf for its shady insertion of code into "inactive" popular repos and they are forced to shut down), here is the relevant bit reproduced:
When decompressing a JPEG image using an output colorspace of
JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR, or JCS_EXT_XRGB, libjpeg-turbo will
now set the unused byte to 0xFF, which allows applications to interpret that
byte as an alpha channel (0xFF = opaque).
Note that this also allows for alternate orderings such as BGR should you need them.
To use it after your jpeg_read_header() call (because this call sets a member on cinfo we need to a default) but before your jpeg_start_decompress() call (because it uses the value of this member), add:
cinfo.out_color_space = JCS_EXT_RGBX; // or JCS_EXT_XRGB, JCS_EXT_BGRX, etc.
And now scanning lines during the decompress will return an extra fourth component for each pixel set to 255.

Does a conversion take place when uploading a texture of GL_ALPHA format (glTexImage2D)?

The documentation for glTexImage2D says
GL_RED (for GL) / GL_ALPHA (for GL ES). "The GL converts it to floating point and assembles it into an RGBA element by attaching 0 for green and blue, and 1 for alpha. Each component is clamped to the range [0,1]."
I've read through the GL ES specs to see if it specifies whether the GPU memory is actually 32bit vs 8bit, but it seems rather vague. Can anyone confirm whether uploading a texture as GL_RED / GL_ALPHA gets converted from 8bit to 32bit on the GPU?
I'm interested in answers for GL and GL ES.
I've read through the GL ES specs to see if it specifies whether the GPU memory is actually 32bit vs 8bit, but it seems rather vague.
Well, that's what it is. The actual details are left for the actual implementation to decide. Giving such liberties in the specification allows actual implementations to contain optimizations tightly tailored to the target system. For example a certain GPU may cope better with a 10 bits per channel format, so it's then at liberty to convert to such a format.
So it's impossible to say in general, but for a specific implementation (i.e. GPU + driver) a certain format will be likely choosen. Which one depends on GPU and driver.
Following on from what datenwolf has said, I found the following in the "POWERVR SGX
OpenGL ES 2.0 Application Development Recommendations" document:
6.3. Texture Upload
When you upload textures to the OpenGL ES driver via glTexImage2D, the input data is usually in linear scanline
format. Internally, though, POWERVR SGX uses a twiddled layout (i.e.
following a plane-filling curve) to greatly improve memory access
locality when texturing. Because of this different layout uploading
textures will always require a somewhat expensive reformatting
operation, regardless of whether the input pixel format exactly
matches the internal pixel format or not.
For this reason we
recommend that you upload all required textures at application or
level start-up time in order to not cause any framerate dips when
additional textures are uploaded later on.
You should especially
avoid uploading texture data mid-frame to a texture object that has
already been used in the frame.

Creating DirectDraw Surface from scratch in c++

I'm trying to convert a 2d array to a DDS and saving it to a file. Array is full of Color structs (each having a red, green, blue and alpha component). Once I get the array to the correct format, I'm sure the saving it to file part won't be a problem.
I'm fine with either using a lib for this (as long as its license allows me to use it in a closed source project and works on both Linux and Windows) or doing it manually, if I can find a nice resource explaining how to do it.
If anyone can point me in the right direction, I'd really appreciate it.
In DirectDraw you can create a surface from the data in memory, by setting up certain fields in the DDSURFACEDESC structure and passing it to the CreateSurface method of the IDirectDraw interface.
First you need to tell DirectDraw which fields of the DDSURFACEDESC structure contain the correct information by setting the dwFlags field to the following set of flags: DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_LPSURFACE | DDSD_PITCH.
Oh, and this only works for system-memory surfaces, so it's probably needed to add the DDSCAPS_SYSTEMMEMORY flag in the ddsCaps.dwCaps field (if DirectDraw won't do it by default).
Then you specify the address of the beginning of your pixel data array in the lpSurface field. If your buffer is continuous, just set the lPitch to 0. Else you set the correct pitch there (the distance in bytes between the beginnings of two subsequent scanlines).
Set the correct pixel format in ddpfPixelFormat field, with correct bit depth in dwRGBBitCount and RGB masks in dwRBitMask, dwGBitMask and dwBBitMask.
Then set the lXPitch to the number of bytes your pixel has (3 for RGB). It depends on the pixel format you use.
Then pass the filled structure into CreateSurface and see if it works.
When you create the surface this way, keep in mind that DirectDraw will not manage its data buffer himself, and won't free this memory once you call Release on your surface. You need to free this memory yourself when it's no longer used by the surface.
If you want this pixel data to be placed in video memory, on the other hand, you need to create an offscreen surface in a usual way and then lock it, copy your pixels to its own buffer in video memory (you'll find its address in the lpSurface field, and remember to take lPitch in count!), and then unlock it.

Reading a BMP into memory using the correct structures

I'm currently doing a steganography project (for myself). I have done a bit of code already but after thinking about it, I know there are better ways of doing what I want.
Also - this is my first time using dynamic memory allocation and binary file I/O.
Here is my code to hide a text file within a BMP image: Link to code
Also note that I'm not using the LSB to store the message in this code, but rather replacing the alpha byte, assuming its a 32 bit per pixel (bbp) image. Which is another reason why this won't be very flexible if there are 1, 4, 8, 16, 24 bpp in the image. For example if it were 24 bbp, the alpha channel will be 6 bits, not 1 byte.
My question is what is the best way to read the entire BMP into memory using structures?
This is how I see it:
Read BITMAPFILEHEADER
Read BITMAPINFOHEADER
Read ColorTable (if there is one)
Read PixelArray
I know how I to read in the two headers, but the ColorTable is confusing me, I don't know what size the ColorTable is, or if there is one in an image at all.
Also, after the PixelArray, Wikipedia says that there could be an ICC Color Profile, how do I know one exists? Link to BMP File Format (Wikipedia)
Another thing, since I need to know the header info in order to know where the PixelArray starts, I would need to make multiple reads like I showed above, right?
Sorry for all the questions in one, but I'm really unsure at the moment on what to do.
The size of the color table is determined by bV5ClrUsed.
An ICC color profile is present in the file only if bV5CSType == PROFILE_EMBEDDED.
The documentation here provides all that information.
Then, 24-bit color means 8 red, 8 green, 8 blue, 0 alpha. You'd have to convert that to 32-bit RGBA in order to have any alpha channel at all.
Finally, the alpha channel DOES affect the display of the image, so you can't use it freely for steganography. You really are better off using the least significant bits of all channels (and maybe not from all pixels).