0
\$\begingroup\$

I have a program capable of rendering 3D graphics using Direct3D11 and it is currently compatible with two different texture formats, TGA and DDS. The code to read and load a TGA texture in memory I'm using was from Rastertek tutorials here and it handle TGA textures which are upside down by reading their bytes backwards, but the code to read and load a DDS texture I'm using is just D3DX11CreateShaderResourcesViewFromFile function from D3DX11tex.h, and it was good enough for me until a while ago.

Now I'm facing a problem as some of DDS textures I want to use are upside down in a similar way to the TGA textures, so I need somehow a code where I can control how it reads the texture so I can modify it to read the DDS texture upside-down.

I've read this article from Microsoft, but even their simplest code is far more complex than the code from Rastertek to TGA file. It's not clear to me how I can implement a method to read a DDS upside-down.

What I 'think' I understand about TGA's data it that its first 18 bytes are their header and the rest is 4 bytes for each pixel(in a RGBA style), but I really don't understand how DDS works.

\$\endgroup\$
0
1
\$\begingroup\$

The simplest option for loading DDS files is probably to use the open source library that Microsoft provide - https://github.com/Microsoft/DirectXTK. That doesn't include functionality to turn them upside down when loading them though.

If you really want to do it manually, then take a look at https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide which documents the file format. It's not a simple file format though.

For example, the pixel data for the texture can be in a wide variety of formats, including things like BC7 compressed data (which is intended to be stored compressed in GPU memory, and get decompressed on the fly when read by the GPU). There's often data for more than one mipmap in the file too.

For compressed pixel formats like BC7, turning the texture upside down at load time isn't really practical. In addition, the compression is lossy, which makes it a bad choice for storing editable content anyway. Ideally you'd author content for the game in a losslessly compressed editable file format (e.g. .PNG or .PSD). You can then convert those source images into compressed DDS files with mipmaps for use in the game as part of the build process.

Microsoft also provide an open source library for creating DDS textures - https://github.com/microsoft/DirectXTex. That can be used to do things like loading a DDS file, turning it upside down, and saving it again, as well as the more sensible case of loading a PNG and converting it to a DDS.

As a side note, you can also easily flip a texture upside down when rendering with it, by adjusting the UV coordinates you use.

\$\endgroup\$
6
  • \$\begingroup\$ First of all, thank you for answering my question. The idea of using the library you mentioned is interesting, but I'm still not confident about it, and the main reason for this is because I don't know if I'll run in another issue with the texture in which I would need to modify the code's interpretation of it... So I took Microsoft Documentation's structure for the header of the texture and could read it's properties using std::fstream, it looks like everything was read correctly and now I'm trying to interpret this data to load my texture correctly. \$\endgroup\$ – Rafael Ferreira Apr 6 at 0:40
  • \$\begingroup\$ Could you tell me if the data right after the end of the header of the texture is the same as TGA files, for example for RGBA formats the next four bytes compose the first pixel? I've already marked your answer the correct one as it was very informative and the tip about the UV coordinates is certainly a interesting idea. \$\endgroup\$ – Rafael Ferreira Apr 6 at 0:42
  • \$\begingroup\$ If you've read the documentation for the various DDS formats, it should be very obvious that the data is not stored the same as TGA files. BCn compressed textues use 4-8 BITS per pixel on average, not 4 bytes per pixel as with TGA, so there's no way the same data layout would fit here. \$\endgroup\$ – DMGregory Apr 6 at 1:01
  • \$\begingroup\$ Ok, thank you for answering again, I'm going to read more about it so I may find a way to load these textures. \$\endgroup\$ – Rafael Ferreira Apr 6 at 1:36
  • \$\begingroup\$ Note that it is possible to store uncompressed pixel data inside a DDS file (e.g. when the pixel format is D3DFMT_A8B8G8R8 or D3DFMT_A8R8G8B8). It's just not a very common thing to do. \$\endgroup\$ – Adam Apr 6 at 21:54
0
\$\begingroup\$

I'm a newbie too. In the answer above, it talks about decoding the DDS into a bitmap, and the re-encoding it once the rows have been reversed.

For your reference, here is a code to reverse the row order of a bitmap. Note that the image has 4 channels, hence the magic number 4. Where Pixels is a vector of unsigned char, of size width*height*4:

// Reverse row order
short unsigned int num_rows_to_swap = height;
vector<unsigned char> buffer(static_cast<size_t>(width) * 4);

if (0 != height % 2)
    num_rows_to_swap--;

num_rows_to_swap /= 2;

for (size_t i = 0; i < num_rows_to_swap; i++)
{
    size_t y_first = i * static_cast<size_t>(width) * 4;
    size_t y_last = (static_cast<size_t>(height) - 1 - i) * static_cast<size_t>(width) * 4;

    // Probably should have used std::copy here...
    memcpy(&buffer[0], &Pixels[y_first], static_cast<size_t>(width) * 4);
    memcpy(&Pixels[y_first], &Pixels[y_last], static_cast<size_t>(width) * 4);
    memcpy(&Pixels[y_last], &buffer[0], static_cast<size_t>(width) * 4);
}
\$\endgroup\$
2
  • \$\begingroup\$ How does this answer the question about flipping DDS format images, which are not simply bitmaps? \$\endgroup\$ – DMGregory Apr 6 at 1:51
  • \$\begingroup\$ The other answer also deals with the process of deciding and re-encoding DDS files on the CPU. It happens. :) \$\endgroup\$ – shawn_halayka Apr 6 at 1:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.