Skip to content

Commit 6373436

Browse files
committed
feat: drop dat storage of images for gli texture2d
If we want to load and carry around image swith mipmaps and array layers from disk we need a more flexible data format than a single byte array. As GLI is a suitable library for loading assets from DDS in the first place we might as well make gli::texture2d our internal carrier format for image internals and properties. This commit removes the previous `dat` member of `image_c`, superseding it with a GLI texture2d member. We also take the opportunity to do some drive-by cleanup and fixing non-virtual dynamic destruction of image_c objects.
1 parent 79176d5 commit 6373436

7 files changed

Lines changed: 136 additions & 68 deletions

File tree

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,12 @@ find_package(unofficial-angle CONFIG REQUIRED)
107107
find_package(CURL CONFIG REQUIRED)
108108
find_package(fmt CONFIG REQUIRED)
109109
find_package(glfw3 CONFIG REQUIRED)
110+
find_package(gli CONFIG REQUIRED)
110111
find_package(LuaJIT REQUIRED)
111112
find_package(PkgConfig REQUIRED)
112113
find_package(re2 CONFIG REQUIRED)
113114
find_package(Threads REQUIRED)
115+
find_package(zstd REQUIRED)
114116
find_package(ZLIB REQUIRED)
115117

116118
pkg_check_modules(libjxl REQUIRED IMPORTED_TARGET libjxl)
@@ -199,6 +201,7 @@ target_link_libraries(SimpleGraphic
199201
unofficial::angle::libGLESv2
200202
fmt::fmt
201203
glfw
204+
gli
202205
glm::glm
203206
imgui
204207
LuaJIT::LuaJIT

engine/core/core_image.cpp

Lines changed: 84 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -25,48 +25,79 @@
2525
#include <jxl/decode_cxx.h>
2626
#include <jxl/resizable_parallel_runner_cxx.h>
2727

28-
// =======
29-
// Classes
30-
// =======
31-
32-
// Generic client data structure, used by JPEG and PNG
33-
struct clientData_s {
34-
IConsole* con;
35-
const char* fileName;
36-
ioStream_c* io;
37-
};
28+
#include <gli/gli.hpp>
3829

3930
// =========
4031
// Raw Image
4132
// =========
4233

4334
image_c::image_c(IConsole* conHnd)
4435
: con(conHnd)
45-
{
46-
dat = NULL;
47-
}
48-
49-
image_c::~image_c()
50-
{
51-
Free();
52-
}
36+
{}
5337

5438
void image_c::CopyRaw(int inType, dword inWidth, dword inHeight, const byte* inDat)
5539
{
56-
if (dat) delete[] dat;
57-
dat = nullptr;
40+
// Populate legacy fields
5841
comp = inType & 0xF;
5942
type = inType;
6043
width = inWidth;
6144
height = inHeight;
62-
dat = new byte[width * height * comp];
63-
memcpy(dat, inDat, width * height * comp);
45+
46+
// Populate gli texture
47+
PopulateTex(inDat);
6448
}
6549

6650
void image_c::Free()
6751
{
68-
delete[] dat;
69-
dat = NULL;
52+
tex = {};
53+
}
54+
55+
//TODO(zao): Make this function only care about unpacked source data.
56+
void image_c::PopulateTex(const byte* dat)
57+
{
58+
gli::format format = gli::format::FORMAT_UNDEFINED;
59+
const glm::ivec2 extent{ width, height };
60+
size_t dataSize = width * height * comp; // Common default for regular formats.
61+
62+
const auto PhysicalSize = [](dword width, dword height, dword blockSize, dword side = 4) -> dword {
63+
dword numBlocks = ((width + side - 1) / side) * ((height + side - 1) / side);
64+
return numBlocks * blockSize;
65+
};
66+
switch ((imageType_s)type) {
67+
case imageType_s::IMGTYPE_NONE:
68+
tex = {};
69+
return;
70+
case imageType_s::IMGTYPE_GRAY:
71+
format = gli::format::FORMAT_R8_SRGB_PACK8;
72+
break;
73+
case imageType_s::IMGTYPE_RGB:
74+
format = gli::format::FORMAT_RGB8_SRGB_PACK8;
75+
break;
76+
case imageType_s::IMGTYPE_RGBA:
77+
format = gli::format::FORMAT_RGBA8_SNORM_PACK8;
78+
break;
79+
case imageType_s::IMGTYPE_RGBA_BC1:
80+
format = gli::format::FORMAT_RGBA_DXT1_SRGB_BLOCK8;
81+
dataSize = PhysicalSize(width, height, 8);
82+
break;
83+
case imageType_s::IMGTYPE_RGBA_BC2:
84+
format = gli::format::FORMAT_RGBA_DXT3_SRGB_BLOCK16;
85+
dataSize = PhysicalSize(width, height, 16);
86+
break;
87+
case imageType_s::IMGTYPE_RGBA_BC3:
88+
format = gli::format::FORMAT_RGBA_DXT5_SRGB_BLOCK16;
89+
dataSize = PhysicalSize(width, height, 16);
90+
break;
91+
case imageType_s::IMGTYPE_RGBA_BC7:
92+
format = gli::format::FORMAT_RGBA_BP_SRGB_BLOCK16;
93+
dataSize = PhysicalSize(width, height, 16);
94+
}
95+
96+
tex = gli::texture2d(format, extent, 1);
97+
if (tex.size(0) == dataSize)
98+
memcpy(tex.data(0, 0, 0), dat, dataSize);
99+
else
100+
assert(tex.size(0) == dataSize);
70101
}
71102

72103
bool image_c::Load(const char* fileName, std::optional<size_callback_t> sizeCallback)
@@ -176,7 +207,8 @@ bool targa_c::Load(const char* fileName, std::optional<size_callback_t> sizeCall
176207
comp = hdr.depth >> 3;
177208
type = ittable[it_m][2];
178209
int rowSize = width * comp;
179-
dat = new byte[height * rowSize];
210+
std::vector<byte> datBuf(height * rowSize);
211+
byte* dat = datBuf.data();
180212
bool flipV = !(hdr.descriptor & 0x20);
181213
if (hdr.imgType & 8) {
182214
// Decode RLE image
@@ -221,6 +253,8 @@ bool targa_c::Load(const char* fileName, std::optional<size_callback_t> sizeCall
221253
}
222254
}
223255

256+
PopulateTex(dat);
257+
224258
return false;
225259
}
226260

@@ -236,10 +270,13 @@ bool targa_c::Save(const char* fileName)
236270
return true;
237271
}
238272

