Skip to content

Commit 38d6c2a

Browse files
committed
feat: add dxt extension
1 parent edcd256 commit 38d6c2a

File tree

8 files changed

+331
-2
lines changed

8 files changed

+331
-2
lines changed

.github/workflows/build.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ jobs:
187187
if not exist ../out/pl_script_camera.dll exit 1
188188
if not exist ../out/pl_shader_variant_ext.dll exit 1
189189
if not exist ../out/pl_dds_ext.dll exit 1
190+
if not exist ../out/pl_dxt_ext.dll exit 1
190191
cd ..
191192
192193
- name: Package Pilot Light
@@ -391,6 +392,7 @@ jobs:
391392
test -f ./out/pl_script_camera.dylib || exit 1
392393
test -f ./out/pl_shader_variant_ext.dylib || exit 1
393394
test -f ./out/pl_dds_ext.dylib || exit 1
395+
test -f ./out/pl_dxt_ext.dylib || exit 1
394396
395397
- name: Package Pilot Light
396398
run: |
@@ -606,6 +608,7 @@ jobs:
606608
test -f ./out/pl_script_camera.so || exit 1
607609
test -f ./out/pl_shader_variant_ext.so || exit 1
608610
test -f ./out/pl_dds_ext.so || exit 1
611+
test -f ./out/pl_dxt_ext.so || exit 1
609612
610613
- name: Package Pilot Light
611614
run: |

docs/version.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,4 @@ the API is complete. It just means we won't break what currently exists.
102102
* Renderer v0.2.1 (pl_renderer_ext.h)
103103
* Dear ImGui v0.1.0 (pl_dear_imgui_ext.h)
104104
* Animation v0.1.0 (pl_animation_ext.h)
105+
* DXT v0.1.0 (pl_dxt_ext.h)

