Blender V4.5
format_dds.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Google Inc. All rights reserved. (BSD-3-Clause)
2 * SPDX-FileCopyrightText: 2023-2024 Blender Authors (GPL-2.0-or-later).
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later AND BSD-3-Clause */
5
12
13#include <algorithm>
14
16
17#include "IMB_filetype.hh"
18#include "IMB_imbuf_types.hh"
19
20#include "BLI_math_base.h"
21#include "BLI_path_utils.hh"
22#include "BLI_string.h"
23
24#ifdef __BIG_ENDIAN__
25# include "BLI_endian_switch.h"
26#endif
27
28OIIO_NAMESPACE_USING
29using namespace blender::imbuf;
30
31static void LoadDXTCImage(ImBuf *ibuf, Filesystem::IOMemReader &mem_reader);
32
34{
35 /* To match historical behavior for DDS file loading, tell OpenImageIO
36 * to process BC5 compressed textures as normal maps. But only do so
37 * if the environment does not already contain a directive that might
38 * say otherwise. */
39 const char *bc5normal = "dds:bc5normal";
40 const char *oiio_env = BLI_getenv("OPENIMAGEIO_OPTIONS");
41 if (!oiio_env || !BLI_strcasestr(oiio_env, bc5normal)) {
42 OIIO::attribute(bc5normal, 1);
43 }
44}
45
46bool imb_is_a_dds(const uchar *mem, size_t size)
47{
48 return imb_oiio_check(mem, size, "dds");
49}
50
51ImBuf *imb_load_dds(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace)
52{
53 ImageSpec config, spec;
54 ReadContext ctx{mem, size, "dds", IMB_FTYPE_DDS, flags};
55
56 ImBuf *ibuf = imb_oiio_read(ctx, config, r_colorspace, spec);
57
58 /* Load compressed DDS information if available. */
59 if (ibuf && (flags & IB_test) == 0) {
60 Filesystem::IOMemReader mem_reader(cspan<uchar>(mem, size));
61 LoadDXTCImage(ibuf, mem_reader);
62 }
63
64 return ibuf;
65}
66
67/* A function that flips a DXTC block. */
68using FlipBlockFunction = void (*)(uint8_t *block);
69
70/* Flips a full DXT1 block in the y direction. */
71static void FlipDXT1BlockFull(uint8_t *block)
72{
73 /* A DXT1 block layout is:
74 * [0-1] color0.
75 * [2-3] color1.
76 * [4-7] color bitmap, 2 bits per pixel.
77 * So each of the 4-7 bytes represents one line, flipping a block is just
78 * flipping those bytes. */
79 uint8_t tmp = block[4];
80 block[4] = block[7];
81 block[7] = tmp;
82 tmp = block[5];
83 block[5] = block[6];
84 block[6] = tmp;
85}
86
87/* Flips the first 2 lines of a DXT1 block in the y direction. */
88static void FlipDXT1BlockHalf(uint8_t *block)
89{
90 /* See layout above. */
91 uint8_t tmp = block[4];
92 block[4] = block[5];
93 block[5] = tmp;
94}
95
96/* Flips a full DXT3 block in the y direction. */
97static void FlipDXT3BlockFull(uint8_t *block)
98{
99 /* A DXT3 block layout is:
100 * [0-7] alpha bitmap, 4 bits per pixel.
101 * [8-15] a DXT1 block. */
102
103 /* We can flip the alpha bits at the byte level (2 bytes per line). */
104 uint8_t tmp = block[0];
105
106 block[0] = block[6];
107 block[6] = tmp;
108 tmp = block[1];
109 block[1] = block[7];
110 block[7] = tmp;
111 tmp = block[2];
112 block[2] = block[4];
113 block[4] = tmp;
114 tmp = block[3];
115 block[3] = block[5];
116 block[5] = tmp;
117
118 /* And flip the DXT1 block using the above function. */
119 FlipDXT1BlockFull(block + 8);
120}
121
122/* Flips the first 2 lines of a DXT3 block in the y direction. */
123static void FlipDXT3BlockHalf(uint8_t *block)
124{
125 /* See layout above. */
126 uint8_t tmp = block[0];
127
128 block[0] = block[2];
129 block[2] = tmp;
130 tmp = block[1];
131 block[1] = block[3];
132 block[3] = tmp;
133 FlipDXT1BlockHalf(block + 8);
134}
135
136/* Flips a full DXT5 block in the y direction. */
137static void FlipDXT5BlockFull(uint8_t *block)
138{
139 /* A DXT5 block layout is:
140 * [0] alpha0.
141 * [1] alpha1.
142 * [2-7] alpha bitmap, 3 bits per pixel.
143 * [8-15] a DXT1 block. */
144
145 /* The alpha bitmap doesn't easily map lines to bytes, so we have to
146 * interpret it correctly. Extracted from
147 * http://www.opengl.org/registry/specs/EXT/texture_compression_s3tc.txt :
148 *
149 * The 6 "bits" bytes of the block are decoded into one 48-bit integer:
150 *
151 * bits = bits_0 + 256 * (bits_1 + 256 * (bits_2 + 256 * (bits_3 +
152 * 256 * (bits_4 + 256 * bits_5))))
153 *
154 * bits is a 48-bit unsigned-integer, from which a three-bit control code
155 * is extracted for a texel at location (x,y) in the block using:
156 *
157 * code(x,y) = bits[3*(4*y+x)+1..3*(4*y+x)+0]
158 *
159 * where bit 47 is the most significant and bit 0 is the least
160 * significant bit. */
161 uint line_0_1 = block[2] + 256 * (block[3] + 256 * block[4]);
162 uint line_2_3 = block[5] + 256 * (block[6] + 256 * block[7]);
163 /* swap lines 0 and 1 in line_0_1. */
164 uint line_1_0 = ((line_0_1 & 0x000fff) << 12) | ((line_0_1 & 0xfff000) >> 12);
165 /* swap lines 2 and 3 in line_2_3. */
166 uint line_3_2 = ((line_2_3 & 0x000fff) << 12) | ((line_2_3 & 0xfff000) >> 12);
167
168 block[2] = line_3_2 & 0xff;
169 block[3] = (line_3_2 & 0xff00) >> 8;
170 block[4] = (line_3_2 & 0xff0000) >> 16;
171 block[5] = line_1_0 & 0xff;
172 block[6] = (line_1_0 & 0xff00) >> 8;
173 block[7] = (line_1_0 & 0xff0000) >> 16;
174
175 /* And flip the DXT1 block using the above function. */
176 FlipDXT1BlockFull(block + 8);
177}
178
179/* Flips the first 2 lines of a DXT5 block in the y direction. */
180static void FlipDXT5BlockHalf(uint8_t *block)
181{
182 /* See layout above. */
183 uint line_0_1 = block[2] + 256 * (block[3] + 256 * block[4]);
184 uint line_1_0 = ((line_0_1 & 0x000fff) << 12) | ((line_0_1 & 0xfff000) >> 12);
185 block[2] = line_1_0 & 0xff;
186 block[3] = (line_1_0 & 0xff00) >> 8;
187 block[4] = (line_1_0 & 0xff0000) >> 16;
188 FlipDXT1BlockHalf(block + 8);
189}
190
196static void FlipDXTCImage(ImBuf *ibuf)
197{
198 uint32_t width = ibuf->x;
199 uint32_t height = ibuf->y;
200 uint32_t levels = ibuf->dds_data.nummipmaps;
201 int fourcc = ibuf->dds_data.fourcc;
202 uint8_t *data = ibuf->dds_data.data;
203 int data_size = ibuf->dds_data.size;
204
205 uint32_t *num_valid_levels = &ibuf->dds_data.nummipmaps;
206 *num_valid_levels = 0;
207
208 /* Must have valid dimensions. */
209 if (width == 0 || height == 0) {
210 return;
211 }
212 /* Height must be a power-of-two: not because something within DXT/S3TC
213 * needs it. Only because we want to flip the image upside down, by
214 * swapping and flipping block rows, and that in general case (with mipmaps)
215 * is only possible for POT height. */
216 if (!is_power_of_2_i(height)) {
217 return;
218 }
219
220 FlipBlockFunction full_block_function;
221 FlipBlockFunction half_block_function;
222 uint block_bytes = 0;
223
224 switch (fourcc) {
225 case FOURCC_DXT1:
226 full_block_function = FlipDXT1BlockFull;
227 half_block_function = FlipDXT1BlockHalf;
228 block_bytes = 8;
229 break;
230 case FOURCC_DXT3:
231 full_block_function = FlipDXT3BlockFull;
232 half_block_function = FlipDXT3BlockHalf;
233 block_bytes = 16;
234 break;
235 case FOURCC_DXT5:
236 full_block_function = FlipDXT5BlockFull;
237 half_block_function = FlipDXT5BlockHalf;
238 block_bytes = 16;
239 break;
240 default:
241 return;
242 }
243
244 *num_valid_levels = levels;
245
246 uint mip_width = width;
247 uint mip_height = height;
248
249 const uint8_t *data_end = data + data_size;
250
251 for (uint level = 0; level < levels; level++) {
252 uint blocks_per_row = (mip_width + 3) / 4;
253 uint blocks_per_col = (mip_height + 3) / 4;
254 uint blocks = blocks_per_row * blocks_per_col;
255
256 if (data + block_bytes * blocks > data_end) {
257 /* Stop flipping when running out of data to be modified, avoiding possible buffer overrun
258 * on a malformed files. */
259 *num_valid_levels = level;
260 break;
261 }
262
263 if (mip_height == 1) {
264 /* no flip to do, and we're done. */
265 break;
266 }
267 if (mip_height == 2) {
268 /* flip the first 2 lines in each block. */
269 for (uint i = 0; i < blocks_per_row; i++) {
270 half_block_function(data + i * block_bytes);
271 }
272 }
273 else {
274 /* flip each block. */
275 for (uint i = 0; i < blocks; i++) {
276 full_block_function(data + i * block_bytes);
277 }
278
279 /* Swap each block line in the first half of the image with the
280 * corresponding one in the second half.
281 * note that this is a no-op if mip_height is 4. */
282 uint row_bytes = block_bytes * blocks_per_row;
283 uint8_t *temp_line = new uint8_t[row_bytes];
284
285 for (uint y = 0; y < blocks_per_col / 2; y++) {
286 uint8_t *line1 = data + y * row_bytes;
287 uint8_t *line2 = data + (blocks_per_col - y - 1) * row_bytes;
288
289 memcpy(temp_line, line1, row_bytes);
290 memcpy(line1, line2, row_bytes);
291 memcpy(line2, temp_line, row_bytes);
292 }
293
294 delete[] temp_line;
295 }
296
297 /* mip levels are contiguous. */
298 data += block_bytes * blocks;
299 mip_width = std::max(1U, mip_width >> 1);
300 mip_height = std::max(1U, mip_height >> 1);
301 }
302}
303
304static void LoadDXTCImage(ImBuf *ibuf, Filesystem::IOMemReader &mem_reader)
305{
306 /* Reach into memory and pull out the pixel format flags and mipmap counts. This is safe if
307 * we've made it this far. */
308 uint32_t flags = 0;
309 mem_reader.pread(&flags, sizeof(uint32_t), 8);
310 mem_reader.pread(&ibuf->dds_data.nummipmaps, sizeof(uint32_t), 28);
311 mem_reader.pread(&ibuf->dds_data.fourcc, sizeof(uint32_t), 84);
312
313#ifdef __BIG_ENDIAN__
315#endif
316
317 const uint32_t DDSD_MIPMAPCOUNT = 0x00020000U;
318 if ((flags & DDSD_MIPMAPCOUNT) == 0) {
319 ibuf->dds_data.nummipmaps = 1;
320 }
321
322 /* Load the compressed data. */
323 if (ibuf->dds_data.fourcc != FOURCC_DDS) {
324 uint32_t dds_header_size = 128;
325 if (ibuf->dds_data.fourcc == FOURCC_DX10) {
326 dds_header_size += 20;
327 }
328
329 ibuf->dds_data.size = mem_reader.size() - dds_header_size;
330 ibuf->dds_data.data = (uchar *)malloc(ibuf->dds_data.size);
331 mem_reader.pread(ibuf->dds_data.data, ibuf->dds_data.size, dds_header_size);
333
334 /* Flip compressed image data to match OpenGL convention. */
335 FlipDXTCImage(ibuf);
336 }
337}
BLI_INLINE void BLI_endian_switch_uint32(unsigned int *val) ATTR_NONNULL(1)
MINLINE int is_power_of_2_i(int n)
const char * BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
int char * BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
unsigned char uchar
unsigned int uint
@ IMB_FTYPE_DDS
#define FOURCC_DX10
#define FOURCC_DXT5
#define FOURCC_DDS
#define FOURCC_DXT1
@ IB_TAKE_OWNERSHIP
@ IB_test
#define FOURCC_DXT3
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static void FlipDXT3BlockFull(uint8_t *block)
Definition format_dds.cc:97
void(*)(uint8_t *block) FlipBlockFunction
Definition format_dds.cc:68
ImBuf * imb_load_dds(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace)
Definition format_dds.cc:51
static void FlipDXT5BlockHalf(uint8_t *block)
static void FlipDXTCImage(ImBuf *ibuf)
bool imb_is_a_dds(const uchar *mem, size_t size)
Definition format_dds.cc:46
static void LoadDXTCImage(ImBuf *ibuf, Filesystem::IOMemReader &mem_reader)
void imb_init_dds()
Definition format_dds.cc:33
static void FlipDXT1BlockFull(uint8_t *block)
Definition format_dds.cc:71
static void FlipDXT5BlockFull(uint8_t *block)
static void FlipDXT1BlockHalf(uint8_t *block)
Definition format_dds.cc:88
static void FlipDXT3BlockHalf(uint8_t *block)
ImBuf * imb_oiio_read(const ReadContext &ctx, const ImageSpec &config, ImFileColorSpace &r_colorspace, ImageSpec &r_newspec)
bool imb_oiio_check(const uchar *mem, size_t mem_size, const char *file_format)
unsigned int size
unsigned int nummipmaps
ImBufOwnership ownership
unsigned char * data
unsigned int fourcc
DDSData dds_data
i
Definition text_draw.cc:230