273+
if (is_compressed(tex.format()))
274+
return true;
275+
239276
auto rc = stbi_write_tga_to_func([](void* ctx, void* data, int size) {
240277
auto out = (fileOutputStream_c*)ctx;
241278
out->Write(data, size);
242-
}, &out, width, height, comp, dat);
279+
}, &out, width, height, comp, tex.data(0, 0, 0));
243280

244281
return !rc;
245282
}
@@ -282,10 +319,10 @@ bool jpeg_c::Load(const char* fileName, std::optional<size_callback_t> sizeCallb
282319
height = y;
283320
comp = in_comp;
284321
type = in_comp == 1 ? IMGTYPE_GRAY : IMGTYPE_RGB;
285-
const size_t byteSize = width * height * comp;
286-
dat = new byte[byteSize];
287-
std::copy_n(data, byteSize, dat);
322+
323+
PopulateTex(data);
288324
stbi_image_free(data);
325+
289326
return false;
290327
}
291328

@@ -302,10 +339,13 @@ bool jpeg_c::Save(const char* fileName)
302339
return true;
303340
}
304341

342+
if (is_compressed(tex.format()))
343+
return true;
344+
305345
int rc = stbi_write_jpg_to_func([](void* ctx, void* data, int size) {
306346
auto out = (fileOutputStream_c*)ctx;
307347
out->Write(data, size);
308-
}, &out, width, height, comp, dat, quality);
348+
}, &out, width, height, comp, tex.data(0, 0, 0), quality);
309349
return !rc;
310350
}
311351