extensions/pl_dxt_ext.c

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
/*
2+
pl_dxt_ext.c
3+
*/
4+
5+
/*
6+
Index of this file:
7+
// [SECTION] includes
8+
// [SECTION] public api implementation
9+
// [SECTION] extension loading
10+
// [SECTION] unity build
11+
*/
12+
13+
//-----------------------------------------------------------------------------
14+
// [SECTION] includes
15+
//-----------------------------------------------------------------------------
16+
17+
#include <string.h>
18+
#include <math.h>
19+
#include "pl.h"
20+
#include "pl_dxt_ext.h"
21+
22+
// extensions
23+
#include "pl_graphics_ext.h"
24+
25+
// libraries
26+
#include "stb_dxt.h"
27+
28+
//-----------------------------------------------------------------------------
29+
// [SECTION] public api implementation
30+
//-----------------------------------------------------------------------------
31+
32+
void
33+
pl_dxt_compress(const plDxtInfo* ptInfo, uint8_t* puDataOut, size_t* szSizeOut)
34+
{
35+
const uint32_t uAdjustedImageWidth = (uint32_t)ceilf((float)ptInfo->uWidth / 4.0f) * 4;
36+
const uint32_t uAdjustedImageHeight = (uint32_t)ceilf((float)ptInfo->uHeight / 4.0f) * 4;
37+
38+
const uint32_t uBlockSize = (ptInfo->uChannels == 2 || ptInfo->uChannels == 4 ? 16 : 8);
39+
40+
if(szSizeOut)
41+
{
42+
if(ptInfo->uChannels == 4)
43+
*szSizeOut = uAdjustedImageWidth * uAdjustedImageHeight;
44+
else if(ptInfo->uChannels == 3)
45+
*szSizeOut = uAdjustedImageWidth * uAdjustedImageHeight / 2;
46+
else
47+
{
48+
PL_ASSERT(false && "Only supporting 3 & 4 channels for now");
49+
}
50+
}
51+
52+
if(puDataOut == NULL)
53+
return;
54+
55+
uint32_t uOffset = 0;
56+
const uint8_t* puData = ptInfo->puData;
57+
58+
uint8_t auInDataBuf[64] = {0};
59+
uint8_t auOutDataBuf[16] = {0};
60+
61+
const uint32_t uBytesPerPixel = ptInfo->uChannels;
62+
const uint32_t uOverflowH = (4 - (uAdjustedImageWidth - ptInfo->uWidth)) % 4;
63+
const uint32_t uOverflowV = (4 - (uAdjustedImageHeight - ptInfo->uHeight)) % 4;
64+
const uint32_t uWrapPosH = ptInfo->uWidth - uOverflowH;
65+
const uint32_t uWrapPosV = ptInfo->uHeight - uOverflowV;
66+
67+
uint8_t auPadded[4] = {0};
68+
69+
int iDxtFlags = STB_DXT_NORMAL;
70+
if(ptInfo->tFlags & PL_DXT_FLAGS_HIGH_QUALITY)
71+
iDxtFlags = STB_DXT_HIGHQUAL;
72+
73+
int iIncludeAlpha = 0;
74+
if(ptInfo->uChannels == 4)
75+
iIncludeAlpha = 1;
76+
77+
if(uOverflowH == 0 && uOverflowV == 0 && ptInfo->uChannels > 2)
78+
{
79+
80+
for (uint32_t uRowStart = 0; uRowStart < uAdjustedImageHeight; uRowStart += 4)
81+
{
82+
for (uint32_t uColumnStart = 0; uColumnStart < uAdjustedImageWidth; uColumnStart += 4)
83+
{
84+
for (uint32_t uRow = 0; uRow < 4; uRow++)
85+
{
86+
for (uint32_t uColumn = 0; uColumn < 4; uColumn++)
87+
{
88+
const uint8_t* ptSource = puData + (uRow * ptInfo->uWidth + uColumn) * ptInfo->uChannels;
89+
for(uint32_t uChannel = 0; uChannel < ptInfo->uChannels; uChannel++)
90+
auPadded[uChannel] = ptSource[uChannel];
91+
memcpy(auInDataBuf + (uRow * 4 + uColumn) * 4, auPadded, 4);
92+
}
93+
}
94+
95+
stb_compress_dxt_block(auOutDataBuf, auInDataBuf, iIncludeAlpha, iDxtFlags);
96+
memcpy(&puDataOut[uOffset], auOutDataBuf, uBlockSize);
97+
98+
uOffset += uBlockSize;
99+
puData += uBytesPerPixel * 4;
100+
}
101+
puData += ptInfo->uWidth * uBytesPerPixel * 3; // by 3 since we already moved first row across
102+
}
103+
}
104+
else // slow path
105+
{
106+
107+
for (uint32_t uRowStart = 0; uRowStart < uAdjustedImageHeight; uRowStart += 4)
108+
{
109+
for (uint32_t uColumnStart = 0; uColumnStart < uAdjustedImageWidth; uColumnStart += 4)
110+
{
111+
if (uRowStart >= uWrapPosV && uColumnStart >= uWrapPosH) // overflow on bottom right corner
112+
{
113+
memset(auInDataBuf, 255, 64);
114+
115+
for (uint32_t uRow = 0; uRow < uOverflowV; uRow++)
116+
{
117+
for (uint32_t uColumn = 0; uColumn < uOverflowH; uColumn++)
118+
{
119+
const uint8_t* ptSource = puData + (uRow * ptInfo->uWidth + uColumn) * 4;
120+
memcpy(auInDataBuf + (uRow * 4 + uColumn) * ptInfo->uChannels, ptSource, ptInfo->uChannels);
121+
}
122+
}
123+
}
124+
else if(uColumnStart >= uWrapPosH) // overflow on right
125+
{
126+
memset(auInDataBuf, 0, 64);
127+
128+
for (uint32_t uRow = 0; uRow < 4; uRow++)
129+
{
130+
for (uint32_t uColumn = 0; uColumn < uOverflowH; uColumn++)
131+
{
132+
const uint8_t* ptSource = puData + (uRow * ptInfo->uWidth + uColumn) * 4;
133+
memcpy(auInDataBuf + (uRow * 4 + uColumn) * ptInfo->uChannels, ptSource, ptInfo->uChannels);
134+
135+
if(uColumn == uOverflowH - 1)
136+
{
137+
for(uint32_t iOverflow = 0; iOverflow < uOverflowV; iOverflow++)
138+
{
139+
memcpy(auInDataBuf + (uRow * 4 + uColumn + iOverflow + 1) * ptInfo->uChannels, ptSource, ptInfo->uChannels);
140+
}
141+
}
142+
}
143+
}
144+
}
145+
else if (uRowStart >= uWrapPosV) // overflow on bottom
146+
{
147+
memset(auInDataBuf, 255, 64);
148+
149+
for (uint32_t uRow = 0; uRow < uOverflowV; uRow++)
150+
{
151+
for (uint32_t uColumn = 0; uColumn < 4; uColumn++)
152+
{
153+
const uint8_t* ptSource = puData + (uRow * ptInfo->uWidth + uColumn) * 4;
154+
memcpy(auInDataBuf + (uRow * 4 + uColumn) * ptInfo->uChannels, ptSource, ptInfo->uChannels);
155+
if(uRow == uOverflowV - 1)
156+
{
157+
for(uint32_t iOverflow = 0; iOverflow < uOverflowV; iOverflow++)
158+
{
159+
memcpy(auInDataBuf + ((uRow + iOverflow + 1) * 4 + uColumn) * ptInfo->uChannels, ptSource, ptInfo->uChannels);
160+
}
161+
}
162+
}
163+
}
164+
}
165+
166+
else
167+
{
168+
for (uint32_t uRow = 0; uRow < 4; uRow++)
169+
{
170+
for (uint32_t uColumn = 0; uColumn < 4; uColumn++)
171+
{
172+
const uint8_t* ptSource = puData + (uRow * ptInfo->uWidth + uColumn) * 4;
173+
memcpy(auInDataBuf + (uRow * 4 + uColumn) * ptInfo->uChannels, ptSource, ptInfo->uChannels);
174+
}
175+
}
176+
}
177+
178+
switch (ptInfo->uChannels)
179+
{
180+
case 3:
181+
case 4:
182+
stb_compress_dxt_block(auOutDataBuf, auInDataBuf, iIncludeAlpha, iDxtFlags);
183+
break;
184+
case 1:
185+
stb_compress_bc4_block(auOutDataBuf, auInDataBuf);
186+
break;
187+
case 2:
188+
stb_compress_bc5_block(auOutDataBuf, auInDataBuf);
189+
break;
190+
}
191+
memcpy(&puDataOut[uOffset], auOutDataBuf, 16);
192+
193+
uOffset += 16;
194+
if(uColumnStart >= uWrapPosH && uOverflowH > 0)
195+
puData += uBytesPerPixel * uOverflowH;
196+
else
197+
puData += uBytesPerPixel * 4;
198+
}
199+
puData += ptInfo->uWidth * uBytesPerPixel * 3; // by 3 since we already moved first row across
200+
}
201+
}
202+
}
203+
204+
//-----------------------------------------------------------------------------
205+
// [SECTION] extension loading
206+
//-----------------------------------------------------------------------------
207+
208+
PL_EXPORT void
209+
pl_load_dxt_ext(plApiRegistryI* ptApiRegistry, bool bReload)
210+
{
211+
const plDxtI tApi = {
212+
.compress = pl_dxt_compress
213+
};
214+
pl_set_api(ptApiRegistry, plDxtI, &tApi);
215+
}
216+
217+
PL_EXPORT void
218+
pl_unload_dxt_ext(plApiRegistryI* ptApiRegistry, bool bReload)
219+
{
220+
if(bReload)
221+
return;
222+
223+
const plDxtI* ptApi = pl_get_api_latest(ptApiRegistry, plDxtI);
224+
ptApiRegistry->remove_api(ptApi);
225+
}
226+
227+
//-----------------------------------------------------------------------------
228+
// [SECTION] unity build
229+
//-----------------------------------------------------------------------------
230+
231+
#ifndef PL_UNITY_BUILD
232+
233+
#define STB_DXT_IMPLEMENTATION
234+
#include "stb_dxt.h"
235+
#undef STB_DXT_IMPLEMENTATION
236+
237+
#endif

