File: TextureLoaderDds.cpp

package info (click to toggle)
jazz2-native 3.5.0-2
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid
  • size: 16,912 kB
  • sloc: cpp: 172,557; xml: 113; python: 36; makefile: 5; sh: 2
file content (165 lines) | stat: -rw-r--r-- 5,742 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include "TextureLoaderDds.h"

#include <Base/Memory.h>

using namespace Death::IO;
using namespace Death::Memory;

namespace nCine
{
	TextureLoaderDds::TextureLoaderDds(std::unique_ptr<Stream> fileHandle)
		: ITextureLoader(std::move(fileHandle))
	{
		DdsHeader header;

		if (!fileHandle_->IsValid()) {
			return;
		}

		const bool headerRead = readHeader(header);
		DEATH_ASSERT(headerRead, "DDS header cannot be read", );
		const bool formatParsed = parseFormat(header);
		DEATH_ASSERT(formatParsed, "DDS format cannot be parsed", );

		hasLoaded_ = true;
	}

	bool TextureLoaderDds::readHeader(DdsHeader& header)
	{
		// DDS header is 128 bytes long
		fileHandle_->Read(&header, 128);

		// Checking for the header presence
		DEATH_ASSERT(AsLE(header.dwMagic) == 0x20534444 /* "DDS " */, "Invalid DDS signature", false);

		headerSize_ = 128;
		width_ = AsLE(header.dwWidth);
		height_ = AsLE(header.dwHeight);
		mipMapCount_ = AsLE(header.dwMipMapCount);

		if (mipMapCount_ == 0) {
			mipMapCount_ = 1;
		}

		return true;
	}

	bool TextureLoaderDds::parseFormat(const DdsHeader& header)
	{
		GLenum internalFormat = GL_RGB; // to suppress uninitialized variable warning

		const uint32_t flags = AsLE(header.ddspf.dwFlags);

		// Texture contains compressed RGB data, dwFourCC contains valid data
		if (flags & DDPF_FOURCC) {
			const uint32_t fourCC = AsLE(header.ddspf.dwFourCC);

			const char* fourCCchars = reinterpret_cast<const char*>(&fourCC);
			LOGI("FourCC: \"{:c}{:c}{:c}{:c}\" (0x{:x})", fourCCchars[0], fourCCchars[1], fourCCchars[2], fourCCchars[3], fourCC);

			// Parsing the FourCC format
			switch (fourCC) {
				case DDS_DXT1:
					internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
					break;
				case DDS_DXT3:
					internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
					break;
				case DDS_DXT5:
					internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
					break;
#if defined(WITH_OPENGLES)
				case DDS_ETC1:
					internalFormat = GL_ETC1_RGB8_OES;
					break;
				case DDS_ATC:
					internalFormat = GL_ATC_RGB_AMD;
					break;
				case DDS_ATCA:
					internalFormat = GL_ATC_RGBA_EXPLICIT_ALPHA_AMD;
					break;
				case DDS_ATCI:
					internalFormat = GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD;
					break;
#endif
				default:
					LOGE("Unsupported FourCC compression code: {}", fourCC);
					return false;
			}

			loadPixels(internalFormat);
		} else {
			// Texture contains uncompressed data
			GLenum type = GL_UNSIGNED_BYTE;

			const uint32_t bitCount = AsLE(header.ddspf.dwRGBBitCount);
			const uint32_t redMask = AsLE(header.ddspf.dwRBitMask);
			const uint32_t greenMask = AsLE(header.ddspf.dwGBitMask);
			const uint32_t blueMask = AsLE(header.ddspf.dwBBitMask);
			const uint32_t alphaMask = AsLE(header.ddspf.dwABitMask);

			LOGI("Pixel masks ({}bit): R:0x{:x} G:0x{:x} B:0x{:x} A:0x{:x}", bitCount, redMask, greenMask, blueMask, alphaMask);

			// Texture contains uncompressed RGB data
			// dwRGBBitCount and the RGB masks (dwRBitMask, dwRBitMask, dwRBitMask) contain valid data
			if (flags & DDPF_RGB || flags & (DDPF_RGB | DDPF_ALPHAPIXELS)) {
				if ((redMask == 0x00FF0000 && greenMask == 0x0000FF00 && blueMask == 0x000000FF && alphaMask == 0x0) ||
					(blueMask == 0x00FF0000 && greenMask == 0x0000FF00 && redMask == 0x000000FF && alphaMask == 0x0)) { // 888
					internalFormat = GL_RGB8;
				} else if ((alphaMask == 0xFF000000 && redMask == 0x00FF0000 && greenMask == 0x0000FF00 && blueMask == 0x000000FF) ||
						 (alphaMask == 0xFF000000 && blueMask == 0x00FF0000 && greenMask == 0x0000FF00 && redMask == 0x000000FF)) { // 8888
					internalFormat = GL_RGBA8;
				}
#if 0
				// 16 bits uncompressed DDS data is not compatbile with OpenGL color channels order
				else if (redMask == 0xF800 && greenMask == 0x07E0 && blueMask == 0x001F) { // 565
					internalFormat = GL_RGB565;
					type = GL_UNSIGNED_SHORT_5_6_5;
				} else if (alphaMask == 0x8000 && redMask == 0x7C00 && greenMask == 0x03E0 && blueMask == 0x001F) { // 5551
					internalFormat = GL_RGB5_A1;
					type = GL_UNSIGNED_SHORT_5_5_5_1;
				} else if (alphaMask == 0xF000 && redMask == 0x0F00 && greenMask == 0x00F0 && blueMask == 0x000F) { // 4444
					internalFormat = GL_RGBA4;
					type = GL_UNSIGNED_SHORT_4_4_4_4;
				}
#endif
				else {
					LOGE("Unsupported DDPF_RGB pixel format");
					return false;
				}
			} else if (flags & (DDPF_LUMINANCE | DDPF_ALPHAPIXELS)) {
				// Used in some older DDS files for single channel color uncompressed data
				// dwRGBBitCount contains the luminance channel bit count; dwRBitMask contains the channel mask
				// Can be combined with DDPF_ALPHAPIXELS for a two channel DDS file
				internalFormat = GL_RG8;
			} else if (flags & DDPF_LUMINANCE) {
				internalFormat = GL_R8;
			} else if (flags & DDPF_ALPHA) {
				// Used in some older DDS files for alpha channel only uncompressed data
				// dwRGBBitCount contains the alpha channel bitcount; dwABitMask contains valid data
				internalFormat = GL_R8;
			} else {
				LOGE("Unsupported DDS uncompressed pixel format: {}", flags);
				return false;
			}

			loadPixels(internalFormat, type);

			if (redMask > blueMask && bitCount > 16) {
				texFormat_.bgrFormat();
			}
		}

		if (mipMapCount_ > 1) {
			LOGI("MIP Maps: {}", mipMapCount_);
			mipDataOffsets_ = std::make_unique<std::uint32_t[]>(mipMapCount_);
			mipDataSizes_ = std::make_unique<std::uint32_t[]>(mipMapCount_);
			std::uint32_t dataSizesSum = TextureFormat::calculateMipSizes(internalFormat, width_, height_, mipMapCount_, mipDataOffsets_.get(), mipDataSizes_.get());
			if (dataSizesSum != dataSize_) {
				LOGW("The sum of MIP maps size ({}) is different than texture total data ({})", dataSizesSum, dataSize_);
			}
		}

		return true;
	}
}