@@ -344,10 +384,10 @@ bool png_c::Load(const char* fileName, std::optional<size_callback_t> sizeCallba
344384
stbi_image_free(data);
345385
return true;
346386
}
347-
const size_t byteSize = width * height * comp;
348-
dat = new byte[byteSize];
349-
std::copy_n(data, byteSize, dat);
387+
388+
PopulateTex(data);
350389
stbi_image_free(data);
390+
351391
return false;
352392
}
353393

@@ -366,7 +406,7 @@ bool png_c::Save(const char* fileName)
366406
auto rc = stbi_write_png_to_func([](void* ctx, void* data, int size) {
367407
auto out = (fileOutputStream_c*)ctx;
368408
out->Write(data, size);
369-
}, &out, width, height, comp, dat, width * comp);
409+
}, &out, width, height, comp, tex.data(0, 0, 0), width * comp);
370410

371411
return !rc;
372412
}
@@ -401,10 +441,10 @@ bool gif_c::Load(const char* fileName, std::optional<size_callback_t> sizeCallba
401441

402442
comp = in_comp;
403443
type = IMGTYPE_RGBA;
404-
const size_t byteSize = width * height * comp;
405-
dat = new byte[byteSize];
406-
std::copy_n(data, byteSize, dat);
444+
445+
PopulateTex(data);
407446
stbi_image_free(data);
447+
408448
return false;
409449
}
410450
}
@@ -449,6 +489,8 @@ bool jpeg_xl_c::Load(const char* fileName, std::optional<size_callback_t> sizeCa
449489
JxlDecoderSetInput(dec.get(), fileData.data(), fileData.size());
450490
JxlDecoderCloseInput(dec.get());
451491

492+
std::vector<byte> datBuf;
493+
452494
while (true) {
453495
JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
454496

@@ -484,18 +526,19 @@ bool jpeg_xl_c::Load(const char* fileName, std::optional<size_callback_t> sizeCa
484526
size_t bufferSize{};
485527
if (JXL_DEC_SUCCESS != JxlDecoderImageOutBufferSize(dec.get(), &format, &bufferSize))
486528
return true;
487-
dat = new byte[width * height * comp];
488-
if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format, dat, bufferSize))
529+
assert(bufferSize == width * height * comp);
530+
datBuf.resize(bufferSize);
531+
if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format, datBuf.data(), bufferSize))
489532
return true;
490533
} break;
491534

535+
case JXL_DEC_SUCCESS:
492536
case JXL_DEC_FULL_IMAGE: {
493537
// We don't care about animations, consider loading completed when a full image has obtained.
538+
PopulateTex(datBuf.data());
494539
return false;
495540
} break;
496541

497-
case JXL_DEC_SUCCESS:
498-
return false;
499542
default:
500543
continue;
501544
}

engine/core/core_image.h

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,42 @@
1010

1111
#include <functional>
1212
#include <optional>
13+
14+
#include <gli/texture2d.hpp>
15+
1316
// Image types
1417
enum imageType_s {
1518
IMGTYPE_NONE = 0x00,
1619
IMGTYPE_GRAY = 0x11,
1720
IMGTYPE_RGB = 0x23,
1821
IMGTYPE_RGBA = 0x34,
1922
IMGTYPE_RGB_DXT1 = 0x63,
20-
IMGTYPE_RGBA_DXT1 = 0x74,
21-
IMGTYPE_RGBA_DXT3 = 0x84,
22-
IMGTYPE_RGBA_DXT5 = 0x94
23+
IMGTYPE_RGBA_BC1 = 0x74,
24+
IMGTYPE_RGBA_DXT1 = IMGTYPE_RGBA_BC1,
25+
IMGTYPE_RGBA_BC2 = 0x84,
26+
IMGTYPE_RGBA_DXT3 = IMGTYPE_RGBA_BC2,
27+
IMGTYPE_RGBA_BC3 = 0x94,
28+
IMGTYPE_RGBA_DXT5 = IMGTYPE_RGBA_BC3,
29+
//IMGTYPE_R_BC4 = 0xA4,
30+
//IMGTYPE_RG_BC5 = 0xB4,
31+
//IMGTYPE_RGBF_BC6H = 0xC4,
32+
IMGTYPE_RGBA_BC7 = 0xD4,
2333
};
2434