extensions/pl_dxt_ext.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
pl_dxt_ext.h
3+
*/
4+
5+
/*
6+
Index of this file:
7+
// [SECTION] header mess
8+
// [SECTION] APIs
9+
// [SECTION] includes
10+
// [SECTION] forward declarations
11+
// [SECTION] public api struct
12+
// [SECTION] structs
13+
// [SECTION] enums
14+
*/
15+
16+
//-----------------------------------------------------------------------------
17+
// [SECTION] header mess
18+
//-----------------------------------------------------------------------------
19+
20+
#ifndef PL_DXT_EXT_H
21+
#define PL_DXT_EXT_H
22+
23+
//-----------------------------------------------------------------------------
24+
// [SECTION] APIs
25+
//-----------------------------------------------------------------------------
26+
27+
#define plDxtI_version {0, 1, 0}
28+
29+
//-----------------------------------------------------------------------------
30+
// [SECTION] includes
31+
//-----------------------------------------------------------------------------
32+
33+
#include <stdint.h> // uint*_t
34+
#include <stddef.h> // size_t
35+
36+
//-----------------------------------------------------------------------------
37+
// [SECTION] forward declarations
38+
//-----------------------------------------------------------------------------
39+
40+
// basic types
41+
typedef struct _plDxtInfo plDxtInfo;
42+
43+
// flags/enums
44+
typedef int plDxtFlags;
45+
46+
//-----------------------------------------------------------------------------
47+
// [SECTION] public api struct
48+
//-----------------------------------------------------------------------------
49+
50+
typedef struct _plDxtI
51+
{
52+
void (*compress)(const plDxtInfo*, uint8_t* dataOut, size_t* sizeOut);
53+
} plDxtI;
54+
55+
//-----------------------------------------------------------------------------
56+
// [SECTION] structs
57+
//-----------------------------------------------------------------------------
58+
59+
typedef struct _plDxtInfo
60+
{
61+
plDxtFlags tFlags;
62+
uint32_t uWidth;
63+
uint32_t uHeight;
64+
uint32_t uChannels; // currently limited to 3 & 4
65+
const uint8_t* puData;
66+
} plDxtInfo;
67+
68+
//-----------------------------------------------------------------------------
69+
// [SECTION] enums
70+
//-----------------------------------------------------------------------------
71+
72+
enum _plDxtFlags
73+
{
74+
PL_DXT_FLAGS_NONE = 0,
75+
PL_DXT_FLAGS_HIGH_QUALITY = 1 << 0
76+
};
77+
78+
#endif // PL_DXT_EXT_H

