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