mirror of
https://github.com/BinomialLLC/basis_universal.git
synced 2026-08-16 08:09:30 +00:00
174 lines
6.1 KiB
C++
174 lines
6.1 KiB
C++
#include "helpers.h"
|
|
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
|
|
//************************* See cubic/texture-dxt.cpp ************************/
|
|
|
|
/**
|
|
* Expands an RGB565 format colour into BGR888.
|
|
*
|
|
* \note Unlike the original code, which was RGB888, for a Windows bitmap we
|
|
* need this is as \e BGR.
|
|
*
|
|
* \param[in] color RGB565 format colour
|
|
* \return a \e bit \e expansion of the supplied colour as 8-bit per channel (with zero alpha)
|
|
*/
|
|
static uint32_t rgbFrom565(unsigned const color) {
|
|
unsigned r = (color >> 11) & 0x1F;
|
|
unsigned g = (color >> 5) & 0x3F;
|
|
unsigned b = (color >> 0) & 0x1F;
|
|
return (((r << 3) | (r >> 2)) << 16)
|
|
| (((g << 2) | (g >> 4)) << 8)
|
|
| (((b << 3) | (b >> 2)) << 0);
|
|
}
|
|
|
|
/**
|
|
* Calculates the DXT decompressor's \e midpoint colour, where the weighting
|
|
* is \c 2:1 (the parameters can be exchanged to calculate both midpoints).
|
|
* Used by the DXT block decode.
|
|
*
|
|
* \param[in] color0 \c endpoint colour receiving a double weighting
|
|
* \param[in] color1 \c endpoint colour receiving a single weighting
|
|
* \return blended colour (excluding alpha)
|
|
*/
|
|
static uint32_t midpoint21(uint32_t const color0, uint32_t const color1) {
|
|
return (((2 * (color0 & 0x0000FF) + (color1 & 0x0000FF)) / 3) & 0x0000FF)
|
|
| (((2 * (color0 & 0x00FF00) + (color1 & 0x00FF00)) / 3) & 0x00FF00)
|
|
| (((2 * (color0 & 0xFF0000) + (color1 & 0xFF0000)) / 3) & 0xFF0000);
|
|
}
|
|
|
|
/**
|
|
* Calculates the DXT decompressor's \e midpoint colour, where the weighting
|
|
* is \c 1:1. Used by the DXT block decode.
|
|
*
|
|
* \param[in] color0 \c endpoint colour (any of the endpoints)
|
|
* \param[in] color1 \c endpoint colour (any of the endpoints)
|
|
* \return blended colour (excluding alpha)
|
|
*/
|
|
static uint32_t midpoint11(uint32_t const color0, uint32_t const color1) {
|
|
return ((((color0 & 0x0000FF) + (color1 & 0x0000FF)) / 2) & 0x0000FF)
|
|
| ((((color0 & 0x00FF00) + (color1 & 0x00FF00)) / 2) & 0x00FF00)
|
|
| ((((color0 & 0xFF0000) + (color1 & 0xFF0000)) / 2) & 0xFF0000);
|
|
}
|
|
|
|
/**
|
|
* Decodes a DXT1 block.
|
|
*
|
|
* \note Unlike the original code, which was RGB888, for a Windows bitmap we
|
|
* need this is as \e BGR.
|
|
*
|
|
* \param[in] src start of the encoded data (eight contiguous bytes)
|
|
* \param[in] dst destination of the block's top-left pixel
|
|
* \param[in] span number of pixels to advance to the next line (usually the texture width)
|
|
*/
|
|
static void decodeDxt1(const uint8_t* const src, uint32_t* dst, unsigned const span) {
|
|
assert(src && dst && span);
|
|
/*
|
|
* Extract the two 16-bit 'endpoints'. These are little Endian, regardless
|
|
* of the platform. Note that the midpoint choices are made against these
|
|
* (not the platform Endian RGB/BGR versions) and that (specifically in
|
|
* this implementation) DXT1 will always have solid alpha (which we bake
|
|
* into the colour table). The color 'codes' are collated here into a
|
|
* 32-bit value (which simplifies addressing the bits directly later).
|
|
*/
|
|
#if defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)
|
|
unsigned const color0((src[0] << 0) | (src[1] << 8));
|
|
unsigned const color1((src[2] << 0) | (src[3] << 8));
|
|
unsigned const ccodes((src[4] << 0) | (src[5] << 8)
|
|
| (src[6] << 16) | (src[7] << 24));
|
|
#else
|
|
unsigned const color0(*reinterpret_cast<const uint16_t*>(src + 0));
|
|
unsigned const color1(*reinterpret_cast<const uint16_t*>(src + 2));
|
|
unsigned const ccodes(*reinterpret_cast<const uint32_t*>(src + 4));
|
|
#endif
|
|
uint32_t color[4];
|
|
color[0] = 0xFF000000 | rgbFrom565(color0);
|
|
color[1] = 0xFF000000 | rgbFrom565(color1);
|
|
color[2] = 0xFF000000 | ((color0 > color1)
|
|
? midpoint21(color[0], color[1])
|
|
: midpoint11(color[0], color[1]));
|
|
color[3] = 0xFF000000 | ((color0 > color1)
|
|
? midpoint21(color[1], color[0])
|
|
: 0);
|
|
/*
|
|
* The 4x4 block is unrolled. For destinations smaller than 4x4 the pixel
|
|
* overwrites here are inefficient, but the overhead isn't enough to worry
|
|
* about (when compared with the GL texture upload).
|
|
*/
|
|
dst[0] = color[(ccodes >> 0) & 0x03];
|
|
dst[1] = color[(ccodes >> 2) & 0x03];
|
|
dst[2] = color[(ccodes >> 4) & 0x03];
|
|
dst[3] = color[(ccodes >> 6) & 0x03];
|
|
dst += span;
|
|
dst[0] = color[(ccodes >> 8) & 0x03];
|
|
dst[1] = color[(ccodes >> 10) & 0x03];
|
|
dst[2] = color[(ccodes >> 12) & 0x03];
|
|
dst[3] = color[(ccodes >> 14) & 0x03];
|
|
dst += span;
|
|
dst[0] = color[(ccodes >> 16) & 0x03];
|
|
dst[1] = color[(ccodes >> 18) & 0x03];
|
|
dst[2] = color[(ccodes >> 20) & 0x03];
|
|
dst[3] = color[(ccodes >> 22) & 0x03];
|
|
dst += span;
|
|
dst[0] = color[(ccodes >> 24) & 0x03];
|
|
dst[1] = color[(ccodes >> 26) & 0x03];
|
|
dst[2] = color[(ccodes >> 28) & 0x03];
|
|
dst[3] = color[(ccodes >> 30) & 0x03];
|
|
}
|
|
|
|
//******************************** Public API ********************************/
|
|
|
|
void dprintf(char* const fmt, ...) {
|
|
va_list args;
|
|
char buf[256];
|
|
va_start(args, fmt);
|
|
int len = vsnprintf_s(buf, sizeof buf, fmt, args);
|
|
va_end (args);
|
|
if (len > 0) {
|
|
buf[sizeof buf - 1] = 0;
|
|
OutputDebugStringA(buf);
|
|
}
|
|
}
|
|
|
|
HBITMAP dxtToBitmap(const uint8_t* src, uint32_t const imgW, uint32_t const imgH, bool const flip) {
|
|
assert(src && imgW && imgH);
|
|
/*
|
|
* Creates a bitmap (a DIB) for the passed-in pixel size. Note that
|
|
* negation of the height means top-down, origin upper-left, which is the
|
|
* regular case.
|
|
*
|
|
* TODO: 16-bit variant instead?
|
|
* TODO: we're growing the nearest 4x4 but not cropping afterwards (what about shrinking and skipping the last blocks?)
|
|
*/
|
|
uint32_t nearW = (imgW + 3) & ~3;
|
|
uint32_t nearH = (imgH + 3) & ~3;
|
|
BITMAPINFO bmi = {
|
|
sizeof(bmi.bmiHeader)
|
|
};
|
|
bmi.bmiHeader.biWidth = nearW;
|
|
bmi.bmiHeader.biHeight = (flip) ? nearH : -static_cast<int32_t>(nearH);
|
|
bmi.bmiHeader.biPlanes = 1;
|
|
bmi.bmiHeader.biBitCount = 32;
|
|
bmi.bmiHeader.biCompression = BI_RGB;
|
|
void* pixels = NULL;
|
|
HBITMAP hbmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &pixels, NULL, 0);
|
|
/*
|
|
* Decode the BC1 blocks.
|
|
*/
|
|
if (hbmp && pixels) {
|
|
uint32_t* dst = static_cast<uint32_t*>(pixels);
|
|
for (unsigned y = 0; y < nearH; y += 4) {
|
|
uint32_t* row = dst;
|
|
for (unsigned x = 0; x < nearW; x += 4) {
|
|
decodeDxt1(src, row, nearW);
|
|
src += 8;
|
|
row += 4;
|
|
}
|
|
dst += 4 * nearW;
|
|
}
|
|
GdiFlush();
|
|
}
|
|
return hbmp;
|
|
}
|