extensions/pl_unity_ext.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Index of this file:
5353
#include "pl_datetime_ext.c"
5454
#include "pl_compress_ext.c"
5555
#include "pl_dds_ext.c"
56+
#include "pl_dxt_ext.c"
5657

5758
//-----------------------------------------------------------------------------
5859
// [SECTION] extension loading
@@ -103,6 +104,7 @@ pl_load_ext(plApiRegistryI* ptApiRegistry, bool bReload)
103104
gptDateTime = pl_get_api_latest(ptApiRegistry, plDateTimeI);
104105
gptCompress = pl_get_api_latest(ptApiRegistry, plCompressI);
105106
gptDds = pl_get_api_latest(ptApiRegistry, plDdsI);
107+
gptDxt = pl_get_api_latest(ptApiRegistry, plDxtI);
106108
gptIO = gptIOI->get_io();
107109

108110
pl_load_log_ext(ptApiRegistry, bReload);
@@ -143,6 +145,7 @@ pl_load_ext(plApiRegistryI* ptApiRegistry, bool bReload)
143145
pl_load_shader_variant_ext(ptApiRegistry, bReload);
144146
pl_load_compress_ext(ptApiRegistry, bReload);
145147
pl_load_dds_ext(ptApiRegistry, bReload);
148+
pl_load_dxt_ext(ptApiRegistry, bReload);
146149
}
147150

148151
PL_EXPORT void
@@ -184,6 +187,7 @@ pl_unload_ext(plApiRegistryI* ptApiRegistry, bool bReload)
184187
pl_unload_shader_variant_ext(ptApiRegistry, bReload);
185188
pl_unload_compress_ext(ptApiRegistry, bReload);
186189
pl_unload_dds_ext(ptApiRegistry, bReload);
190+
pl_unload_dxt_ext(ptApiRegistry, bReload);
187191
}
188192

189193
//-----------------------------------------------------------------------------
@@ -239,4 +243,8 @@ pl_unload_ext(plApiRegistryI* ptApiRegistry, bool bReload)
239243
#include "pl_json.h"
240244
#undef PL_JSON_IMPLEMENTATION
241245

246+
#define STB_DXT_IMPLEMENTATION
247+
#include "stb_dxt.h"
248+
#undef STB_DXT_IMPLEMENTATION
249+
242250
#undef CGLTF_IMPLEMENTATION

extensions/pl_unity_ext.inc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ static const struct _plShaderVariantI* gptShaderVariant = 0;
5656
static const struct _plVfsI* gptVfs = 0;
5757
static const struct _plCompressI* gptCompress = 0;
5858
static const struct _plDdsI* gptDds = 0;
59-
60-
59+
static const struct _plDxtI* gptDxt = 0;
6160

6261
// context
6362
static struct _plIO* gptIO = 0;

0 commit comments

Comments
 (0)