Use libsquish to decompress DXT textures.
This commit is contained in:
parent
a75623f436
commit
3c175115eb
5 changed files with 47 additions and 328 deletions
329
core/image.cpp
329
core/image.cpp
|
@ -1370,7 +1370,7 @@ Error Image::load(const String &p_path) {
|
||||||
return ImageLoader::load_image(p_path, this);
|
return ImageLoader::load_image(p_path, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Error Image::save_png(const String &p_path) {
|
Error Image::save_png(const String &p_path) const {
|
||||||
|
|
||||||
if (save_png_func == NULL)
|
if (save_png_func == NULL)
|
||||||
return ERR_UNAVAILABLE;
|
return ERR_UNAVAILABLE;
|
||||||
|
@ -1391,337 +1391,14 @@ int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format
|
||||||
return mm;
|
return mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error Image::_decompress_bc() {
|
|
||||||
|
|
||||||
int wd = width, ht = height;
|
|
||||||
if (wd % 4 != 0) {
|
|
||||||
wd += 4 - (wd % 4);
|
|
||||||
}
|
|
||||||
if (ht % 4 != 0) {
|
|
||||||
ht += 4 - (ht % 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mm;
|
|
||||||
int size = _get_dst_image_size(wd, ht, FORMAT_RGBA8, mm);
|
|
||||||
|
|
||||||
PoolVector<uint8_t> newdata;
|
|
||||||
newdata.resize(size);
|
|
||||||
|
|
||||||
PoolVector<uint8_t>::Write w = newdata.write();
|
|
||||||
PoolVector<uint8_t>::Read r = data.read();
|
|
||||||
|
|
||||||
int rofs = 0;
|
|
||||||
int wofs = 0;
|
|
||||||
|
|
||||||
//print_line("width: "+itos(wd)+" height: "+itos(ht));
|
|
||||||
|
|
||||||
for (int i = 0; i <= mm; i++) {
|
|
||||||
|
|
||||||
switch (format) {
|
|
||||||
|
|
||||||
case FORMAT_DXT1: {
|
|
||||||
|
|
||||||
int len = (wd * ht) / 16;
|
|
||||||
uint8_t *dst = &w[wofs];
|
|
||||||
|
|
||||||
uint32_t ofs_table[16];
|
|
||||||
for (int x = 0; x < 4; x++) {
|
|
||||||
|
|
||||||
for (int y = 0; y < 4; y++) {
|
|
||||||
|
|
||||||
ofs_table[15 - (y * 4 + (3 - x))] = (x + y * wd) * 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = 0; j < len; j++) {
|
|
||||||
|
|
||||||
const uint8_t *src = &r[rofs + j * 8];
|
|
||||||
uint16_t col_a = src[1];
|
|
||||||
col_a <<= 8;
|
|
||||||
col_a |= src[0];
|
|
||||||
uint16_t col_b = src[3];
|
|
||||||
col_b <<= 8;
|
|
||||||
col_b |= src[2];
|
|
||||||
|
|
||||||
uint8_t table[4][4] = {
|
|
||||||
{ uint8_t((col_a >> 11) << 3), uint8_t(((col_a >> 5) & 0x3f) << 2), uint8_t(((col_a)&0x1f) << 3), 255 },
|
|
||||||
{ uint8_t((col_b >> 11) << 3), uint8_t(((col_b >> 5) & 0x3f) << 2), uint8_t(((col_b)&0x1f) << 3), 255 },
|
|
||||||
{ 0, 0, 0, 255 },
|
|
||||||
{ 0, 0, 0, 255 }
|
|
||||||
};
|
|
||||||
|
|
||||||
if (col_a < col_b) {
|
|
||||||
//punchrough
|
|
||||||
table[2][0] = (int(table[0][0]) + int(table[1][0])) >> 1;
|
|
||||||
table[2][1] = (int(table[0][1]) + int(table[1][1])) >> 1;
|
|
||||||
table[2][2] = (int(table[0][2]) + int(table[1][2])) >> 1;
|
|
||||||
table[3][3] = 0; //premul alpha black
|
|
||||||
} else {
|
|
||||||
//gradient
|
|
||||||
table[2][0] = (int(table[0][0]) * 2 + int(table[1][0])) / 3;
|
|
||||||
table[2][1] = (int(table[0][1]) * 2 + int(table[1][1])) / 3;
|
|
||||||
table[2][2] = (int(table[0][2]) * 2 + int(table[1][2])) / 3;
|
|
||||||
table[3][0] = (int(table[0][0]) + int(table[1][0]) * 2) / 3;
|
|
||||||
table[3][1] = (int(table[0][1]) + int(table[1][1]) * 2) / 3;
|
|
||||||
table[3][2] = (int(table[0][2]) + int(table[1][2]) * 2) / 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t block = src[4];
|
|
||||||
block <<= 8;
|
|
||||||
block |= src[5];
|
|
||||||
block <<= 8;
|
|
||||||
block |= src[6];
|
|
||||||
block <<= 8;
|
|
||||||
block |= src[7];
|
|
||||||
|
|
||||||
int y = (j / (wd / 4)) * 4;
|
|
||||||
int x = (j % (wd / 4)) * 4;
|
|
||||||
int pixofs = (y * wd + x) * 4;
|
|
||||||
|
|
||||||
for (int k = 0; k < 16; k++) {
|
|
||||||
int idx = pixofs + ofs_table[k];
|
|
||||||
dst[idx + 0] = table[block & 0x3][0];
|
|
||||||
dst[idx + 1] = table[block & 0x3][1];
|
|
||||||
dst[idx + 2] = table[block & 0x3][2];
|
|
||||||
dst[idx + 3] = table[block & 0x3][3];
|
|
||||||
block >>= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rofs += len * 8;
|
|
||||||
wofs += wd * ht * 4;
|
|
||||||
|
|
||||||
wd /= 2;
|
|
||||||
ht /= 2;
|
|
||||||
|
|
||||||
} break;
|
|
||||||
case FORMAT_DXT3: {
|
|
||||||
|
|
||||||
int len = (wd * ht) / 16;
|
|
||||||
uint8_t *dst = &w[wofs];
|
|
||||||
|
|
||||||
uint32_t ofs_table[16];
|
|
||||||
for (int x = 0; x < 4; x++) {
|
|
||||||
|
|
||||||
for (int y = 0; y < 4; y++) {
|
|
||||||
|
|
||||||
ofs_table[15 - (y * 4 + (3 - x))] = (x + y * wd) * 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = 0; j < len; j++) {
|
|
||||||
|
|
||||||
const uint8_t *src = &r[rofs + j * 16];
|
|
||||||
|
|
||||||
uint64_t ablock = src[1];
|
|
||||||
ablock <<= 8;
|
|
||||||
ablock |= src[0];
|
|
||||||
ablock <<= 8;
|
|
||||||
ablock |= src[3];
|
|
||||||
ablock <<= 8;
|
|
||||||
ablock |= src[2];
|
|
||||||
ablock <<= 8;
|
|
||||||
ablock |= src[5];
|
|
||||||
ablock <<= 8;
|
|
||||||
ablock |= src[4];
|
|
||||||
ablock <<= 8;
|
|
||||||
ablock |= src[7];
|
|
||||||
ablock <<= 8;
|
|
||||||
ablock |= src[6];
|
|
||||||
|
|
||||||
uint16_t col_a = src[8 + 1];
|
|
||||||
col_a <<= 8;
|
|
||||||
col_a |= src[8 + 0];
|
|
||||||
uint16_t col_b = src[8 + 3];
|
|
||||||
col_b <<= 8;
|
|
||||||
col_b |= src[8 + 2];
|
|
||||||
|
|
||||||
uint8_t table[4][4] = {
|
|
||||||
{ uint8_t((col_a >> 11) << 3), uint8_t(((col_a >> 5) & 0x3f) << 2), uint8_t(((col_a)&0x1f) << 3), 255 },
|
|
||||||
{ uint8_t((col_b >> 11) << 3), uint8_t(((col_b >> 5) & 0x3f) << 2), uint8_t(((col_b)&0x1f) << 3), 255 },
|
|
||||||
|
|
||||||
{ 0, 0, 0, 255 },
|
|
||||||
{ 0, 0, 0, 255 }
|
|
||||||
};
|
|
||||||
|
|
||||||
//always gradient
|
|
||||||
table[2][0] = (int(table[0][0]) * 2 + int(table[1][0])) / 3;
|
|
||||||
table[2][1] = (int(table[0][1]) * 2 + int(table[1][1])) / 3;
|
|
||||||
table[2][2] = (int(table[0][2]) * 2 + int(table[1][2])) / 3;
|
|
||||||
table[3][0] = (int(table[0][0]) + int(table[1][0]) * 2) / 3;
|
|
||||||
table[3][1] = (int(table[0][1]) + int(table[1][1]) * 2) / 3;
|
|
||||||
table[3][2] = (int(table[0][2]) + int(table[1][2]) * 2) / 3;
|
|
||||||
|
|
||||||
uint32_t block = src[4 + 8];
|
|
||||||
block <<= 8;
|
|
||||||
block |= src[5 + 8];
|
|
||||||
block <<= 8;
|
|
||||||
block |= src[6 + 8];
|
|
||||||
block <<= 8;
|
|
||||||
block |= src[7 + 8];
|
|
||||||
|
|
||||||
int y = (j / (wd / 4)) * 4;
|
|
||||||
int x = (j % (wd / 4)) * 4;
|
|
||||||
int pixofs = (y * wd + x) * 4;
|
|
||||||
|
|
||||||
for (int k = 0; k < 16; k++) {
|
|
||||||
uint8_t alpha = ablock & 0xf;
|
|
||||||
alpha = int(alpha) * 255 / 15; //right way for alpha
|
|
||||||
int idx = pixofs + ofs_table[k];
|
|
||||||
dst[idx + 0] = table[block & 0x3][0];
|
|
||||||
dst[idx + 1] = table[block & 0x3][1];
|
|
||||||
dst[idx + 2] = table[block & 0x3][2];
|
|
||||||
dst[idx + 3] = alpha;
|
|
||||||
block >>= 2;
|
|
||||||
ablock >>= 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rofs += len * 16;
|
|
||||||
wofs += wd * ht * 4;
|
|
||||||
|
|
||||||
wd /= 2;
|
|
||||||
ht /= 2;
|
|
||||||
|
|
||||||
} break;
|
|
||||||
case FORMAT_DXT5: {
|
|
||||||
|
|
||||||
int len = (wd * ht) / 16;
|
|
||||||
uint8_t *dst = &w[wofs];
|
|
||||||
|
|
||||||
uint32_t ofs_table[16];
|
|
||||||
for (int x = 0; x < 4; x++) {
|
|
||||||
|
|
||||||
for (int y = 0; y < 4; y++) {
|
|
||||||
|
|
||||||
ofs_table[15 - (y * 4 + (3 - x))] = (x + y * wd) * 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = 0; j < len; j++) {
|
|
||||||
|
|
||||||
const uint8_t *src = &r[rofs + j * 16];
|
|
||||||
|
|
||||||
uint8_t a_start = src[1];
|
|
||||||
uint8_t a_end = src[0];
|
|
||||||
|
|
||||||
uint64_t ablock = src[3];
|
|
||||||
ablock <<= 8;
|
|
||||||
ablock |= src[2];
|
|
||||||
ablock <<= 8;
|
|
||||||
ablock |= src[5];
|
|
||||||
ablock <<= 8;
|
|
||||||
ablock |= src[4];
|
|
||||||
ablock <<= 8;
|
|
||||||
ablock |= src[7];
|
|
||||||
ablock <<= 8;
|
|
||||||
ablock |= src[6];
|
|
||||||
|
|
||||||
uint8_t atable[8];
|
|
||||||
|
|
||||||
if (a_start > a_end) {
|
|
||||||
|
|
||||||
atable[0] = (int(a_start) * 7 + int(a_end) * 0) / 7;
|
|
||||||
atable[1] = (int(a_start) * 6 + int(a_end) * 1) / 7;
|
|
||||||
atable[2] = (int(a_start) * 5 + int(a_end) * 2) / 7;
|
|
||||||
atable[3] = (int(a_start) * 4 + int(a_end) * 3) / 7;
|
|
||||||
atable[4] = (int(a_start) * 3 + int(a_end) * 4) / 7;
|
|
||||||
atable[5] = (int(a_start) * 2 + int(a_end) * 5) / 7;
|
|
||||||
atable[6] = (int(a_start) * 1 + int(a_end) * 6) / 7;
|
|
||||||
atable[7] = (int(a_start) * 0 + int(a_end) * 7) / 7;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
atable[0] = (int(a_start) * 5 + int(a_end) * 0) / 5;
|
|
||||||
atable[1] = (int(a_start) * 4 + int(a_end) * 1) / 5;
|
|
||||||
atable[2] = (int(a_start) * 3 + int(a_end) * 2) / 5;
|
|
||||||
atable[3] = (int(a_start) * 2 + int(a_end) * 3) / 5;
|
|
||||||
atable[4] = (int(a_start) * 1 + int(a_end) * 4) / 5;
|
|
||||||
atable[5] = (int(a_start) * 0 + int(a_end) * 5) / 5;
|
|
||||||
atable[6] = 0;
|
|
||||||
atable[7] = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t col_a = src[8 + 1];
|
|
||||||
col_a <<= 8;
|
|
||||||
col_a |= src[8 + 0];
|
|
||||||
uint16_t col_b = src[8 + 3];
|
|
||||||
col_b <<= 8;
|
|
||||||
col_b |= src[8 + 2];
|
|
||||||
|
|
||||||
uint8_t table[4][4] = {
|
|
||||||
{ uint8_t((col_a >> 11) << 3), uint8_t(((col_a >> 5) & 0x3f) << 2), uint8_t(((col_a)&0x1f) << 3), 255 },
|
|
||||||
{ uint8_t((col_b >> 11) << 3), uint8_t(((col_b >> 5) & 0x3f) << 2), uint8_t(((col_b)&0x1f) << 3), 255 },
|
|
||||||
|
|
||||||
{ 0, 0, 0, 255 },
|
|
||||||
{ 0, 0, 0, 255 }
|
|
||||||
};
|
|
||||||
|
|
||||||
//always gradient
|
|
||||||
table[2][0] = (int(table[0][0]) * 2 + int(table[1][0])) / 3;
|
|
||||||
table[2][1] = (int(table[0][1]) * 2 + int(table[1][1])) / 3;
|
|
||||||
table[2][2] = (int(table[0][2]) * 2 + int(table[1][2])) / 3;
|
|
||||||
table[3][0] = (int(table[0][0]) + int(table[1][0]) * 2) / 3;
|
|
||||||
table[3][1] = (int(table[0][1]) + int(table[1][1]) * 2) / 3;
|
|
||||||
table[3][2] = (int(table[0][2]) + int(table[1][2]) * 2) / 3;
|
|
||||||
|
|
||||||
uint32_t block = src[4 + 8];
|
|
||||||
block <<= 8;
|
|
||||||
block |= src[5 + 8];
|
|
||||||
block <<= 8;
|
|
||||||
block |= src[6 + 8];
|
|
||||||
block <<= 8;
|
|
||||||
block |= src[7 + 8];
|
|
||||||
|
|
||||||
int y = (j / (wd / 4)) * 4;
|
|
||||||
int x = (j % (wd / 4)) * 4;
|
|
||||||
int pixofs = (y * wd + x) * 4;
|
|
||||||
|
|
||||||
for (int k = 0; k < 16; k++) {
|
|
||||||
uint8_t alpha = ablock & 0x7;
|
|
||||||
int idx = pixofs + ofs_table[k];
|
|
||||||
dst[idx + 0] = table[block & 0x3][0];
|
|
||||||
dst[idx + 1] = table[block & 0x3][1];
|
|
||||||
dst[idx + 2] = table[block & 0x3][2];
|
|
||||||
dst[idx + 3] = atable[alpha];
|
|
||||||
block >>= 2;
|
|
||||||
ablock >>= 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rofs += len * 16;
|
|
||||||
wofs += wd * ht * 4;
|
|
||||||
|
|
||||||
wd /= 2;
|
|
||||||
ht /= 2;
|
|
||||||
|
|
||||||
} break;
|
|
||||||
default: {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
w = PoolVector<uint8_t>::Write();
|
|
||||||
r = PoolVector<uint8_t>::Read();
|
|
||||||
|
|
||||||
data = newdata;
|
|
||||||
format = FORMAT_RGBA8;
|
|
||||||
if (wd != width || ht != height) {
|
|
||||||
|
|
||||||
SWAP(width, wd);
|
|
||||||
SWAP(height, ht);
|
|
||||||
crop(wd, ht);
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Image::is_compressed() const {
|
bool Image::is_compressed() const {
|
||||||
return format >= FORMAT_RGB565;
|
return format >= FORMAT_RGB565;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error Image::decompress() {
|
Error Image::decompress() {
|
||||||
|
|
||||||
if (format >= FORMAT_DXT1 && format <= FORMAT_ATI2)
|
if (format >= FORMAT_DXT1 && format <= FORMAT_ATI2 && _image_decompress_bc)
|
||||||
_decompress_bc(); //_image_decompress_bc(this);
|
_image_decompress_bc(this);
|
||||||
else if (format >= FORMAT_PVRTC2 && format <= FORMAT_PVRTC4A && _image_decompress_pvrtc)
|
else if (format >= FORMAT_PVRTC2 && format <= FORMAT_PVRTC4A && _image_decompress_pvrtc)
|
||||||
_image_decompress_pvrtc(this);
|
_image_decompress_pvrtc(this);
|
||||||
else if (format == FORMAT_ETC && _image_decompress_etc)
|
else if (format == FORMAT_ETC && _image_decompress_etc)
|
||||||
|
|
|
@ -227,7 +227,7 @@ public:
|
||||||
PoolVector<uint8_t> get_data() const;
|
PoolVector<uint8_t> get_data() const;
|
||||||
|
|
||||||
Error load(const String &p_path);
|
Error load(const String &p_path);
|
||||||
Error save_png(const String &p_path);
|
Error save_png(const String &p_path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create an empty image
|
* create an empty image
|
||||||
|
|
|
@ -39,6 +39,46 @@
|
||||||
|
|
||||||
#include <squish.h>
|
#include <squish.h>
|
||||||
|
|
||||||
|
void image_decompress_squish(Image *p_image) {
|
||||||
|
int w = p_image->get_width();
|
||||||
|
int h = p_image->get_height();
|
||||||
|
|
||||||
|
Image::Format target_format = Image::FORMAT_RGBA8;
|
||||||
|
PoolVector<uint8_t> data;
|
||||||
|
int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps() ? -1 : 0);
|
||||||
|
int mm_count = p_image->get_mipmap_count();
|
||||||
|
data.resize(target_size);
|
||||||
|
|
||||||
|
PoolVector<uint8_t>::Read rb = p_image->get_data().read();
|
||||||
|
PoolVector<uint8_t>::Write wb = data.write();
|
||||||
|
|
||||||
|
int squish_flags = Image::FORMAT_MAX;
|
||||||
|
if (p_image->get_format() == Image::FORMAT_DXT1) {
|
||||||
|
squish_flags = squish::kDxt1;
|
||||||
|
} else if (p_image->get_format() == Image::FORMAT_DXT3) {
|
||||||
|
squish_flags = squish::kDxt3;
|
||||||
|
} else if (p_image->get_format() == Image::FORMAT_DXT5) {
|
||||||
|
squish_flags = squish::kDxt5;
|
||||||
|
} else if (p_image->get_format() == Image::FORMAT_ATI1) {
|
||||||
|
squish_flags = squish::kBc4;
|
||||||
|
} else if (p_image->get_format() == Image::FORMAT_ATI2) {
|
||||||
|
squish_flags = squish::kBc5;
|
||||||
|
} else {
|
||||||
|
ERR_FAIL_COND(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dst_ofs = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i <= mm_count; i++) {
|
||||||
|
int src_ofs = 0, mipmap_size = 0, mipmap_w = 0, mipmap_h = 0;
|
||||||
|
p_image->get_mipmap_offset_size_and_dimensions(i, src_ofs, mipmap_size, mipmap_w, mipmap_h);
|
||||||
|
squish::DecompressImage(&wb[dst_ofs], mipmap_w, mipmap_h, &rb[src_ofs], squish_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
|
||||||
|
}
|
||||||
|
|
||||||
void image_compress_squish(Image *p_image) {
|
void image_compress_squish(Image *p_image) {
|
||||||
|
|
||||||
int w = p_image->get_width();
|
int w = p_image->get_width();
|
||||||
|
@ -56,7 +96,7 @@ void image_compress_squish(Image *p_image) {
|
||||||
return; //do not compress, already compressed
|
return; //do not compress, already compressed
|
||||||
|
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
int squish_comp = squish::kColourRangeFit;
|
int squish_comp = squish::kColourRangeFit; // TODO: use lossy quality setting to determine the quality
|
||||||
Image::Format target_format;
|
Image::Format target_format;
|
||||||
|
|
||||||
if (p_image->get_format() == Image::FORMAT_LA8) {
|
if (p_image->get_format() == Image::FORMAT_LA8) {
|
||||||
|
|
|
@ -33,5 +33,6 @@
|
||||||
#include "image.h"
|
#include "image.h"
|
||||||
|
|
||||||
void image_compress_squish(Image *p_image);
|
void image_compress_squish(Image *p_image);
|
||||||
|
void image_decompress_squish(Image *p_image);
|
||||||
|
|
||||||
#endif // IMAGE_COMPRESS_SQUISH_H
|
#endif // IMAGE_COMPRESS_SQUISH_H
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
void register_squish_types() {
|
void register_squish_types() {
|
||||||
|
|
||||||
Image::set_compress_bc_func(image_compress_squish);
|
Image::set_compress_bc_func(image_compress_squish);
|
||||||
|
Image::_image_decompress_bc = image_decompress_squish;
|
||||||
}
|
}
|
||||||
|
|
||||||
void unregister_squish_types() {}
|
void unregister_squish_types() {}
|
||||||
|
|
Loading…
Reference in a new issue