2535
// Image
2636
class image_c {
2737
public:
28-
byte* dat = nullptr;
2938
dword width = 0;
3039
dword height = 0;
3140
int comp = 0;
3241
int type = 0;
3342

34-
image_c(IConsole* conHnd = NULL);
35-
~image_c();
43+
// This `tex` member supersedes the past raw data and metadata fields in order to hold
44+
// both unblocked and blocked texture formats with array layers and mip levels.
45+
gli::texture2d tex{};
46+
47+
explicit image_c(IConsole* conHnd = NULL);
48+
virtual ~image_c() = default;
3649

3750
IConsole* con;
3851

@@ -43,6 +56,7 @@ class image_c {
4356

4457
void CopyRaw(int type, dword width, dword height, const byte* dat);
4558
void Free();
59+
void PopulateTex(const byte* inDat);
4660

4761
static image_c* LoaderForFile(IConsole* conHnd, const char* fileName);
4862
};
@@ -51,7 +65,7 @@ class image_c {
5165
class targa_c : public image_c {
5266
public:
5367
bool rle;
54-
targa_c(IConsole* conHnd): image_c(conHnd) { rle = true; }
68+
targa_c(IConsole* conHnd) : image_c(conHnd) { rle = true; }
5569
bool Load(const char* fileName, std::optional<size_callback_t> sizeCallback = {}) override;
5670
bool Save(const char* fileName) override;
5771
};
@@ -60,15 +74,15 @@ class targa_c : public image_c {
6074
class jpeg_c : public image_c {
6175
public:
6276
int quality;
63-
jpeg_c(IConsole* conHnd): image_c(conHnd) { quality = 80; }
77+
jpeg_c(IConsole* conHnd) : image_c(conHnd) { quality = 80; }
6478
bool Load(const char* fileName, std::optional<size_callback_t> sizeCallback = {}) override;
6579
bool Save(const char* fileName) override;
6680
};
6781

6882
// PNG Image
6983
class png_c : public image_c {
7084
public:
71-
png_c(IConsole* conHnd): image_c(conHnd) { }
85+
png_c(IConsole* conHnd) : image_c(conHnd) { }
7286
bool Load(const char* fileName, std::optional<size_callback_t> sizeCallback = {}) override;
7387
bool Save(const char* fileName) override;
7488
};
@@ -84,7 +98,7 @@ class jpeg_xl_c : public image_c {
8498
// GIF Image
8599
class gif_c : public image_c {
86100
public:
87-
gif_c(IConsole* conHnd): image_c(conHnd) { }
101+
gif_c(IConsole* conHnd) : image_c(conHnd) { }
88102
bool Load(const char* fileName, std::optional<size_callback_t> sizeCallback = {}) override;
89103
bool Save(const char* fileName) override;
90104
};

engine/render/r_main.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1880,9 +1880,9 @@ void r_renderer_c::DoScreenshot(image_c* i, const char* ext)
18801880
// Flip and convert the image to RGB
18811881
int const readSpan = xs * 4;
18821882
int const writeSpan = xs * 3;
1883-
byte* ss = new byte[writeSize]; // This is a raw pointer as ownership is taken by the image object.
1883+
std::vector<byte> ss(writeSize); // This is a raw pointer as ownership is taken by the image object.
18841884
byte* p1 = sbuf.data();
1885-
byte* p2 = ss + writeSize - writeSpan;
1885+
byte* p2 = ss.data() + writeSize - writeSpan;
18861886
for (int y = 0; y < ys; ++y, p2 -= writeSpan * 2) {
18871887
for (int x = 0; x < xs; ++x) {
18881888
*p2++ = *p1++; // R
@@ -1894,7 +1894,8 @@ void r_renderer_c::DoScreenshot(image_c* i, const char* ext)
18941894
sbuf.clear();
18951895

18961896
// Set image info
1897-
i->dat = ss;
1897+
i->PopulateTex(ss.data());
1898+
ss.clear();
18981899
i->width = xs;
18991900
i->height = ys;
19001901
i->comp = 3;

0 commit comments

Comments
 (0)