From 6728f5bd546da4421ca689599ef6bcf0b29919a2 Mon Sep 17 00:00:00 2001 From: Raghav-B Date: Sat, 28 Dec 2024 12:15:03 +0800 Subject: [PATCH 1/2] Add support for DDS files Current supported DDS formats are in parity with Python Pillow --- src/JPEGView/BcnDecode.cpp | 960 ++++++++++++++++++++++++++ src/JPEGView/BcnDecode.h | 28 + src/JPEGView/DDSWrapper.cpp | 427 ++++++++++++ src/JPEGView/DDSWrapper.h | 225 ++++++ src/JPEGView/FileExtensionsDlg.cpp | 1 + src/JPEGView/FileList.cpp | 4 +- src/JPEGView/ImageLoadThread.cpp | 17 + src/JPEGView/ImageLoadThread.h | 1 + src/JPEGView/ImageProcessingTypes.h | 1 + src/JPEGView/JPEGView.vcxproj | 4 + src/JPEGView/JPEGView.vcxproj.filters | 18 + 11 files changed, 1684 insertions(+), 2 deletions(-) create mode 100644 src/JPEGView/BcnDecode.cpp create mode 100644 src/JPEGView/BcnDecode.h create mode 100644 src/JPEGView/DDSWrapper.cpp create mode 100644 src/JPEGView/DDSWrapper.h diff --git a/src/JPEGView/BcnDecode.cpp b/src/JPEGView/BcnDecode.cpp new file mode 100644 index 00000000..8a86d0c0 --- /dev/null +++ b/src/JPEGView/BcnDecode.cpp @@ -0,0 +1,960 @@ +/* + * ADAPTED FROM: + * + * The Python Imaging Library + * + * decoder for DXTn-compressed data + * + * Format documentation: + * https://web.archive.org/web/20170802060935/http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt + * + * The contents of this file are in the public domain (CC0) + * Full text of the CC0 license: + * https://creativecommons.org/publicdomain/zero/1.0/ + * + * + * CHANGES MADE: + * - Replaced PIL-specific structs + * - ImagingBcnDecode() writes to a 1D flattened array + * - All uncompressed images are outputted as BGRA to accommodate JPEGView + * - Removed edge case handling for lowest mipmap level support; we only show the first/biggest mipmap + */ + +#include "stdafx.h" +#include "BcnDecode.h" + +#include +#include + +typedef struct { + uint8_t r, g, b, a; +} rgba; + +typedef struct { + uint8_t l; +} lum; + +typedef struct { + uint16_t c0, c1; + uint32_t lut; +} bc1_color; + +typedef struct { + uint8_t a0, a1; + uint8_t lut[6]; +} bc3_alpha; + +typedef struct { + int8_t a0, a1; + uint8_t lut[6]; +} bc5s_alpha; + +#define LOAD16(p) (p)[0] | ((p)[1] << 8) + +#define LOAD32(p) (p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24) + +static void +bc1_color_load(bc1_color* dst, const uint8_t* src) { + dst->c0 = LOAD16(src); + dst->c1 = LOAD16(src + 2); + dst->lut = LOAD32(src + 4); +} + +static rgba +decode_565(uint16_t x) { + rgba c; + int r, g, b; + r = (x & 0xf800) >> 8; + r |= r >> 5; + c.r = r; + g = (x & 0x7e0) >> 3; + g |= g >> 6; + c.g = g; + b = (x & 0x1f) << 3; + b |= b >> 5; + c.b = b; + c.a = 0xff; + return c; +} + +static void +decode_bc1_color(rgba* dst, const uint8_t* src, int separate_alpha) { + bc1_color col; + rgba p[4]; + int n, cw; + uint16_t r0, g0, b0, r1, g1, b1; + bc1_color_load(&col, src); + + p[0] = decode_565(col.c0); + r0 = p[0].r; + g0 = p[0].g; + b0 = p[0].b; + p[1] = decode_565(col.c1); + r1 = p[1].r; + g1 = p[1].g; + b1 = p[1].b; + + /* NOTE: BC2 and BC3 reuse BC1 color blocks but always act like c0 > c1 */ + if (col.c0 > col.c1 || separate_alpha) { + p[2].r = (2 * r0 + 1 * r1) / 3; + p[2].g = (2 * g0 + 1 * g1) / 3; + p[2].b = (2 * b0 + 1 * b1) / 3; + p[2].a = 0xff; + p[3].r = (1 * r0 + 2 * r1) / 3; + p[3].g = (1 * g0 + 2 * g1) / 3; + p[3].b = (1 * b0 + 2 * b1) / 3; + p[3].a = 0xff; + } + else { + p[2].r = (r0 + r1) / 2; + p[2].g = (g0 + g1) / 2; + p[2].b = (b0 + b1) / 2; + p[2].a = 0xff; + p[3].r = 0; + p[3].g = 0; + p[3].b = 0; + p[3].a = 0; + } + for (n = 0; n < 16; n++) { + cw = 3 & (col.lut >> (2 * n)); + dst[n] = p[cw]; + } +} + +static void +decode_bc3_alpha(char* dst, const uint8_t* src, int stride, int o, int sign) { + uint16_t a0, a1; + uint8_t a[8]; + int n, lut1, lut2, aw; + if (sign == 1) { + bc5s_alpha b; + memcpy(&b, src, sizeof(bc5s_alpha)); + a0 = b.a0 + 128; + a1 = b.a1 + 128; + lut1 = b.lut[0] | (b.lut[1] << 8) | (b.lut[2] << 16); + lut2 = b.lut[3] | (b.lut[4] << 8) | (b.lut[5] << 16); + } + else { + bc3_alpha b; + memcpy(&b, src, sizeof(bc3_alpha)); + a0 = b.a0; + a1 = b.a1; + lut1 = b.lut[0] | (b.lut[1] << 8) | (b.lut[2] << 16); + lut2 = b.lut[3] | (b.lut[4] << 8) | (b.lut[5] << 16); + } + + a[0] = (uint8_t)a0; + a[1] = (uint8_t)a1; + if (a0 > a1) { + a[2] = (6 * a0 + 1 * a1) / 7; + a[3] = (5 * a0 + 2 * a1) / 7; + a[4] = (4 * a0 + 3 * a1) / 7; + a[5] = (3 * a0 + 4 * a1) / 7; + a[6] = (2 * a0 + 5 * a1) / 7; + a[7] = (1 * a0 + 6 * a1) / 7; + } + else { + a[2] = (4 * a0 + 1 * a1) / 5; + a[3] = (3 * a0 + 2 * a1) / 5; + a[4] = (2 * a0 + 3 * a1) / 5; + a[5] = (1 * a0 + 4 * a1) / 5; + a[6] = 0; + a[7] = 0xff; + } + for (n = 0; n < 8; n++) { + aw = 7 & (lut1 >> (3 * n)); + dst[stride * n + o] = a[aw]; + } + for (n = 0; n < 8; n++) { + aw = 7 & (lut2 >> (3 * n)); + dst[stride * (8 + n) + o] = a[aw]; + } +} + +static void +decode_bc1_block(rgba* col, const uint8_t* src) { + decode_bc1_color(col, src, 0); +} + +static void +decode_bc2_block(rgba* col, const uint8_t* src) { + int n, bitI, byI, av; + decode_bc1_color(col, src + 8, 1); + for (n = 0; n < 16; n++) { + bitI = n * 4; + byI = bitI >> 3; + av = 0xf & (src[byI] >> (bitI & 7)); + av = (av << 4) | av; + col[n].a = av; + } +} + +static void +decode_bc3_block(rgba* col, const uint8_t* src) { + decode_bc1_color(col, src + 8, 1); + decode_bc3_alpha((char*)col, src, sizeof(col[0]), 3, 0); +} + +static void +decode_bc4_block(lum* col, const uint8_t* src) { + decode_bc3_alpha((char*)col, src, sizeof(col[0]), 0, 0); +} + +static void +decode_bc5_block(rgba* col, const uint8_t* src, int sign) { + decode_bc3_alpha((char*)col, src, sizeof(col[0]), 0, sign); + decode_bc3_alpha((char*)col, src + 8, sizeof(col[0]), 1, sign); +} + +/* BC6 and BC7 are described here: + https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_compression_bptc.txt + */ + +static uint8_t +get_bit(const uint8_t* src, int bit) { + int by = bit >> 3; + bit &= 7; + return (src[by] >> bit) & 1; +} + +static uint8_t +get_bits(const uint8_t* src, int bit, int count) { + uint8_t v; + int x; + int by = bit >> 3; + bit &= 7; + if (!count) { + return 0; + } + if (bit + count <= 8) { + v = (src[by] >> bit) & ((1 << count) - 1); + } + else { + x = src[by] | (src[by + 1] << 8); + v = (x >> bit) & ((1 << count) - 1); + } + return v; +} + +/* BC7 */ +typedef struct { + char ns; + char pb; + char rb; + char isb; + char cb; + char ab; + char epb; + char spb; + char ib; + char ib2; +} bc7_mode_info; + +static const bc7_mode_info bc7_modes[] = { + {3, 4, 0, 0, 4, 0, 1, 0, 3, 0}, + {2, 6, 0, 0, 6, 0, 0, 1, 3, 0}, + {3, 6, 0, 0, 5, 0, 0, 0, 2, 0}, + {2, 6, 0, 0, 7, 0, 1, 0, 2, 0}, + {1, 0, 2, 1, 5, 6, 0, 0, 2, 3}, + {1, 0, 2, 0, 7, 8, 0, 0, 2, 2}, + {1, 0, 0, 0, 7, 7, 1, 0, 4, 0}, + {2, 6, 0, 0, 5, 5, 1, 0, 2, 0} +}; + +/* Subset indices: + Table.P2, 1 bit per index */ +static const uint16_t bc7_si2[] = { + 0xcccc, 0x8888, 0xeeee, 0xecc8, 0xc880, 0xfeec, 0xfec8, 0xec80, 0xc800, 0xffec, + 0xfe80, 0xe800, 0xffe8, 0xff00, 0xfff0, 0xf000, 0xf710, 0x008e, 0x7100, 0x08ce, + 0x008c, 0x7310, 0x3100, 0x8cce, 0x088c, 0x3110, 0x6666, 0x366c, 0x17e8, 0x0ff0, + 0x718e, 0x399c, 0xaaaa, 0xf0f0, 0x5a5a, 0x33cc, 0x3c3c, 0x55aa, 0x9696, 0xa55a, + 0x73ce, 0x13c8, 0x324c, 0x3bdc, 0x6996, 0xc33c, 0x9966, 0x0660, 0x0272, 0x04e4, + 0x4e40, 0x2720, 0xc936, 0x936c, 0x39c6, 0x639c, 0x9336, 0x9cc6, 0x817e, 0xe718, + 0xccf0, 0x0fcc, 0x7744, 0xee22 +}; + +/* Table.P3, 2 bits per index */ +static const uint32_t bc7_si3[] = { + 0xaa685050, 0x6a5a5040, 0x5a5a4200, 0x5450a0a8, 0xa5a50000, 0xa0a05050, 0x5555a0a0, + 0x5a5a5050, 0xaa550000, 0xaa555500, 0xaaaa5500, 0x90909090, 0x94949494, 0xa4a4a4a4, + 0xa9a59450, 0x2a0a4250, 0xa5945040, 0x0a425054, 0xa5a5a500, 0x55a0a0a0, 0xa8a85454, + 0x6a6a4040, 0xa4a45000, 0x1a1a0500, 0x0050a4a4, 0xaaa59090, 0x14696914, 0x69691400, + 0xa08585a0, 0xaa821414, 0x50a4a450, 0x6a5a0200, 0xa9a58000, 0x5090a0a8, 0xa8a09050, + 0x24242424, 0x00aa5500, 0x24924924, 0x24499224, 0x50a50a50, 0x500aa550, 0xaaaa4444, + 0x66660000, 0xa5a0a5a0, 0x50a050a0, 0x69286928, 0x44aaaa44, 0x66666600, 0xaa444444, + 0x54a854a8, 0x95809580, 0x96969600, 0xa85454a8, 0x80959580, 0xaa141414, 0x96960000, + 0xaaaa1414, 0xa05050a0, 0xa0a5a5a0, 0x96000000, 0x40804080, 0xa9a8a9a8, 0xaaaaaa44, + 0x2a4a5254 +}; + +/* Anchor indices: + Table.A2 */ +static const char bc7_ai0[] = { 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 2, 8, 2, 2, 8, 8, 15, 2, 8, + 2, 2, 8, 8, 2, 2, 15, 15, 6, 8, 2, 8, 15, + 15, 2, 8, 2, 2, 2, 15, 15, 6, 6, 2, 6, 8, + 15, 15, 2, 2, 15, 15, 15, 15, 15, 2, 2, 15 }; + +/* Table.A3a */ +static const char bc7_ai1[] = { 3, 3, 15, 15, 8, 3, 15, 15, 8, 8, 6, 6, 6, + 5, 3, 3, 3, 3, 8, 15, 3, 3, 6, 10, 5, 8, + 8, 6, 8, 5, 15, 15, 8, 15, 3, 5, 6, 10, 8, + 15, 15, 3, 15, 5, 15, 15, 15, 15, 3, 15, 5, 5, + 5, 8, 5, 10, 5, 10, 8, 13, 15, 12, 3, 3 }; + +/* Table.A3b */ +static const char bc7_ai2[] = { 15, 8, 8, 3, 15, 15, 3, 8, 15, 15, 15, 15, 15, + 15, 15, 8, 15, 8, 15, 3, 15, 8, 15, 8, 3, 15, + 6, 10, 15, 15, 10, 8, 15, 3, 15, 10, 10, 8, 9, + 10, 6, 15, 8, 15, 3, 6, 6, 8, 15, 3, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 3, 15, 15, 8 }; + +/* Interpolation weights */ +static const char bc7_weights2[] = { 0, 21, 43, 64 }; +static const char bc7_weights3[] = { 0, 9, 18, 27, 37, 46, 55, 64 }; +static const char bc7_weights4[] = { + 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 +}; + +static const char* +bc7_get_weights(int n) { + if (n == 2) { + return bc7_weights2; + } + if (n == 3) { + return bc7_weights3; + } + return bc7_weights4; +} + +static int +bc7_get_subset(int ns, int partition, int n) { + if (ns == 2) { + return 1 & (bc7_si2[partition] >> n); + } + if (ns == 3) { + return 3 & (bc7_si3[partition] >> (2 * n)); + } + return 0; +} + +static uint8_t +expand_quantized(uint8_t v, int bits) { + v = v << (8 - bits); + return v | (v >> bits); +} + +static void +bc7_lerp(rgba* dst, const rgba* e, int s0, int s1) { + int t0 = 64 - s0; + int t1 = 64 - s1; + dst->r = (uint8_t)((t0 * e[0].r + s0 * e[1].r + 32) >> 6); + dst->g = (uint8_t)((t0 * e[0].g + s0 * e[1].g + 32) >> 6); + dst->b = (uint8_t)((t0 * e[0].b + s0 * e[1].b + 32) >> 6); + dst->a = (uint8_t)((t1 * e[0].a + s1 * e[1].a + 32) >> 6); +} + +static void +decode_bc7_block(rgba* col, const uint8_t* src) { + rgba endpoints[6]; + int bit = 0, cibit, aibit; + int mode = src[0]; + int i, j; + int numep, cb, ab, ib, ib2, i0, i1, s; + uint8_t index_sel, partition, rotation, val; + const char* cw, * aw; + const bc7_mode_info* info; + + /* mode is the number of unset bits before the first set bit: */ + if (!mode) { + /* degenerate case when no bits set */ + for (i = 0; i < 16; i++) { + col[i].r = col[i].g = col[i].b = 0; + col[i].a = 255; + } + return; + } + while (!(mode & (1 << bit++))); + mode = bit - 1; + info = &bc7_modes[mode]; + /* color selection bits: {subset}{endpoint} */ + cb = info->cb; + ab = info->ab; + cw = bc7_get_weights(info->ib); + aw = bc7_get_weights((ab && info->ib2) ? info->ib2 : info->ib); + +#define LOAD(DST, N) \ + DST = get_bits(src, bit, N); \ + bit += N; + LOAD(partition, info->pb); + LOAD(rotation, info->rb); + LOAD(index_sel, info->isb); + numep = info->ns << 1; + + /* red */ + for (i = 0; i < numep; i++) { + LOAD(val, cb); + endpoints[i].r = val; + } + + /* green */ + for (i = 0; i < numep; i++) { + LOAD(val, cb); + endpoints[i].g = val; + } + + /* blue */ + for (i = 0; i < numep; i++) { + LOAD(val, cb); + endpoints[i].b = val; + } + + /* alpha */ + for (i = 0; i < numep; i++) { + if (ab) { + LOAD(val, ab); + } + else { + val = 255; + } + endpoints[i].a = val; + } + + /* p-bits */ +#define ASSIGN_P(x) x = (x << 1) | val + if (info->epb) { + /* per endpoint */ + cb++; + if (ab) { + ab++; + } + for (i = 0; i < numep; i++) { + LOAD(val, 1); + ASSIGN_P(endpoints[i].r); + ASSIGN_P(endpoints[i].g); + ASSIGN_P(endpoints[i].b); + if (ab) { + ASSIGN_P(endpoints[i].a); + } + } + } + if (info->spb) { + /* per subset */ + cb++; + if (ab) { + ab++; + } + for (i = 0; i < numep; i += 2) { + LOAD(val, 1); + for (j = 0; j < 2; j++) { + ASSIGN_P(endpoints[i + j].r); + ASSIGN_P(endpoints[i + j].g); + ASSIGN_P(endpoints[i + j].b); + if (ab) { + ASSIGN_P(endpoints[i + j].a); + } + } + } + } +#undef ASSIGN_P +#define EXPAND(x, b) x = expand_quantized(x, b) + for (i = 0; i < numep; i++) { + EXPAND(endpoints[i].r, cb); + EXPAND(endpoints[i].g, cb); + EXPAND(endpoints[i].b, cb); + if (ab) { + EXPAND(endpoints[i].a, ab); + } + } +#undef EXPAND +#undef LOAD + cibit = bit; + aibit = cibit + 16 * info->ib - info->ns; + for (i = 0; i < 16; i++) { + s = bc7_get_subset(info->ns, partition, i) << 1; + ib = info->ib; + if (i == 0) { + ib--; + } + else if (info->ns == 2) { + if (i == bc7_ai0[partition]) { + ib--; + } + } + else if (info->ns == 3) { + if (i == bc7_ai1[partition]) { + ib--; + } + else if (i == bc7_ai2[partition]) { + ib--; + } + } + i0 = get_bits(src, cibit, ib); + cibit += ib; + + if (ab && info->ib2) { + ib2 = info->ib2; + if (ib2 && i == 0) { + ib2--; + } + i1 = get_bits(src, aibit, ib2); + aibit += ib2; + if (index_sel) { + bc7_lerp(&col[i], &endpoints[s], aw[i1], cw[i0]); + } + else { + bc7_lerp(&col[i], &endpoints[s], cw[i0], aw[i1]); + } + } + else { + bc7_lerp(&col[i], &endpoints[s], cw[i0], cw[i0]); + } +#define ROTATE(x, y) \ + val = x; \ + x = y; \ + y = val + if (rotation == 1) { + ROTATE(col[i].r, col[i].a); + } + else if (rotation == 2) { + ROTATE(col[i].g, col[i].a); + } + else if (rotation == 3) { + ROTATE(col[i].b, col[i].a); + } +#undef ROTATE + } +} + +/* BC6 */ +typedef struct { + char ns; /* number of subsets (also called regions) */ + char tr; /* whether endpoints are delta-compressed */ + char pb; /* partition bits */ + char epb; /* endpoint bits */ + char rb; /* red bits (delta) */ + char gb; /* green bits (delta) */ + char bb; /* blue bits (delta) */ +} bc6_mode_info; + +static const bc6_mode_info bc6_modes[] = { + // 00 + {2, 1, 5, 10, 5, 5, 5}, + // 01 + {2, 1, 5, 7, 6, 6, 6}, + // 10 + {2, 1, 5, 11, 5, 4, 4}, + {2, 1, 5, 11, 4, 5, 4}, + {2, 1, 5, 11, 4, 4, 5}, + {2, 1, 5, 9, 5, 5, 5}, + {2, 1, 5, 8, 6, 5, 5}, + {2, 1, 5, 8, 5, 6, 5}, + {2, 1, 5, 8, 5, 5, 6}, + {2, 0, 5, 6, 6, 6, 6}, + // 11 + {1, 0, 0, 10, 10, 10, 10}, + {1, 1, 0, 11, 9, 9, 9}, + {1, 1, 0, 12, 8, 8, 8}, + {1, 1, 0, 16, 4, 4, 4} +}; + +/* Table.F, encoded as a sequence of bit indices */ +static const uint8_t bc6_bit_packings[][75] = { + {116, 132, 180, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, + 66, 67, 68, 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, + 129, 130, 131, 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {117, 164, 165, 0, 1, 2, 3, 4, 5, 6, 176, 177, 132, 16, 17, + 18, 19, 20, 21, 22, 133, 178, 116, 32, 33, 34, 35, 36, 37, 38, + 179, 181, 180, 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, + 66, 67, 68, 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, + 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 48, 49, 50, 51, 52, 10, 112, 113, 114, 115, 64, 65, 66, 67, 26, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 42, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 48, 49, 50, 51, 10, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, + 26, 160, 161, 162, 163, 80, 81, 82, 83, 42, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 176, 178, 144, 145, 146, 147, 116, 179}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 48, 49, 50, 51, 10, 132, 112, 113, 114, 115, 64, 65, 66, 67, 26, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 42, 128, 129, 130, 131, + 96, 97, 98, 99, 177, 178, 144, 145, 146, 147, 180, 179}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 132, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 116, 32, 33, 34, 35, 36, 37, 38, 39, 40, 180, + 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {0, 1, 2, 3, 4, 5, 6, 7, 164, 132, 16, 17, 18, 19, 20, + 21, 22, 23, 178, 116, 32, 33, 34, 35, 36, 37, 38, 39, 179, 180, + 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, 66, 67, 68, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, + {0, 1, 2, 3, 4, 5, 6, 7, 176, 132, 16, 17, 18, 19, 20, + 21, 22, 23, 117, 116, 32, 33, 34, 35, 36, 37, 38, 39, 165, 180, + 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, + 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {0, 1, 2, 3, 4, 5, 6, 7, 177, 132, 16, 17, 18, 19, 20, + 21, 22, 23, 133, 116, 32, 33, 34, 35, 36, 37, 38, 39, 181, 180, + 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {0, 1, 2, 3, 4, 5, 164, 176, 177, 132, 16, 17, 18, 19, 20, + 21, 117, 133, 178, 116, 32, 33, 34, 35, 36, 37, 165, 179, 181, 180, + 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, 66, 67, 68, + 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 53, 54, 55, 56, 10, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 26, 80, 81, 82, 83, 84, 85, 86, 87, 88, 42}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 53, 54, 55, 11, 10, + 64, 65, 66, 67, 68, 69, 70, 71, 27, 26, 80, 81, 82, 83, 84, 85, 86, 87, 43, 42}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 15, 14, 13, 12, 11, 10, + 64, 65, 66, 67, 31, 30, 29, 28, 27, 26, 80, 81, 82, 83, 47, 46, 45, 44, 43, 42} +}; + +static void +bc6_sign_extend(uint16_t* v, int prec) { + int x = *v; + if (x & (1 << (prec - 1))) { + x |= -1 << prec; + } + *v = (uint16_t)x; +} + +static int +bc6_unquantize(uint16_t v, int prec, int sign) { + int s = 0; + int x; + if (!sign) { + x = v; + if (prec >= 15) { + return x; + } + if (x == 0) { + return 0; + } + if (x == ((1 << prec) - 1)) { + return 0xffff; + } + return ((x << 15) + 0x4000) >> (prec - 1); + } + else { + x = (int16_t)v; + if (prec >= 16) { + return x; + } + if (x < 0) { + s = 1; + x = -x; + } + + if (x != 0) { + if (x >= ((1 << (prec - 1)) - 1)) { + x = 0x7fff; + } + else { + x = ((x << 15) + 0x4000) >> (prec - 1); + } + } + + if (s) { + return -x; + } + return x; + } +} + +static float +half_to_float(uint16_t h) { + /* https://gist.github.com/rygorous/2144712 */ + union { + uint32_t u; + float f; + } o, m; + m.u = 0x77800000; + o.u = (h & 0x7fff) << 13; + o.f *= m.f; + m.u = 0x47800000; + if (o.f >= m.f) { + o.u |= 255 << 23; + } + o.u |= (h & 0x8000) << 16; + return o.f; +} + +static float +bc6_finalize(int v, int sign) { + if (sign) { + if (v < 0) { + v = ((-v) * 31) / 32; + return half_to_float((uint16_t)(0x8000 | v)); + } + else { + return half_to_float((uint16_t)((v * 31) / 32)); + } + } + else { + return half_to_float((uint16_t)((v * 31) / 64)); + } +} + +static uint8_t +bc6_clamp(float value) { + if (value < 0.0f) { + return 0; + } + else if (value > 1.0f) { + return 255; + } + else { + return (uint8_t)(value * 255.0f); + } +} + +static void +bc6_lerp(rgba* col, int* e0, int* e1, int s, int sign) { + int r, g, b; + int t = 64 - s; + r = (e0[0] * t + e1[0] * s) >> 6; + g = (e0[1] * t + e1[1] * s) >> 6; + b = (e0[2] * t + e1[2] * s) >> 6; + col->r = bc6_clamp(bc6_finalize(r, sign)); + col->g = bc6_clamp(bc6_finalize(g, sign)); + col->b = bc6_clamp(bc6_finalize(b, sign)); +} + +static void +decode_bc6_block(rgba* col, const uint8_t* src, int sign) { + uint16_t endpoints[12]; /* storage for r0, g0, b0, r1, ... */ + int ueps[12]; + int i, i0, ib2, di, dw, mask, numep, s; + uint8_t partition; + const bc6_mode_info* info; + const char* cw; + int bit = 5; + int epbits = 75; + int ib = 3; + int mode = src[0] & 0x1f; + if ((mode & 3) == 0 || (mode & 3) == 1) { + mode &= 3; + bit = 2; + } + else if ((mode & 3) == 2) { + mode = 2 + (mode >> 2); + epbits = 72; + } + else { + mode = 10 + (mode >> 2); + epbits = 60; + ib = 4; + } + if (mode >= 14) { + /* invalid block */ + memset(col, 0, 16 * sizeof(col[0])); + return; + } + info = &bc6_modes[mode]; + cw = bc7_get_weights(ib); + numep = info->ns == 2 ? 12 : 6; + for (i = 0; i < 12; i++) { + endpoints[i] = 0; + } + for (i = 0; i < epbits; i++) { + di = bc6_bit_packings[mode][i]; + dw = di >> 4; + di &= 15; + endpoints[dw] |= (uint16_t)get_bit(src, bit + i) << di; + } + bit += epbits; + partition = get_bits(src, bit, info->pb); + bit += info->pb; + mask = (1 << info->epb) - 1; + if (sign) { /* sign-extend e0 if signed */ + bc6_sign_extend(&endpoints[0], info->epb); + bc6_sign_extend(&endpoints[1], info->epb); + bc6_sign_extend(&endpoints[2], info->epb); + } + if (sign || info->tr) { /* sign-extend e1,2,3 if signed or deltas */ + for (i = 3; i < numep; i += 3) { + bc6_sign_extend(&endpoints[i], info->rb); + bc6_sign_extend(&endpoints[i + 1], info->gb); + bc6_sign_extend(&endpoints[i + 2], info->bb); + } + } + if (info->tr) { /* apply deltas */ + for (i = 3; i < numep; i += 3) { + endpoints[i] = (endpoints[i] + endpoints[0]) & mask; + endpoints[i + 1] = (endpoints[i + 1] + endpoints[1]) & mask; + endpoints[i + 2] = (endpoints[i + 2] + endpoints[2]) & mask; + } + } + for (i = 0; i < numep; i++) { + ueps[i] = bc6_unquantize(endpoints[i], info->epb, sign); + } + for (i = 0; i < 16; i++) { + s = bc7_get_subset(info->ns, partition, i) * 6; + ib2 = ib; + if (i == 0) { + ib2--; + } + else if (info->ns == 2) { + if (i == bc7_ai0[partition]) { + ib2--; + } + } + i0 = get_bits(src, bit, ib2); + bit += ib2; + + bc6_lerp(&col[i], &ueps[s], &ueps[s + 3], cw[i0], sign); + } +} + + +static void +put_block(uint8_t* dst, DecodeState *state, const char* col, int sz) { + int width = state->xsize; + int height = state->ysize; + int xmax = width + state->xoff; + int ymax = height + state->yoff; + + const int blockSize = 4; + int chan = state->outNumChannels; + + for (int i = 0; i < blockSize; i++) { + for (int j = 0; j < blockSize; j++) { + // Single channel image edge case + if (state->inNumChannels == 1) { + int src_flat_idx = (i * blockSize * sz) + (j * sz); + int dest_flat_idx = ((state->y + i) * width * chan) + ((state->x + j) * chan); + + dst[dest_flat_idx + 0] = col[src_flat_idx]; + dst[dest_flat_idx + 1] = col[src_flat_idx]; + dst[dest_flat_idx + 2] = col[src_flat_idx]; + dst[dest_flat_idx + 3] = 255; + continue; + } + + // Dual channel (BC5) edge case + if (state->inNumChannels == 2) { + int src_flat_idx_r = (i * blockSize * sz) + (j * sz) + 0; + int src_flat_idx_g = (i * blockSize * sz) + (j * sz) + 1; + + // RGBA to BGRA + int dest_flat_idx_r = ((state->y + i) * width * chan) + ((state->x + j) * chan) + 2; + int dest_flat_idx_g = ((state->y + i) * width * chan) + ((state->x + j) * chan) + 1; + int dest_flat_idx_b = ((state->y + i) * width * chan) + ((state->x + j) * chan) + 0; + int dest_flat_idx_a = ((state->y + i) * width * chan) + ((state->x + j) * chan) + 3; + + dst[dest_flat_idx_r] = col[src_flat_idx_r]; + dst[dest_flat_idx_g] = col[src_flat_idx_g]; + dst[dest_flat_idx_b] = 0; + dst[dest_flat_idx_a] = 255; + continue; + } + + int src_flat_idx_r = (i * blockSize * sz) + (j * sz) + 0; + int src_flat_idx_g = (i * blockSize * sz) + (j * sz) + 1; + int src_flat_idx_b = (i * blockSize * sz) + (j * sz) + 2; + int src_flat_idx_a = (i * blockSize * sz) + (j * sz) + 3; + + // RGBA to BGRA + int dest_flat_idx_r = ((state->y + i) * width * chan) + ((state->x + j) * chan) + 2; + int dest_flat_idx_g = ((state->y + i) * width * chan) + ((state->x + j) * chan) + 1; + int dest_flat_idx_b = ((state->y + i) * width * chan) + ((state->x + j) * chan) + 0; + int dest_flat_idx_a = ((state->y + i) * width * chan) + ((state->x + j) * chan) + 3; + + dst[dest_flat_idx_r] = col[src_flat_idx_r]; + dst[dest_flat_idx_g] = col[src_flat_idx_g]; + dst[dest_flat_idx_b] = col[src_flat_idx_b]; + dst[dest_flat_idx_a] = col[src_flat_idx_a]; + } + } + + state->x += blockSize; + if (state->x >= xmax) { + state->y += blockSize; + state->x = state->xoff; + } +} + +static int +decode_bcn( + const uint8_t* src, + uint8_t* dst, + DecodeState *state, + int bytes +) { + int ymax = state->ysize + state->yoff; + const uint8_t* ptr = src; + switch (state->n) { + +#define DECODE_LOOP(NN, SZ, TY, ...) \ + case NN: \ + while (bytes >= SZ) { \ + TY col[16]; \ + memset(col, 0, 16 * sizeof(col[0])); \ + decode_bc##NN##_block(col, ptr); \ + put_block(dst, state, (const char *)col, sizeof(col[0])); \ + ptr += SZ; \ + bytes -= SZ; \ + if (state->y >= ymax) { \ + return 0; \ + } \ + } \ + break + + DECODE_LOOP(1, 8, rgba); + DECODE_LOOP(2, 16, rgba); + DECODE_LOOP(3, 16, rgba); + DECODE_LOOP(4, 8, lum); + case 5: { + int sign = strcmp(state->pixel_format, "BC5S") == 0 ? 1 : 0; + while (bytes >= 16) { + rgba col[16]; + memset(col, sign ? 128 : 0, 16 * sizeof(col[0])); + decode_bc5_block(col, ptr, sign); + put_block(dst, state, (const char*)col, sizeof(col[0])); + ptr += 16; + bytes -= 16; + if (state->y >= ymax) { + return 0; + } + } + break; + } + case 6: { + int sign = strcmp(state->pixel_format, "BC6HS") == 0 ? 1 : 0; + while (bytes >= 16) { + rgba col[16]; + decode_bc6_block(col, ptr, sign); + put_block(dst, state, (const char*)col, sizeof(col[0])); + ptr += 16; + bytes -= 16; + if (state->y >= ymax) { + return 0; + } + } + break; + } + DECODE_LOOP(7, 16, rgba); +#undef DECODE_LOOP + } + return (int)(ptr - src); +} + +int +ImagingBcnDecode(const uint8_t* src, uint8_t* dst, DecodeState *state, uint32_t bytes) { + return decode_bcn(src, dst, state, bytes); +} diff --git a/src/JPEGView/BcnDecode.h b/src/JPEGView/BcnDecode.h new file mode 100644 index 00000000..fb8e3f7f --- /dev/null +++ b/src/JPEGView/BcnDecode.h @@ -0,0 +1,28 @@ +/* + * + * + */ + +#pragma once + +#include + +typedef struct DecodeState { + int xsize; // Width + int ysize; // Height + int xoff; // X Offset + int yoff; // Y Offset + + int ystep = 0; + + int n; // BCN format + char* pixel_format; + int inNumChannels; + int outNumChannels; + int bytesPerBlock; + + int x; // Current x position + int y; // Current y position +} DecodeState; + +int ImagingBcnDecode(const uint8_t* src, uint8_t* dst, DecodeState *state, uint32_t bytes); \ No newline at end of file diff --git a/src/JPEGView/DDSWrapper.cpp b/src/JPEGView/DDSWrapper.cpp new file mode 100644 index 00000000..bed59660 --- /dev/null +++ b/src/JPEGView/DDSWrapper.cpp @@ -0,0 +1,427 @@ +#include "stdafx.h" +#include "DDSWrapper.h" + +#include "BcnDecode.h" +#include "SettingsProvider.h" + +// Implementation adapted from https://github.com/python-pillow/Pillow/blob/main/src/PIL/DdsImagePlugin.py +// Tested with same .dds files as Pillow to replicate functionality +CJPEGImage* DDSWrapper::ReadImage(LPCTSTR strFileName, bool& bOutOfMemory) +{ + bOutOfMemory = false; + + FILE* file = _tfopen(strFileName, _T("rb")); + if (file == NULL) { + return NULL; + } + + // Skip magic number + fseek(file, 4, SEEK_SET); + + // Read header + DDS_HEADER header; + fread(&header, sizeof(DDS_HEADER), 1, file); + if (header.dwSize != 124) { + fclose(file); + return NULL; + } + + uint8_t *pixelBuf = NULL; + + // Check pixel format flags and handle accordingly + DWORD pfFlags = header.pixelFormat.dwFlags; + if (pfFlags & DDS_PIXEL_FORMAT_FLAGS::PF_RGB || pfFlags & DDS_PIXEL_FORMAT_FLAGS::PF_YUV) { + pixelBuf = HandleDDSRGB(file, &header, bOutOfMemory); + + } else if (pfFlags & DDS_PIXEL_FORMAT_FLAGS::PF_LUMINANCE) { + pixelBuf = HandleLuminance(file, &header, bOutOfMemory); + + } else if (pfFlags & DDS_PIXEL_FORMAT_FLAGS::PF_PALETTEINDEXED8) { + pixelBuf = HandlePaletteIndexed(file, &header, bOutOfMemory); + + } else if (pfFlags & DDS_PIXEL_FORMAT_FLAGS::PF_FOURCC) { + if (header.pixelFormat.dwFourCC == DDS_FOURCC::FOURCC_DX10) { + pixelBuf = HandleDXT10(file, &header, bOutOfMemory); + } else { + pixelBuf = HandleFourCC(file, &header, bOutOfMemory); + } + } else { + fclose(file); + return NULL; + } + + if (pixelBuf == NULL) { + fclose(file); + return NULL; + } + + fclose(file); + + // Multiply alpha value into each AABBGGRR pixel + uint32* pImage32 = (uint32*)pixelBuf; + for (int i = 0; i < header.dwWidth * header.dwHeight; i++) + *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, CSettingsProvider::This().ColorTransparency()); + + return new CJPEGImage( + header.dwWidth, header.dwHeight, + (void*)pixelBuf, NULL, 4, 0, IF_DDS, false, 0, 1, 0, NULL, 0, 0); +} + + +void GetMaskPadding(uint32_t mask, uint32_t* offset, uint32_t* maxVal) { + if (mask == 0) { + return; + } + + *offset = 0; + *maxVal = 0; + + while (true) { + // Shift the mask right then back left to remove 1 bit of padding + uint32_t shifted_mask = mask >> (*offset + 1); + uint32_t reconstructed_mask = shifted_mask << (*offset + 1); + + if (reconstructed_mask != mask) { + *maxVal = mask >> *offset; + break; + } + *offset += 1; + } +} + + +uint8_t* DDSWrapper::HandleDDSRGB(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory) { + uint32_t byteCount = header->pixelFormat.dwRGBBitCount / 8; // How many bits RGB(A) is total + uint32_t inPixels = header->dwHeight * header->dwWidth; + uint32_t outSize = header->dwHeight * header->dwWidth * 4; + uint8_t *dst = (uint8_t*)malloc(sizeof(uint8_t) * outSize); + if (dst == NULL) { + bOutOfMemory = true; + return NULL; + } + + // Remove padding from each mask + uint32_t rOffset = 0, gOffset = 0, bOffset = 0, aOffset = 0; + uint32_t rMaxVal = 0, gMaxVal = 0, bMaxVal = 0, aMaxVal = 0; + GetMaskPadding(header->pixelFormat.dwRBitMask, &rOffset, &rMaxVal); + GetMaskPadding(header->pixelFormat.dwGBitMask, &gOffset, &gMaxVal); + GetMaskPadding(header->pixelFormat.dwBBitMask, &bOffset, &bMaxVal); + GetMaskPadding(header->pixelFormat.dwABitMask, &aOffset, &aMaxVal); + + // Read each pixel data and apply masks + for (uint32_t i = 0; i < inPixels; i++) { + uint32_t value; + fread(&value, byteCount, 1, file); + + uint32_t r = (value & header->pixelFormat.dwRBitMask) >> rOffset; + uint32_t g = (value & header->pixelFormat.dwGBitMask) >> gOffset; + uint32_t b = (value & header->pixelFormat.dwBBitMask) >> bOffset; + uint32_t a = (value & header->pixelFormat.dwABitMask) >> aOffset; + + if (rMaxVal != 0) r = (r / (double)rMaxVal) * 255; + if (gMaxVal != 0) g = (g / (double)gMaxVal) * 255; + if (bMaxVal != 0) b = (b / (double)bMaxVal) * 255; + if (aMaxVal != 0) a = (a / (double)aMaxVal) * 255; + + // Set BGR(A) in destination buffer + dst[i * 4 + 0] = b; + dst[i * 4 + 1] = g; + dst[i * 4 + 2] = r; + + if (header->pixelFormat.dwFlags & DDS_PIXEL_FORMAT_FLAGS::PF_ALPHAPIXELS || + header->pixelFormat.dwFlags & DDS_PIXEL_FORMAT_FLAGS::PF_ALPHA + ) { + dst[i * 4 + 3] = a; + } else { + dst[i * 4 + 3] = 255; + } + } + + return dst; +} + + +uint8_t* DDSWrapper::HandleLuminance(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory) { + uint32_t byteCount = header->pixelFormat.dwRGBBitCount / 8; + // Handle unsupported pixel formats + if (byteCount != 1 && byteCount != 2) { + return NULL; + } + if (byteCount == 2 && !(header->pixelFormat.dwFlags & DDS_PIXEL_FORMAT_FLAGS::PF_ALPHAPIXELS || + header->pixelFormat.dwFlags & DDS_PIXEL_FORMAT_FLAGS::PF_ALPHA)) { + return NULL; + } + + uint32_t inPixels = header->dwHeight * header->dwWidth; + uint32_t outSize = header->dwHeight * header->dwWidth * 4; + uint8_t *dst = (uint8_t*)malloc(sizeof(uint8_t) * outSize); + if (dst == NULL) { + bOutOfMemory = true; + return NULL; + } + + // Remove padding from each mask + uint32_t lOffset = 0, aOffset = 0; + uint32_t lMaxVal = 0, aMaxVal = 0; + GetMaskPadding(header->pixelFormat.dwRBitMask, &lOffset, &lMaxVal); + GetMaskPadding(header->pixelFormat.dwABitMask, &aOffset, &aMaxVal); + + // Read each pixel data and apply masks + for (uint32_t i = 0; i < inPixels; i++) { + uint32_t value; + fread(&value, byteCount, 1, file); + + uint32_t l; + uint32_t a; + if (header->pixelFormat.dwFlags & DDS_PIXEL_FORMAT_FLAGS::PF_ALPHAPIXELS || + header->pixelFormat.dwFlags & DDS_PIXEL_FORMAT_FLAGS::PF_ALPHA + ) { + l = (value & header->pixelFormat.dwRBitMask) >> lOffset; + a = (value & header->pixelFormat.dwABitMask) >> aOffset; + if (lMaxVal != 0) l = (l / (double)lMaxVal) * 255; + if (aMaxVal != 0) a = (a / (double)aMaxVal) * 255; + } else { + l = value; + a = 255; + } + + // Set BGR(A) in destination buffer + dst[i * 4 + 0] = l; + dst[i * 4 + 1] = l; + dst[i * 4 + 2] = l; + dst[i * 4 + 3] = a; + } + + return dst; +} + + +uint8_t* DDSWrapper::HandlePaletteIndexed(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory) { + // Read palette buf + uint32_t paletteSize = 256 * 4; + uint8_t *paletteBuf = (uint8_t*)malloc(sizeof(uint8_t) * paletteSize); + if (paletteBuf == NULL) { + bOutOfMemory = true; + return NULL; + } + + fread(paletteBuf, sizeof(uint8_t), paletteSize, file); + + // Read image data + uint32_t inPixels = header->dwHeight * header->dwWidth; + uint32_t outSize = header->dwHeight * header->dwWidth * 4; + uint8_t *dst = (uint8_t*)malloc(sizeof(uint8_t) * outSize); + if (dst == NULL) { + free(paletteBuf); + bOutOfMemory = true; + return NULL; + } + + for (uint32_t i = 0; i < inPixels; i++) { + uint8_t index; + fread(&index, sizeof(uint8_t), 1, file); + + dst[i * 4 + 0] = paletteBuf[index * 4 + 2]; + dst[i * 4 + 1] = paletteBuf[index * 4 + 1]; + dst[i * 4 + 2] = paletteBuf[index * 4 + 0]; + dst[i * 4 + 3] = paletteBuf[index * 4 + 3]; + } + + free(paletteBuf); + return dst; +} + + +uint8_t* DDSWrapper::HandleFourCC(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory) { + // Setup decoder state + DecodeState state; + state.outNumChannels = 4; + state.xsize = header->dwWidth; + state.ysize = header->dwHeight; + state.xoff = 0; + state.yoff = 0; + state.x = 0; + state.y = 0; + + DWORD fourCC = header->pixelFormat.dwFourCC; + if (fourCC == DDS_FOURCC::FOURCC_DXT1) { + state.n = 1; + state.pixel_format = "DXT1"; + state.inNumChannels = 4; + state.bytesPerBlock = 8; + + } else if (fourCC == DDS_FOURCC::FOURCC_DXT3) { + state.n = 2; + state.pixel_format = "DXT3"; + state.inNumChannels = 4; + state.bytesPerBlock = 16; + + } else if (fourCC == DDS_FOURCC::FOURCC_DXT5) { + state.n = 3; + state.pixel_format = "DXT5"; + state.inNumChannels = 4; + state.bytesPerBlock = 16; + + } else if (fourCC == DDS_FOURCC::FOURCC_BC4U || fourCC == DDS_FOURCC::FOURCC_ATI1) { + state.n = 4; + state.pixel_format = "BC4"; + state.inNumChannels = 1; + state.bytesPerBlock = 8; + + } else if (fourCC == DDS_FOURCC::FOURCC_BC5S) { + state.n = 5; + state.pixel_format = "BC5S"; + state.inNumChannels = 2; + state.bytesPerBlock = 16; + + } else if (fourCC == DDS_FOURCC::FOURCC_BC5U || fourCC == DDS_FOURCC::FOURCC_ATI2) { + state.n = 5; + state.pixel_format = "BC5"; + state.inNumChannels = 2; + state.bytesPerBlock = 16; + + } else { + return NULL; + } + + // Setup buffers + int compressedWidth = header->dwWidth / 4; + int compressedHeight = header->dwHeight / 4; + int compressedSize = compressedWidth * compressedHeight * state.bytesPerBlock; + int outSize = header->dwHeight * header->dwWidth * state.outNumChannels; + uint8_t *src = (uint8_t*)malloc(sizeof(uint8_t) * compressedSize); + uint8_t *dst = (uint8_t*)malloc(sizeof(uint8_t) * outSize); + + if (src == NULL || dst == NULL) { + bOutOfMemory = true; + return NULL; + } + + fread(src, sizeof(uint8_t), compressedSize, file); + + int ret = ImagingBcnDecode(src, dst, &state, compressedSize); + if (ret != 0) { + free(src); + free(dst); + return NULL; + } + + free(src); + return dst; +} + + +uint8_t* HandleDXT10_R8G8B8A(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory) { + // Raw loading + int outSize = header->dwHeight * header->dwWidth * 4; + uint8_t *dst = (uint8_t*)malloc(sizeof(uint8_t) * outSize); + if (dst == NULL) { + bOutOfMemory = true; + return NULL; + } + + fread(dst, sizeof(uint8_t), outSize, file); + + // Swap red and blue channels + for (int i = 0; i < outSize; i += 4) { + uint8_t temp = dst[i]; + dst[i] = dst[i + 2]; + dst[i + 2] = temp; + } + + return dst; +} + + +uint8_t* DDSWrapper::HandleDXT10(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory) { + DDS_DXT10_HEADER h10; + fread(&h10, sizeof(DDS_DXT10_HEADER), 1, file); + + // Setup decoder state + DecodeState state; + state.outNumChannels = 4; + state.xsize = header->dwWidth; + state.ysize = header->dwHeight; + state.xoff = 0; + state.yoff = 0; + state.x = 0; + state.y = 0; + + if (h10.dxgiFormat == DDS_DXGI_FORMAT::DF_BC1_TYPELESS || h10.dxgiFormat == DDS_DXGI_FORMAT::DF_BC1_UNORM) { + state.n = 1; + state.pixel_format = "BC1"; + state.inNumChannels = 4; + state.bytesPerBlock = 8; + + } else if (h10.dxgiFormat == DDS_DXGI_FORMAT::DF_BC4_TYPELESS || + h10.dxgiFormat == DDS_DXGI_FORMAT::DF_BC4_UNORM) { + state.n = 4; + state.pixel_format = "BC4"; + state.inNumChannels = 1; + state.bytesPerBlock = 8; + + } else if (h10.dxgiFormat == DDS_DXGI_FORMAT::DF_BC5_TYPELESS || h10.dxgiFormat == DDS_DXGI_FORMAT::DF_BC5_UNORM) { + state.n = 5; + state.pixel_format = "BC5"; + state.inNumChannels = 2; + state.bytesPerBlock = 16; + + } else if (h10.dxgiFormat == DDS_DXGI_FORMAT::DF_BC5_SNORM) { + state.n = 5; + state.pixel_format = "BC5S"; + state.inNumChannels = 2; + state.bytesPerBlock = 16; + + } else if (h10.dxgiFormat == DDS_DXGI_FORMAT::DF_BC6H_UF16) { + state.n = 6; + state.pixel_format = "BC6H"; + state.inNumChannels = 3; + state.bytesPerBlock = 16; + + } else if (h10.dxgiFormat == DDS_DXGI_FORMAT::DF_BC6H_SF16) { + state.n = 6; + state.pixel_format = "BC6HS"; + state.inNumChannels = 3; + state.bytesPerBlock = 16; + + } else if (h10.dxgiFormat == DDS_DXGI_FORMAT::DF_BC7_TYPELESS || + h10.dxgiFormat == DDS_DXGI_FORMAT::DF_BC7_UNORM || + h10.dxgiFormat == DDS_DXGI_FORMAT::DF_BC7_UNORM_SRGB) { + state.n = 7; + state.pixel_format = "BC7"; + state.inNumChannels = 4; + state.bytesPerBlock = 16; + + } else if (h10.dxgiFormat == DDS_DXGI_FORMAT::DF_R8G8B8A8_TYPELESS || + h10.dxgiFormat == DDS_DXGI_FORMAT::DF_R8G8B8A8_UNORM || + h10.dxgiFormat == DDS_DXGI_FORMAT::DF_R8G8B8A8_UNORM_SRGB) { + return HandleDXT10_R8G8B8A(file, header, bOutOfMemory); + + } else { + return NULL; + } + + // Setup buffers + int compressedWidth = header->dwWidth / 4; + int compressedHeight = header->dwHeight / 4; + int compressedSize = compressedWidth * compressedHeight * state.bytesPerBlock; + int outSize = header->dwHeight * header->dwWidth * state.outNumChannels; + uint8_t *src = (uint8_t*)malloc(sizeof(uint8_t) * compressedSize); + uint8_t *dst = (uint8_t*)malloc(sizeof(uint8_t) * outSize); + if (src == NULL || dst == NULL) { + bOutOfMemory = true; + return NULL; + } + + fread(src, sizeof(uint8_t), compressedSize, file); + + int ret = ImagingBcnDecode(src, dst, &state, compressedSize); + if (ret != 0) { + free(src); + free(dst); + return NULL; + } + + free(src); + return dst; +} diff --git a/src/JPEGView/DDSWrapper.h b/src/JPEGView/DDSWrapper.h new file mode 100644 index 00000000..8c07c11a --- /dev/null +++ b/src/JPEGView/DDSWrapper.h @@ -0,0 +1,225 @@ +#pragma once + +#include "JPEGImage.h" + +// Struct/enum definitions taken from: https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-header + +typedef enum DDS_PIXEL_FORMAT_FLAGS { + PF_ALPHAPIXELS = 0x1, + PF_ALPHA = 0x2, + PF_FOURCC = 0x4, + PF_PALETTEINDEXED8 = 0x20, + PF_RGB = 0x40, + PF_YUV = 0x200, + PF_LUMINANCE = 0x20000 +} DDS_PIXEL_FORMAT_FLAGS; + + +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + (static_cast(static_cast(ch0)) \ + | (static_cast(static_cast(ch1)) << 8) \ + | (static_cast(static_cast(ch2)) << 16) \ + | (static_cast(static_cast(ch3)) << 24)) +#endif /* MAKEFOURCC */ + + +// Taken from https://github.com/microsoft/DirectXTex/blob/main/DirectXTex/DDS.h +// Cross-referenced with https://github.com/python-pillow/Pillow/blob/main/src/PIL/DdsImagePlugin.py +typedef enum DDS_FOURCC { + FOURCC_DXT1 = MAKEFOURCC('D', 'X', 'T', '1'), + FOURCC_DXT2 = MAKEFOURCC('D', 'X', 'T', '2'), + FOURCC_DXT3 = MAKEFOURCC('D', 'X', 'T', '3'), + FOURCC_DXT4 = MAKEFOURCC('D', 'X', 'T', '4'), + FOURCC_DXT5 = MAKEFOURCC('D', 'X', 'T', '5'), + FOURCC_BC4U = MAKEFOURCC('B', 'C', '4', 'U'), + FOURCC_BC4S = MAKEFOURCC('B', 'C', '4', 'S'), + FOURCC_BC5U = MAKEFOURCC('B', 'C', '5', 'U'), + FOURCC_BC5S = MAKEFOURCC('B', 'C', '5', 'S'), + FOURCC_RGBG = MAKEFOURCC('R', 'G', 'B', 'G'), + FOURCC_GRGB = MAKEFOURCC('G', 'R', 'G', 'B'), + FOURCC_YUY2 = MAKEFOURCC('Y', 'U', 'Y', '2'), + FOURCC_UYVY = MAKEFOURCC('U', 'Y', 'V', 'Y'), + FOURCC_DX10 = MAKEFOURCC('D', 'X', '1', '0'), + FOURCC_ATI1 = MAKEFOURCC('A', 'T', 'I', '1'), + FOURCC_ATI2 = MAKEFOURCC('A', 'T', 'I', '2') +} DDS_FOURCC; + + +typedef enum DDS_DXGI_FORMAT { + DF_UNKNOWN = 0, + DF_R32G32B32A32_TYPELESS = 1, + DF_R32G32B32A32_FLOAT = 2, + DF_R32G32B32A32_UINT = 3, + DF_R32G32B32A32_SINT = 4, + DF_R32G32B32_TYPELESS = 5, + DF_R32G32B32_FLOAT = 6, + DF_R32G32B32_UINT = 7, + DF_R32G32B32_SINT = 8, + DF_R16G16B16A16_TYPELESS = 9, + DF_R16G16B16A16_FLOAT = 10, + DF_R16G16B16A16_UNORM = 11, + DF_R16G16B16A16_UINT = 12, + DF_R16G16B16A16_SNORM = 13, + DF_R16G16B16A16_SINT = 14, + DF_R32G32_TYPELESS = 15, + DF_R32G32_FLOAT = 16, + DF_R32G32_UINT = 17, + DF_R32G32_SINT = 18, + DF_R32G8X24_TYPELESS = 19, + DF_D32_FLOAT_S8X24_UINT = 20, + DF_R32_FLOAT_X8X24_TYPELESS = 21, + DF_X32_TYPELESS_G8X24_UINT = 22, + DF_R10G10B10A2_TYPELESS = 23, + DF_R10G10B10A2_UNORM = 24, + DF_R10G10B10A2_UINT = 25, + DF_R11G11B10_FLOAT = 26, + DF_R8G8B8A8_TYPELESS = 27, + DF_R8G8B8A8_UNORM = 28, + DF_R8G8B8A8_UNORM_SRGB = 29, + DF_R8G8B8A8_UINT = 30, + DF_R8G8B8A8_SNORM = 31, + DF_R8G8B8A8_SINT = 32, + DF_R16G16_TYPELESS = 33, + DF_R16G16_FLOAT = 34, + DF_R16G16_UNORM = 35, + DF_R16G16_UINT = 36, + DF_R16G16_SNORM = 37, + DF_R16G16_SINT = 38, + DF_R32_TYPELESS = 39, + DF_D32_FLOAT = 40, + DF_R32_FLOAT = 41, + DF_R32_UINT = 42, + DF_R32_SINT = 43, + DF_R24G8_TYPELESS = 44, + DF_D24_UNORM_S8_UINT = 45, + DF_R24_UNORM_X8_TYPELESS = 46, + DF_X24_TYPELESS_G8_UINT = 47, + DF_R8G8_TYPELESS = 48, + DF_R8G8_UNORM = 49, + DF_R8G8_UINT = 50, + DF_R8G8_SNORM = 51, + DF_R8G8_SINT = 52, + DF_R16_TYPELESS = 53, + DF_R16_FLOAT = 54, + DF_D16_UNORM = 55, + DF_R16_UNORM = 56, + DF_R16_UINT = 57, + DF_R16_SNORM = 58, + DF_R16_SINT = 59, + DF_R8_TYPELESS = 60, + DF_R8_UNORM = 61, + DF_R8_UINT = 62, + DF_R8_SNORM = 63, + DF_R8_SINT = 64, + DF_A8_UNORM = 65, + DF_R1_UNORM = 66, + DF_R9G9B9E5_SHAREDEXP = 67, + DF_R8G8_B8G8_UNORM = 68, + DF_G8R8_G8B8_UNORM = 69, + DF_BC1_TYPELESS = 70, + DF_BC1_UNORM = 71, + DF_BC1_UNORM_SRGB = 72, + DF_BC2_TYPELESS = 73, + DF_BC2_UNORM = 74, + DF_BC2_UNORM_SRGB = 75, + DF_BC3_TYPELESS = 76, + DF_BC3_UNORM = 77, + DF_BC3_UNORM_SRGB = 78, + DF_BC4_TYPELESS = 79, + DF_BC4_UNORM = 80, + DF_BC4_SNORM = 81, + DF_BC5_TYPELESS = 82, + DF_BC5_UNORM = 83, + DF_BC5_SNORM = 84, + DF_B5G6R5_UNORM = 85, + DF_B5G5R5A1_UNORM = 86, + DF_B8G8R8A8_UNORM = 87, + DF_B8G8R8X8_UNORM = 88, + DF_R10G10B10_XR_BIAS_A2_UNORM = 89, + DF_B8G8R8A8_TYPELESS = 90, + DF_B8G8R8A8_UNORM_SRGB = 91, + DF_B8G8R8X8_TYPELESS = 92, + DF_B8G8R8X8_UNORM_SRGB = 93, + DF_BC6H_TYPELESS = 94, + DF_BC6H_UF16 = 95, + DF_BC6H_SF16 = 96, + DF_BC7_TYPELESS = 97, + DF_BC7_UNORM = 98, + DF_BC7_UNORM_SRGB = 99, + DF_AYUV = 100, + DF_Y410 = 101, + DF_Y416 = 102, + DF_NV12 = 103, + DF_P010 = 104, + DF_P016 = 105, + DF_420_OPAQUE = 106, + DF_YUY2 = 107, + DF_Y210 = 108, + DF_Y216 = 109, + DF_NV11 = 110, + DF_AI44 = 111, + DF_IA44 = 112, + DF_P8 = 113, + DF_A8P8 = 114, + DF_B4G4R4A4_UNORM = 115, + DF_P208 = 130, + DF_V208 = 131, + DF_V408 = 132, + DF_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE, + DF_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE, + DF_FORCE_UINT = 0xffffffff +} DDS_DXGI_FORMAT; + + +typedef struct DDS_PIXELFORMAT { + DWORD dwSize; + DWORD dwFlags; + DWORD dwFourCC; + DWORD dwRGBBitCount; + DWORD dwRBitMask; + DWORD dwGBitMask; + DWORD dwBBitMask; + DWORD dwABitMask; +} DDS_PIXELFORMAT; + + +typedef struct DDS_HEADER { + DWORD dwSize; + DWORD dwFlags; + DWORD dwHeight; + DWORD dwWidth; + DWORD dwPitchOrLinearSize; + DWORD dwDepth; + DWORD dwMipMapCount; + DWORD dwReserved1[11]; + DDS_PIXELFORMAT pixelFormat; + DWORD dwCaps; + DWORD dwCaps2; + DWORD dwCaps3; + DWORD dwCaps4; + DWORD dwReserved2; +} DDS_HEADER; + + +typedef struct DDS_DXT10_HEADER { + DWORD dxgiFormat; + DWORD resourceDimension; + UINT miscFlag; + UINT arraySize; + UINT miscFlags2; +} DDS_DXT10_HEADER; + + +class DDSWrapper { +public: + static CJPEGImage* ReadImage(LPCTSTR strFileName, bool& bOutOfMemory); + +private: + static uint8_t* DDSWrapper::HandleDDSRGB(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory); + static uint8_t* DDSWrapper::HandleLuminance(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory); + static uint8_t* DDSWrapper::HandlePaletteIndexed(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory); + static uint8_t* DDSWrapper::HandleFourCC(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory); + static uint8_t* HandleDXT10(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory); +}; + diff --git a/src/JPEGView/FileExtensionsDlg.cpp b/src/JPEGView/FileExtensionsDlg.cpp index cfec15c9..21a45728 100644 --- a/src/JPEGView/FileExtensionsDlg.cpp +++ b/src/JPEGView/FileExtensionsDlg.cpp @@ -292,6 +292,7 @@ void CFileExtensionsDlg::FillFileExtensionsList() { InsertExtension(_T("*.heic"), FormatHint(CNLS::GetString(_T("%s images")), _T("High Efficiency Image Container"))); InsertExtension(_T("*.qoi"), FormatHint(CNLS::GetString(_T("%s images")), _T("Quite OK Image"))); InsertExtension(_T("*.psd"), FormatHint(CNLS::GetString(_T("%s images")), _T("Photoshop Document"))); + InsertExtension(_T("*.dds"), FormatHint(CNLS::GetString(_T("%s images")), _T("DirectDraw Surface File"))); InsertExtensions(CSettingsProvider::This().FilesProcessedByWIC(), CNLS::GetString(_T("%s images (processed by Window Imaging Component - WIC)"))); InsertExtensions(CSettingsProvider::This().FileEndingsRAW(), CNLS::GetString(_T("%s camera raw images (embedded JPEGs only)"))); } diff --git a/src/JPEGView/FileList.cpp b/src/JPEGView/FileList.cpp index 1c7895bf..1a4e0e02 100644 --- a/src/JPEGView/FileList.cpp +++ b/src/JPEGView/FileList.cpp @@ -125,9 +125,9 @@ void CFileDesc::SetModificationDate(const FILETIME& lastModDate) { // image file types supported internally (there are additional endings for RAW and WIC - these come from INI file) // NOTE: when adding more supported filetypes, update installer to add another extension for "SupportedTypes" -static const int cnNumEndingsInternal = 17; +static const int cnNumEndingsInternal = 18; static const TCHAR* csFileEndingsInternal[cnNumEndingsInternal] = {_T("jpg"), _T("jpeg"), _T("jfif"), _T("bmp"), _T("png"), - _T("tif"), _T("tiff"), _T("gif"), _T("webp"), _T("jxl"), _T("avif"), _T("heif"), _T("heic"), _T("tga"), _T("qoi"), _T("psd"), _T("psb") }; + _T("tif"), _T("tiff"), _T("gif"), _T("webp"), _T("jxl"), _T("avif"), _T("heif"), _T("heic"), _T("tga"), _T("qoi"), _T("psd"), _T("psb"), _T("dds") }; // supported camera RAW formats static const TCHAR* csFileEndingsRAW = _T("*.pef;*.dng;*.crw;*.nef;*.cr2;*.mrw;*.rw2;*.orf;*.x3f;*.arw;*.kdc;*.nrw;*.dcr;*.sr2;*.raf"); diff --git a/src/JPEGView/ImageLoadThread.cpp b/src/JPEGView/ImageLoadThread.cpp index 54580b53..69dfb869 100644 --- a/src/JPEGView/ImageLoadThread.cpp +++ b/src/JPEGView/ImageLoadThread.cpp @@ -21,6 +21,7 @@ #include "WEBPWrapper.h" #include "QOIWrapper.h" #include "PSDWrapper.h" +#include "DDSWrapper.h" #include "MaxImageDef.h" @@ -79,6 +80,8 @@ static EImageFormat GetImageFormat(LPCTSTR sFileName) { return IF_QOI; } else if (header[0] == '8' && header[1] == 'B' && header[2] == 'P' && header[3] == 'S') { return IF_PSD; + } else if (header[0] == 'D' && header[1] == 'D' && header[2] == 'S' && header[3] == ' ') { + return IF_DDS; } // default fallback if no matches based on magic bytes @@ -386,6 +389,14 @@ void CImageLoadThread::ProcessRequest(CRequestBase& request) { DeleteCachedAvifDecoder(); ProcessReadWICRequest(&rq); break; + case IF_DDS: + DeleteCachedGDIBitmap(); + DeleteCachedWebpDecoder(); + DeleteCachedPngDecoder(); + DeleteCachedJxlDecoder(); + DeleteCachedAvifDecoder(); + ProcessReadDDSRequest(&rq); + break; default: // try with GDI+ DeleteCachedWebpDecoder(); @@ -1062,6 +1073,12 @@ void CImageLoadThread::ProcessReadGDIPlusRequest(CRequest * request) { } } +void CImageLoadThread::ProcessReadDDSRequest(CRequest* request) { + const wchar_t* sFileName; + sFileName = (const wchar_t*)request->FileName; + request->Image = DDSWrapper::ReadImage(sFileName, request->OutOfMemory); +} + static unsigned char* alloc(int sizeInBytes) { return new(std::nothrow) unsigned char[sizeInBytes]; } diff --git a/src/JPEGView/ImageLoadThread.h b/src/JPEGView/ImageLoadThread.h index e2f2c13b..dacfbeb7 100644 --- a/src/JPEGView/ImageLoadThread.h +++ b/src/JPEGView/ImageLoadThread.h @@ -120,6 +120,7 @@ class CImageLoadThread : public CWorkThread void ProcessReadRAWRequest(CRequest * request); void ProcessReadGDIPlusRequest(CRequest * request); void ProcessReadWICRequest(CRequest* request); + void ProcessReadDDSRequest(CRequest* request); static void SetFileDependentProcessParams(CRequest * request); static bool ProcessImageAfterLoad(CRequest * request); diff --git a/src/JPEGView/ImageProcessingTypes.h b/src/JPEGView/ImageProcessingTypes.h index cb0921b3..4f266417 100644 --- a/src/JPEGView/ImageProcessingTypes.h +++ b/src/JPEGView/ImageProcessingTypes.h @@ -46,6 +46,7 @@ enum EImageFormat { IF_CameraRAW, IF_JPEG_Embedded, // JPEG embedded in another file, e.g. camera raw IF_TGA, + IF_DDS, IF_Unknown }; diff --git a/src/JPEGView/JPEGView.vcxproj b/src/JPEGView/JPEGView.vcxproj index 6801186b..dd89cd98 100644 --- a/src/JPEGView/JPEGView.vcxproj +++ b/src/JPEGView/JPEGView.vcxproj @@ -302,8 +302,10 @@ + + @@ -393,8 +395,10 @@ + + diff --git a/src/JPEGView/JPEGView.vcxproj.filters b/src/JPEGView/JPEGView.vcxproj.filters index 8a484a5d..1226e2ea 100644 --- a/src/JPEGView/JPEGView.vcxproj.filters +++ b/src/JPEGView/JPEGView.vcxproj.filters @@ -37,6 +37,12 @@ {099a201d-61b0-4380-a60c-5081af3bbe23} + + {301733e2-8c67-4146-b2bd-b6b2e276a7af} + + + {c06ca9bd-bedf-4469-ac5f-ec9aa9ddc9c1} + @@ -291,6 +297,12 @@ Source Files\Image Types + + Source Files\Image Types\DDS + + + Source Files\Image Types\DDS + @@ -563,6 +575,12 @@ Header Files\Image Types + + Header Files\Image Types\DDS + + + Header Files\Image Types\DDS + From 79b23b07e9c66101ed4b42729a05f67ae6c54c3d Mon Sep 17 00:00:00 2001 From: Raghav-B Date: Sat, 28 Dec 2024 12:53:00 +0800 Subject: [PATCH 2/2] Add comments + minor cleanup --- src/JPEGView/BcnDecode.h | 1 + src/JPEGView/DDSWrapper.cpp | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/JPEGView/BcnDecode.h b/src/JPEGView/BcnDecode.h index fb8e3f7f..19a8a48d 100644 --- a/src/JPEGView/BcnDecode.h +++ b/src/JPEGView/BcnDecode.h @@ -7,6 +7,7 @@ #include +// Contains metadata for BCN decoding typedef struct DecodeState { int xsize; // Width int ysize; // Height diff --git a/src/JPEGView/DDSWrapper.cpp b/src/JPEGView/DDSWrapper.cpp index bed59660..86ea80a2 100644 --- a/src/JPEGView/DDSWrapper.cpp +++ b/src/JPEGView/DDSWrapper.cpp @@ -8,8 +8,6 @@ // Tested with same .dds files as Pillow to replicate functionality CJPEGImage* DDSWrapper::ReadImage(LPCTSTR strFileName, bool& bOutOfMemory) { - bOutOfMemory = false; - FILE* file = _tfopen(strFileName, _T("rb")); if (file == NULL) { return NULL; @@ -57,7 +55,7 @@ CJPEGImage* DDSWrapper::ReadImage(LPCTSTR strFileName, bool& bOutOfMemory) fclose(file); - // Multiply alpha value into each AABBGGRR pixel + // Fake alpha channel uint32* pImage32 = (uint32*)pixelBuf; for (int i = 0; i < header.dwWidth * header.dwHeight; i++) *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, CSettingsProvider::This().ColorTransparency()); @@ -68,6 +66,9 @@ CJPEGImage* DDSWrapper::ReadImage(LPCTSTR strFileName, bool& bOutOfMemory) } +// Takes a dw(RGBA)BitMask and determines +// - how many bits of padding are on the right, used for shifting pixel data +// - the maximum value possible with the mask, used for normalizing pixel data void GetMaskPadding(uint32_t mask, uint32_t* offset, uint32_t* maxVal) { if (mask == 0) { return; @@ -90,6 +91,7 @@ void GetMaskPadding(uint32_t mask, uint32_t* offset, uint32_t* maxVal) { } +// Handles uncompressed RGB(A) pixel data uint8_t* DDSWrapper::HandleDDSRGB(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory) { uint32_t byteCount = header->pixelFormat.dwRGBBitCount / 8; // How many bits RGB(A) is total uint32_t inPixels = header->dwHeight * header->dwWidth; @@ -141,6 +143,7 @@ uint8_t* DDSWrapper::HandleDDSRGB(FILE* file, const DDS_HEADER* header, bool& bO } +// Handles uncompressed luminance pixel data uint8_t* DDSWrapper::HandleLuminance(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory) { uint32_t byteCount = header->pixelFormat.dwRGBBitCount / 8; // Handle unsupported pixel formats @@ -196,6 +199,7 @@ uint8_t* DDSWrapper::HandleLuminance(FILE* file, const DDS_HEADER* header, bool& } +// Handles palette indexed pixel data uint8_t* DDSWrapper::HandlePaletteIndexed(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory) { // Read palette buf uint32_t paletteSize = 256 * 4; @@ -232,6 +236,7 @@ uint8_t* DDSWrapper::HandlePaletteIndexed(FILE* file, const DDS_HEADER* header, } +// Handles non-DXT10 compressed pixel data uint8_t* DDSWrapper::HandleFourCC(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory) { // Setup decoder state DecodeState state; @@ -311,6 +316,7 @@ uint8_t* DDSWrapper::HandleFourCC(FILE* file, const DDS_HEADER* header, bool& bO } +// Helper function for DXT10 uncompressed R8G8B8A8 pixel data uint8_t* HandleDXT10_R8G8B8A(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory) { // Raw loading int outSize = header->dwHeight * header->dwWidth * 4; @@ -333,6 +339,7 @@ uint8_t* HandleDXT10_R8G8B8A(FILE* file, const DDS_HEADER* header, bool& bOutOfM } +// Handles compressed & uncompressed pixel data with DXT10 header uint8_t* DDSWrapper::HandleDXT10(FILE* file, const DDS_HEADER* header, bool& bOutOfMemory) { DDS_DXT10_HEADER h10; fread(&h10, sizeof(DDS_DXT10_HEADER), 1, file);