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
0 commit comments