comparison src/libraries/ddsparse/ddsparse.cpp @ 1420:2a5ef265e040 image-CompressedData

Added support for DXT1/3/5 and BC5/7 compressed textures via love.image.CompressedData. Some internals are still iffy. - added a new CompressedData type to love.image - added love.image.isCompressed(file or data) - love.graphics.newImage automatically tests for compression when loading a file - added love.graphics.isSupported strings for DXT, BC5, and BC7 Compressed textures meant to be used on the GPU offer huge performance gains when - loading the texture from a file - loading the texture from RAM to VRAM - loading mipmaps - rendering
author Alex Szpakowski <slime73@gmail.com>
date Thu, 04 Apr 2013 19:09:18 -0300
parents
children aa603d1b03bc
comparison
equal deleted inserted replaced
1419:edd946442f86 1420:2a5ef265e040
1 /**
2 * Simple DDS data parser for compressed 2D textures.
3 *
4 * Copyright (c) 2013 Alexander Szpakowski.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 **/
24
25 #include "ddsparse.h"
26 #include "ddsinfo.h"
27
28 #include <algorithm>
29
30 namespace dds
31 {
32
33 using namespace dds::dxinfo;
34
35 // Creates a packed uint representation of a FourCC code.
36 static inline uint32_t FourCC(char a, char b, char c, char d)
37 {
38 uint32_t fcc = ((uint32_t) a)
39 | (((uint32_t) b) << 8)
40 | (((uint32_t) c) << 16)
41 | (((uint32_t) d) << 24);
42
43 return fcc;
44 }
45
46 Parser::Image::Image()
47 : width(0)
48 , height(0)
49 , dataSize(0)
50 , data(0)
51 {
52 }
53
54 bool Parser::isDDS(const void *data, size_t dataSize)
55 {
56 const uint8_t *readData = (const uint8_t *) data;
57 ptrdiff_t offset = 0;
58
59 // Is the data large enough to hold the DDS header?
60 if(dataSize < sizeof(uint32_t) + sizeof(DDSHeader))
61 return false;
62
63 // All DDS files start with "DDS ".
64 if((*(uint32_t *) readData) != FourCC('D','D','S',' '))
65 return false;
66
67 offset += sizeof(uint32_t);
68
69 DDSHeader *header = (DDSHeader *) &readData[offset];
70
71
72 // Verify header to validate DDS data.
73 if (header->size != sizeof(DDSHeader) || header->format.size != sizeof(DDSPixelFormat))
74 return false;
75
76 offset += sizeof(DDSHeader);
77
78 // Check for DX10 extension.
79 if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == FourCC('D','X','1','0')))
80 {
81 // Data must be big enough for both headers plus the magic value.
82 if (dataSize < (sizeof(uint32_t) + sizeof(DDSHeader) + sizeof(DDSHeader10)))
83 return false;
84
85 }
86
87 return true;
88 }
89
90 Parser::Parser(const void *data, size_t dataSize, Options opts)
91 : format(FORMAT_UNKNOWN)
92 , options(opts)
93 {
94 parseData(data, dataSize);
95 }
96
97 Parser::Parser(const Parser &other)
98 : format(other.format)
99 , options(other.options)
100 {
101 std::vector<Image>::const_iterator it;
102 for (it = other.texData.begin(); it != other.texData.end(); ++it)
103 {
104 Image img = *it;
105
106 if ((options & OPTIONS_COPY_DATA) && img.dataSize > 0)
107 {
108 uint8_t *data = 0;
109
110 try
111 {
112 data = new uint8_t[img.dataSize];
113 }
114 catch (std::exception &)
115 {
116 clearData();
117 throw;
118 }
119
120 img.data = data;
121 }
122
123 texData.push_back(img);
124 }
125 }
126
127 Parser::~Parser()
128 {
129 // Delete any data we created.
130 clearData();
131 }
132
133 Format Parser::getFormat() const
134 {
135 return format;
136 }
137
138 const Parser::Image *Parser::getImageData(size_t miplevel) const
139 {
140 if (miplevel >= texData.size())
141 return 0;
142
143 return &texData[miplevel];
144 }
145
146 size_t Parser::getNumMipmaps() const
147 {
148 return texData.size();
149 }
150
151 bool Parser::ownsData() const
152 {
153 return (options & OPTIONS_COPY_DATA) != 0;
154 }
155
156 Format Parser::parseDDSFormat(const DDSPixelFormat &fmt) const
157 {
158 if (fmt.flags & DDPF_FOURCC)
159 {
160 if (fmt.fourCC == FourCC('D','X','T','1'))
161 return FORMAT_DXT1;
162 else if (fmt.fourCC == FourCC('D','X','T','3'))
163 return FORMAT_DXT3;
164 else if (fmt.fourCC == FourCC('D','X','T','5'))
165 return FORMAT_DXT5;
166 else if (fmt.fourCC == FourCC('A','T','I','2'))
167 return FORMAT_BC5u;
168 }
169
170 return FORMAT_UNKNOWN;
171 }
172
173 Format Parser::parseDX10Format(DXGIFormat fmt) const
174 {
175 Format f = FORMAT_UNKNOWN;
176
177 switch (fmt)
178 {
179 case DXGI_FORMAT_BC1_TYPELESS:
180 case DXGI_FORMAT_BC1_UNORM:
181 case DXGI_FORMAT_BC1_UNORM_SRGB:
182 f = FORMAT_DXT1;
183 break;
184 case DXGI_FORMAT_BC2_TYPELESS:
185 case DXGI_FORMAT_BC2_UNORM:
186 case DXGI_FORMAT_BC2_UNORM_SRGB:
187 f = FORMAT_DXT3;
188 break;
189 case DXGI_FORMAT_BC3_TYPELESS:
190 case DXGI_FORMAT_BC3_UNORM:
191 case DXGI_FORMAT_BC3_UNORM_SRGB:
192 f = FORMAT_DXT5;
193 break;
194 case DXGI_FORMAT_BC5_SNORM:
195 f = FORMAT_BC5s;
196 break;
197 case DXGI_FORMAT_BC5_TYPELESS:
198 case DXGI_FORMAT_BC5_UNORM:
199 f = FORMAT_BC5u;
200 break;
201 case DXGI_FORMAT_BC7_TYPELESS:
202 case DXGI_FORMAT_BC7_UNORM:
203 f = FORMAT_BC7;
204 break;
205 case DXGI_FORMAT_BC7_UNORM_SRGB:
206 f = FORMAT_BC7srgb;
207 default:
208 break;
209 }
210
211 return f;
212 }
213
214 size_t Parser::parseImageSize(Format fmt, int width, int height) const
215 {
216 size_t size = 0;
217
218 switch (fmt)
219 {
220 case FORMAT_DXT1:
221 case FORMAT_DXT3:
222 case FORMAT_DXT5:
223 case FORMAT_BC5s:
224 case FORMAT_BC5u:
225 case FORMAT_BC7:
226 case FORMAT_BC7srgb:
227 {
228 int numBlocksWide = 0;
229 if (width > 0)
230 numBlocksWide = std::max(1, (width + 3) / 4);
231
232 int numBlocksHigh = 0;
233 if (height > 0)
234 numBlocksHigh = std::max(1, (height + 3) / 4);
235
236 int numBytesPerBlock = (fmt == FORMAT_DXT1 ? 8 : 16);
237
238 size = numBlocksWide * numBytesPerBlock * numBlocksHigh;
239 }
240 break;
241 default:
242 break;
243 }
244
245 return size;
246 }
247
248 bool Parser::parseTexData(const uint8_t *data, size_t dataSize, Format fmt, int w, int h, int mips)
249 {
250 size_t offset = 0;
251
252 for (int i = 0; i < mips; i++)
253 {
254 Image img;
255 img.width = w;
256 img.height = h;
257
258 img.dataSize = parseImageSize(fmt, img.width, img.height);
259
260 // Make sure the data size is valid.
261 if (img.dataSize == 0 || (offset + img.dataSize) > dataSize)
262 {
263 // Clean up any data we allocated previously.
264 clearData();
265 return false;
266 }
267
268 // Create our own copy of the data, if requested.
269 if (options & OPTIONS_COPY_DATA)
270 {
271 uint8_t *newData = 0;
272
273 try
274 {
275 newData = new uint8_t[img.dataSize];
276 }
277 catch (std::exception &)
278 {
279 // Clean up before throwing.
280 clearData();
281 throw;
282 }
283
284 memcpy(newData, &data[offset], img.dataSize);
285 img.data = newData;
286 }
287 else
288 img.data = &data[offset];
289
290 texData.push_back(img);
291
292 // Move to the next mip level.
293 offset += img.dataSize;
294
295 w = std::max(w / 2, 1);
296 h = std::max(h / 2, 1);
297 }
298
299 return true;
300 }
301
302 bool Parser::parseData(const void *data, size_t dataSize)
303 {
304 if (!isDDS(data, dataSize))
305 return false;
306
307 const uint8_t *readData = (const uint8_t *) data;
308 ptrdiff_t offset = sizeof(uint32_t);
309
310 DDSHeader *header = (DDSHeader *) &readData[offset];
311 offset += sizeof(DDSHeader);
312
313
314 // Check for DX10 extension.
315 if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == FourCC('D','X','1','0')))
316 {
317 DDSHeader10 *header10 = (DDSHeader10 *) &readData[offset];
318 offset += sizeof(DDSHeader10);
319
320 // We can't deal with 1D/3D textures.
321 switch (header10->resourceDimension)
322 {
323 case D3D10_RESOURCE_DIMENSION_TEXTURE2D:
324 case D3D10_RESOURCE_DIMENSION_UNKNOWN:
325 break;
326 default:
327 return false;
328 }
329
330 // We also can't deal with texture arrays and cubemaps.
331 if (header10->arraySize > 1)
332 return false;
333
334 format = parseDX10Format(header10->dxgiFormat);
335 }
336 else
337 format = parseDDSFormat(header->format);
338
339
340 if (format == FORMAT_UNKNOWN)
341 return false;
342
343
344 int w = header->width;
345 int h = header->height;
346
347 int mips = std::max((int) header->mipMapCount, 1);
348
349 return parseTexData(&readData[offset], dataSize - offset, format, w, h, mips);
350 }
351
352 void Parser::clearData()
353 {
354 if (options & OPTIONS_COPY_DATA)
355 {
356 // Delete any data we created.
357 std::vector<Image>::iterator it;
358 for (it = texData.begin(); it != texData.end(); ++it)
359 {
360 delete[] it->data;
361 it->data = 0;
362 }
363 }
364
365 texData.clear();
366 }
367
368 } // dds