Files
Lucina3DS/externals/dds-ktx/ctexview/ctexview.c
2025-02-06 22:24:29 +08:00

760 lines
22 KiB
C

#if defined(_WIN32) || defined(_WIN64)
# define SOKOL_D3D11
# define _CRT_SECURE_NO_WARNINGS
# include "quad_hlsl.vert.h"
# include "quad_hlsl.frag.h"
# include "quad_cubemap_hlsl.frag.h"
# define SOKOL_LOG(s) OutputDebugStringA(s)
#elif defined(__linux__)
# include "quad_glsl.vert.h"
# include "quad_glsl.frag.h"
# include "quad_cubemap_glsl.frag.h"
# define SOKOL_GLCORE33
#elif defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
# include "quad_metal.vert.h"
# include "quad_metal.frag.h"
# include "quad_cubemap_metal.frag.h"
# define SOKOL_METAL
#endif
#define SOKOL_IMPL
#define DDSKTX_IMPLEMENT
#define SOKOL_API_DECL static
#include "sokol_app.h"
#include "sokol_gfx.h"
#include "sokol_glue.h"
#define DDSKTX_API static
#include "../dds-ktx.h"
#ifndef __APPLE__
# include <malloc.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <stdarg.h>
#define SOKOL_DEBUGTEXT_IMPL
#include "sokol_debugtext.h"
#define FONT_SCALE 1.1f
#define CHECKER_SIZE 8
typedef struct uniforms_fs
{
float color[4];
float args[4];
} uniforms_fs;
typedef struct uniforms_vs
{
float proj_mat[16];
} uniforms_vs;
typedef struct vertex
{
float x;
float y;
float u;
float v;
float w; // reserved for cubemapping
} vertex;
typedef struct ctexview_state
{
sg_pass_action pass_action;
void* file_data;
int file_size;
ddsktx_texture_info texinfo;
sg_image tex;
sg_shader shader;
sg_shader shader_cubemap;
sg_pipeline pip;
sg_pipeline pip_cubemap;
sg_pipeline pip_checker;
sg_buffer vb;
sg_buffer ib;
sg_buffer vb_checker;
sg_image checker;
bool inv_text_color;
uniforms_fs vars_fs;
int cur_mip;
int cur_slice;
int cube_face;
} ctexview_state;
ctexview_state g_state;
static const vertex k_vertices[] = {
{ -1.0f, -1.0f, 0.0f, 1.0f, 0 },
{ 1.0f, -1.0f, 1.0f, 1.0f, 0 },
{ 1.0f, 1.0f, 1.0f, 0.0f, 0 },
{ -1.0f, 1.0f, 0.0f, 0.0f, 0 },
};
static const uint16_t k_indices[] = { 0, 2, 1, 2, 0, 3 };
static const char* k_cube_face_names[DDSKTX_CUBE_FACE_COUNT] = {
"X+",
"X-",
"Y+",
"Y-",
"Z+",
"Z-"
};
#if defined(_WIN32) || defined(_WIN64)
static desktop_size(int* width, int* height)
{
RECT desktop;
const HWND hDesktop = GetDesktopWindow();
GetWindowRect(hDesktop, &desktop);
*width = desktop.right;
*height = desktop.bottom;
}
#endif
static int nearest_pow2(int n)
{
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n++;
return n;
}
// https://en.wikipedia.org/wiki/Cube_mapping
static void convert_cube_uv_to_xyz(int index, float u, float v, float* x, float* y, float* z)
{
// convert range 0 to 1 to -1 to 1
float uc = 2.0f * u - 1.0f;
float vc = 2.0f * v - 1.0f;
switch (index) {
case 0: *x = 1.0f; *y = vc; *z = -uc; break; // POSITIVE X
case 1: *x = -1.0f; *y = vc; *z = uc; break; // NEGATIVE X
case 2: *x = uc; *y = 1.0f; *z = -vc; break; // POSITIVE Y
case 3: *x = uc; *y = -1.0f; *z = vc; break; // NEGATIVE Y
case 4: *x = uc; *y = vc; *z = 1.0f; break; // POSITIVE Z
case 5: *x = -uc; *y = vc; *z = -1.0f; break; // NEGATIVE Z
}
}
static void set_cube_face(int index)
{
if (g_state.texinfo.flags & DDSKTX_TEXTURE_FLAG_CUBEMAP) {
assert(index >= 0 && index < DDSKTX_CUBE_FACE_COUNT);
vertex vertices[4];
memcpy(vertices, k_vertices, sizeof(k_vertices));
for (int i = 0; i < 4; i++) {
float x, y, z;
convert_cube_uv_to_xyz(index, vertices[i].u, vertices[i].v, &x, &y, &z);
vertices[i].u = x;
vertices[i].v = y;
vertices[i].w = z;
}
sg_update_buffer(g_state.vb, vertices, sizeof(vertices));
}
}
static void adjust_checker_coords(int width, int height)
{
int count_x = width/CHECKER_SIZE;
int count_y = height/CHECKER_SIZE;
float ratio = (float)width / (float)height;
float u, v;
if (width > height) {
u = (float)count_x;
v = (float)count_y * ratio;
}
else {
v = (float)count_y;
u = (float)count_x / ratio;
}
vertex vertices[4];
memcpy(vertices, k_vertices, sizeof(k_vertices));
for (int i = 0; i < 4; i++) {
vertices[i].u = vertices[i].u != 0 ? u : 0;
vertices[i].v = vertices[i].v != 0 ? v : 0;
}
sg_update_buffer(g_state.vb_checker, vertices, sizeof(vertices));
}
static void sx_mat4_ortho(float mat[16], float width, float height, float zn, float zf, float offset, bool ogl_ndc)
{
const float d = zf - zn;
const float cc = (ogl_ndc ? 2.0f : 1.0f) / d;
const float ff = ogl_ndc ? -(zn + zf) / d : -zn / d;
mat[0] = 2.0f / width;
mat[1] = 0;
mat[2] = 0;
mat[3] = 0;
mat[4] = 0;
mat[5] = 2.0f / height;
mat[6] = 0;
mat[7] = 0;
mat[8] = 0;
mat[9] = 0;
mat[10] = -cc;
mat[11] = 0;
mat[12] = offset;
mat[13] = 0;
mat[14] = ff;
mat[15] = 1.0f;
}
static void sx_mat4_ident(float mat[16])
{
mat[0] = 1.0f;
mat[1] = 0;
mat[2] = 0;
mat[3] = 0;
mat[4] = 0;
mat[5] = 1.0f;
mat[6] = 0;
mat[7] = 0;
mat[8] = 0;
mat[9] = 0;
mat[10] = 1.0f;
mat[11] = 0;
mat[12] = 0;
mat[13] = 0;
mat[14] = 0;
mat[15] = 1.0f;
}
static sg_image create_checker_texture(int checker_size, int size, uint32_t colors[2])
{
assert(size % 4 == 0 && "size must be multiple of four");
assert(size % checker_size == 0 && "checker_size must be dividable by size");
int size_bytes = size * size * sizeof(uint32_t);
uint32_t* pixels = malloc(size_bytes);
assert(pixels);
// split into tiles and color them
int tiles_x = size / checker_size;
int tiles_y = size / checker_size;
int num_tiles = tiles_x * tiles_y;
int* poss = malloc(sizeof(int) * 2 * num_tiles);
assert(poss);
int _x = 0, _y = 0;
for (int i = 0; i < num_tiles; i++) {
poss[i*2] = _x;
poss[i*2 + 1] = _y;
_x += checker_size;
if (_x >= size) {
_x = 0;
_y += checker_size;
}
}
int color_idx = 0;
for (int i = 0; i < num_tiles; i++) {
int* p = poss + i*2;
uint32_t c = colors[color_idx];
if (i == 0 || ((i + 1) % tiles_x) != 0)
color_idx = !color_idx;
int end_x = p[0] + checker_size;
int end_y = p[1] + checker_size;
for (int y = p[1]; y < end_y; y++) {
for (int x = p[0]; x < end_x; x++) {
int pixel = x + y * size;
pixels[pixel] = c;
}
}
}
sg_image tex = sg_make_image(&(sg_image_desc) {
.width = size,
.height = size,
.num_mipmaps = 1,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.content = (sg_image_content){.subimage[0][0].ptr = pixels,
.subimage[0][0].size = size_bytes }
});
free(poss);
free(pixels);
return tex;
}
static void print_msg(const char* fmt, ...)
{
char msg[1024];
va_list args;
va_start(args, fmt);
vsnprintf(msg, sizeof(msg), fmt, args);
va_end(args);
#if defined(_WIN32) || defined(_WIN64)
MessageBoxA(NULL, msg, "DDS/KTX viewer", MB_OK);
#else
puts(msg);
#endif
}
static sg_shader_desc get_shader_desc(const void* vs_data, uint32_t vs_size, const void* fs_data,
uint32_t fs_size, sg_image_type imgtype)
{
return (sg_shader_desc){
.attrs = {
[0] = {.name = "a_pos", .sem_name = "POSITION" },
[1] = {.name = "a_uv", .sem_name = "TEXCOORD" }
},
.vs = {
#ifdef SOKOL_D3D11
.byte_code = (const uint8_t*)vs_data,
.byte_code_size = vs_size,
#else
.source = (const char*)vs_data,
#endif
#ifdef SOKOL_METAL
.entry = "main0",
#endif
.uniform_blocks[0] = {
.size = sizeof(uniforms_vs),
.uniforms = {
[0] = {.name = "proj_mat", .type = SG_UNIFORMTYPE_MAT4 },
}
}
},
.fs = {
#ifdef SOKOL_D3D11
.byte_code = (const uint8_t*)fs_data,
.byte_code_size = fs_size,
#else
.source = (const char*)fs_data,
#endif
#ifdef SOKOL_METAL
.entry = "main0",
#endif
.images[0] = {
.name = "tex_image",
.type = imgtype
},
.uniform_blocks[0] = {
.size = sizeof(uniforms_fs),
.uniforms = {
[0] = {.name = "color", .type = SG_UNIFORMTYPE_FLOAT4 },
[1] = {.name = "target_lod", SG_UNIFORMTYPE_FLOAT4 }
}
}
}
};
}
static void init(void)
{
sg_setup(&(sg_desc) {
.context = sapp_sgcontext()
});
sg_image_type imgtype = (g_state.texinfo.flags & DDSKTX_TEXTURE_FLAG_CUBEMAP) ? SG_IMAGETYPE_CUBE : SG_IMAGETYPE_2D;
g_state.pass_action = (sg_pass_action) {
.colors[0] = { .action = SG_ACTION_CLEAR, .val = {0, 0, 0, 1.0f} }
};
g_state.vb = sg_make_buffer(&(sg_buffer_desc) {
.usage = SG_USAGE_DYNAMIC,
.type = SG_BUFFERTYPE_VERTEXBUFFER,
.size = sizeof(k_vertices)
});
g_state.vb_checker = sg_make_buffer(&(sg_buffer_desc) {
.usage = SG_USAGE_DYNAMIC,
.type = SG_BUFFERTYPE_VERTEXBUFFER,
.size = sizeof(k_vertices)
});
g_state.ib = sg_make_buffer(&(sg_buffer_desc) {
.usage = SG_USAGE_IMMUTABLE,
.type = SG_BUFFERTYPE_INDEXBUFFER,
.size = sizeof(k_indices),
.content = k_indices
});
{
sg_shader_desc desc = get_shader_desc(quad_vs_data, quad_vs_size, quad_fs_data, quad_fs_size,
SG_IMAGETYPE_2D);
g_state.shader = sg_make_shader(&desc);
}
{
sg_shader_desc desc = get_shader_desc(quad_vs_data, quad_vs_size, quad_cubemap_fs_data,
quad_cubemap_fs_size, SG_IMAGETYPE_CUBE);
g_state.shader_cubemap = sg_make_shader(&desc);
}
sg_pipeline_desc pip_desc = (sg_pipeline_desc) {
.layout = {
.buffers[0] = {
.stride = sizeof(vertex),
},
.attrs = {
[0] = {.offset = 0, .format = SG_VERTEXFORMAT_FLOAT2 },
[1] = {.offset = 8, .format = SG_VERTEXFORMAT_FLOAT3 }
}
},
.primitive_type = SG_PRIMITIVETYPE_TRIANGLES,
.index_type = SG_INDEXTYPE_UINT16,
.rasterizer = {
.cull_mode = SG_CULLMODE_BACK
},
.blend = {
.enabled = true,
.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA,
.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA
}
};
{
pip_desc.shader = g_state.shader;
g_state.pip = sg_make_pipeline(&pip_desc);
}
{
pip_desc.shader = g_state.shader_cubemap;
g_state.pip_cubemap = sg_make_pipeline(&pip_desc);
}
// main texture (dds-ktx)
if (imgtype == SG_IMAGETYPE_CUBE) {
set_cube_face(0);
} else {
sg_update_buffer(g_state.vb, k_vertices, sizeof(k_vertices));
}
adjust_checker_coords(sapp_width(), sapp_height());
sg_image_desc desc = {
.type = imgtype,
.width = g_state.texinfo.width,
.height = g_state.texinfo.height,
.depth = 1,
.num_mipmaps = g_state.texinfo.num_mips,
.min_filter = SG_FILTER_NEAREST,
.mag_filter = SG_FILTER_NEAREST
};
switch (g_state.texinfo.format) {
case DDSKTX_FORMAT_BC1: desc.pixel_format = SG_PIXELFORMAT_BC1_RGBA; break;
case DDSKTX_FORMAT_BC2: desc.pixel_format = SG_PIXELFORMAT_BC2_RGBA; break;
case DDSKTX_FORMAT_BC3: desc.pixel_format = SG_PIXELFORMAT_BC3_RGBA; break;
case DDSKTX_FORMAT_BC4: desc.pixel_format = SG_PIXELFORMAT_BC4_R; break;
case DDSKTX_FORMAT_BC5: desc.pixel_format = SG_PIXELFORMAT_BC5_RG; break;
case DDSKTX_FORMAT_BC6H: desc.pixel_format = SG_PIXELFORMAT_BC6H_RGBF; break;
case DDSKTX_FORMAT_BC7: desc.pixel_format = SG_PIXELFORMAT_BC7_RGBA; break;
case DDSKTX_FORMAT_A8:
case DDSKTX_FORMAT_R8: desc.pixel_format = SG_PIXELFORMAT_R8; break;
case DDSKTX_FORMAT_RGBA8:
case DDSKTX_FORMAT_RGBA8S: desc.pixel_format = SG_PIXELFORMAT_RGBA8; break;
case DDSKTX_FORMAT_RG16: desc.pixel_format = SG_PIXELFORMAT_RG16; break;
case DDSKTX_FORMAT_RGB8: desc.pixel_format = SG_PIXELFORMAT_RGBA8; break;
case DDSKTX_FORMAT_R16: desc.pixel_format = SG_PIXELFORMAT_R16; break;
case DDSKTX_FORMAT_R32F: desc.pixel_format = SG_PIXELFORMAT_R32F; break;
case DDSKTX_FORMAT_R16F: desc.pixel_format = SG_PIXELFORMAT_R16F; break;
case DDSKTX_FORMAT_RG16F: desc.pixel_format = SG_PIXELFORMAT_RG16F; break;
case DDSKTX_FORMAT_RG16S: desc.pixel_format = SG_PIXELFORMAT_RG16; break;
case DDSKTX_FORMAT_RGBA16F: desc.pixel_format = SG_PIXELFORMAT_RGBA16F; break;
case DDSKTX_FORMAT_RGBA16: desc.pixel_format = SG_PIXELFORMAT_RGBA16; break;
case DDSKTX_FORMAT_BGRA8: desc.pixel_format = SG_PIXELFORMAT_BGRA8; break;
case DDSKTX_FORMAT_RGB10A2: desc.pixel_format = SG_PIXELFORMAT_RGB10A2; break;
case DDSKTX_FORMAT_RG11B10F: desc.pixel_format = SG_PIXELFORMAT_RG11B10F; break;
case DDSKTX_FORMAT_RG8: desc.pixel_format = SG_PIXELFORMAT_RG8; break;
case DDSKTX_FORMAT_RG8S: desc.pixel_format = SG_PIXELFORMAT_RG8; break;
default: assert(0); exit(-1);
}
int num_faces = imgtype == SG_IMAGETYPE_CUBE ? 6 : 1;
for (int face = 0; face < num_faces; face++) {
for (int mip = 0; mip < g_state.texinfo.num_mips; mip++) {
ddsktx_sub_data subdata;
ddsktx_get_sub(&g_state.texinfo, &subdata, g_state.file_data, g_state.file_size, 0, face, mip);
desc.content.subimage[face][mip].ptr = subdata.buff;
desc.content.subimage[face][mip].size = subdata.size_bytes;
}
}
g_state.tex = sg_make_image(&desc);
sdtx_setup(&(sdtx_desc_t) {
.fonts = {
[0] = sdtx_font_c64(),
},
});
sdtx_set_context(SDTX_DEFAULT_CONTEXT);
sdtx_canvas((float)sapp_width() * (1.0f/FONT_SCALE), (float)sapp_height() * (1.0f/FONT_SCALE));
uint32_t checker_colors[] = { 0xff999999, 0xff666666 };
g_state.checker = create_checker_texture(8, 16, checker_colors);
g_state.vars_fs.color[0] = g_state.vars_fs.color[1] = g_state.vars_fs.color[2] = g_state.vars_fs.color[3] = 1.0f;
}
static const char* texture_type_info()
{
static char info[128];
const char* type = "2D";
if (g_state.texinfo.flags & DDSKTX_TEXTURE_FLAG_CUBEMAP) {
snprintf(info, sizeof(info), "Cube (%s)", k_cube_face_names[g_state.cube_face]);
} else if (g_state.texinfo.depth > 1) {
snprintf(info, sizeof(info), "3D (%d/%d)", g_state.cur_slice, g_state.texinfo.depth);
} else {
strcpy(info, "2D");
}
return info;
}
static void frame(void)
{
sdtx_home();
sdtx_origin(1, 1);
sdtx_pos(0, 0);
sdtx_color3b(!g_state.inv_text_color ? 255 : 0, !g_state.inv_text_color ? 255 : 0, 0);
sdtx_printf("%s\t%dx%d (mip %d/%d)",
ddsktx_format_str(g_state.texinfo.format), g_state.texinfo.width,
g_state.texinfo.height, g_state.cur_mip + 1, g_state.texinfo.num_mips);
sdtx_crlf();
sdtx_printf("%s\tmask: %c%c%c%c\t",
texture_type_info(),
g_state.vars_fs.color[0] == 1.0f ? 'R' : 'X',
g_state.vars_fs.color[1] == 1.0f ? 'G' : 'X',
g_state.vars_fs.color[2] == 1.0f ? 'B' : 'X',
g_state.vars_fs.color[3] == 1.0f ? 'A' : 'X');
sdtx_crlf();
g_state.vars_fs.args[0] = (float)g_state.cur_mip;
sg_begin_default_pass(&g_state.pass_action, sapp_width(), sapp_height());
if (g_state.tex.id) {
sg_bindings bindings = {
.index_buffer = g_state.ib,
};
if (g_state.checker.id) {
bindings.fs_images[0] = g_state.checker;
bindings.vertex_buffers[0] = g_state.vb_checker;
uniforms_fs ufs = {
{1.0f, 1.0f, 1.0f, 1.0f},
{0, 0, 0, 0}
};
uniforms_vs uvs;
float ratio = (float)sapp_width() / (float)sapp_height();
float w = 1.0f, h = 1.0f;
if (sapp_width() > sapp_height()) {
h = w/ratio;
} else {
w = h*ratio;
}
sx_mat4_ortho(uvs.proj_mat, w, h, -1.0f, 1.0f, 0, false);
sg_apply_pipeline(g_state.pip);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &uvs, sizeof(uvs));
sg_apply_uniforms(SG_SHADERSTAGE_FS, 0, &ufs, sizeof(ufs));
sg_apply_bindings(&bindings);
sg_draw(0, 6, 1);
}
uniforms_vs uvs;
sx_mat4_ident(uvs.proj_mat);
bindings.fs_images[0] = g_state.tex;
bindings.vertex_buffers[0] = g_state.vb;
// for the image to the window and keep the ratio
int w = g_state.texinfo.width;
int h = g_state.texinfo.height;
{
float ratio_outer = (float)sapp_width() / (float)sapp_height();
float ratio_inner = (float)w / (float)h;
float scale = (ratio_inner >= ratio_outer) ?
((float)sapp_width()/(float)w) :
((float)sapp_height()/(float)h);
w = (int)((float)w * scale);
h = (int)((float)h * scale);
}
sg_apply_viewport((sapp_width() - w)/2, (sapp_height() - h)/2, w, h, true);
sg_apply_pipeline((g_state.texinfo.flags & DDSKTX_TEXTURE_FLAG_CUBEMAP) ? g_state.pip_cubemap : g_state.pip);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &uvs, sizeof(uvs));
sg_apply_uniforms(SG_SHADERSTAGE_FS, 0, &g_state.vars_fs, sizeof(g_state.vars_fs));
sg_apply_bindings(&bindings);
sg_draw(0, 6, 1);
}
sg_apply_viewport(0, 0, sapp_width(), sapp_height(), true);
sdtx_draw();
sg_end_pass();
sg_commit();
}
static void release(void)
{
free(g_state.file_data);
sg_destroy_pipeline(g_state.pip);
sg_destroy_pipeline(g_state.pip_checker);
sg_destroy_pipeline(g_state.pip_cubemap);
sg_destroy_shader(g_state.shader);
sg_destroy_shader(g_state.shader_cubemap);
sg_destroy_buffer(g_state.vb);
sg_destroy_buffer(g_state.vb_checker);
sg_destroy_buffer(g_state.ib);
sg_destroy_image(g_state.tex);
sg_destroy_image(g_state.checker);
sdtx_shutdown();
sg_shutdown();
}
static void on_events(const sapp_event* e)
{
switch (e->type) {
case SAPP_EVENTTYPE_RESIZED:
sdtx_canvas((float)sapp_width() * (1.0f/FONT_SCALE), (float)sapp_height() * (1.0f/FONT_SCALE));
adjust_checker_coords(e->window_width, e->window_height);
break;
case SAPP_EVENTTYPE_KEY_DOWN:
if (e->key_code == SAPP_KEYCODE_GRAVE_ACCENT) {
g_state.inv_text_color = !g_state.inv_text_color;
}
if (e->key_code == SAPP_KEYCODE_A) {
g_state.vars_fs.color[3] = g_state.vars_fs.color[3] == 1.0f ? 0 : 1.0f;
}
if (e->key_code == SAPP_KEYCODE_R) {
g_state.vars_fs.color[0] = g_state.vars_fs.color[0] == 1.0f ? 0 : 1.0f;
}
if (e->key_code == SAPP_KEYCODE_G) {
g_state.vars_fs.color[1] = g_state.vars_fs.color[1] == 1.0f ? 0 : 1.0f;
}
if (e->key_code == SAPP_KEYCODE_B) {
g_state.vars_fs.color[2] = g_state.vars_fs.color[2] == 1.0f ? 0 : 1.0f;
}
if (e->key_code == SAPP_KEYCODE_UP) {
g_state.cur_mip = (g_state.cur_mip + 1) >= g_state.texinfo.num_mips ? (g_state.texinfo.num_mips - 1) : g_state.cur_mip + 1;
}
if (e->key_code == SAPP_KEYCODE_DOWN) {
g_state.cur_mip = (g_state.cur_mip > 0) ? g_state.cur_mip - 1 : 0;
}
if (e->key_code == SAPP_KEYCODE_ESCAPE) {
sapp_request_quit();
}
if (e->key_code == SAPP_KEYCODE_F) {
g_state.cube_face = (g_state.cube_face + 1) % DDSKTX_CUBE_FACE_COUNT;
set_cube_face(g_state.cube_face);
}
break;
}
}
sapp_desc sokol_main(int argc, char* argv[])
{
if (argc <= 1) {
print_msg("Provide a file to load as argument");
exit(-1);
}
FILE* f = fopen(argv[1], "rb");
if (!f) {
print_msg("Error: could not open file: %s\n", argv[1]);
exit(-1);
}
fseek(f, 0, SEEK_END);
int size = (int)ftell(f);
if (size == 0) {
print_msg("Error: file '%s' is empty\n", argv[1]);
exit(-1);
}
fseek(f, 0, SEEK_SET);
void* data = malloc(size);
if (!data) {
print_msg("out of memory: requested size: %d\n", (int)size);
exit(-1);
}
if (fread(data, 1, size, f) != size) {
print_msg("could not read file data : %s\n", argv[1]);
exit(-1);
}
g_state.file_data = data;
g_state.file_size = size;
fclose(f);
ddsktx_texture_info tc = {0};
ddsktx_error img_err;
if (!ddsktx_parse(&tc, data, size, &img_err)) {
print_msg("Loading image '%s' failed: %s", argv[1], img_err.msg);
exit(-1);
}
g_state.texinfo = tc;
int window_w = tc.width;
int window_h = tc.height;
#if defined(_WIN32) || defined(_WIN64)
int desktop_w, desktop_h;
desktop_size(&desktop_w, &desktop_h);
float ratio = (float)tc.width / (float)tc.height;
if (window_w > (desktop_w - 50)) {
window_w = desktop_w - 50;
window_h = (int)((float)window_w / ratio);
}
if (window_h > (desktop_h - 50)) {
window_h = desktop_h - 50;
window_w = (int)((float)window_h * ratio);
}
#endif
return (sapp_desc) {
.init_cb = init,
.frame_cb = frame,
.cleanup_cb = release,
.event_cb = on_events,
.width = window_w,
.height = window_h,
.window_title = "DDS/KTX viewer",
.swap_interval = 2,
.sample_count = 1
};
}