You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
234 lines
5.7 KiB
234 lines
5.7 KiB
|
|
#include <cassert> |
|
#include <cmath> |
|
#include <cstdlib> |
|
#include <cstdio> |
|
#include <cstring> |
|
|
|
#define STB_IMAGE_IMPLEMENTATION |
|
#include "stb_image.h" |
|
#include "aixlog.hpp" |
|
|
|
#include "util.h" |
|
|
|
|
|
#define PALETTE_ROWS 16 |
|
#define PALETTE_COLUMNS 16 |
|
#define PALETTE_BORDER 1 |
|
const uint MAX_FILESIZE = 2 * 1024 * 1024; // 2MB |
|
const uint MAX_STRING_LENGTH = 1024; |
|
|
|
|
|
//----------------- |
|
// C Strings |
|
|
|
const char* |
|
utilBaseName(const char* path_str) |
|
{ |
|
assert(std::strlen(path_str) < MAX_STRING_LENGTH); |
|
const char* output = std::strrchr(path_str, '/'); |
|
if (output) |
|
return output; |
|
else |
|
return path_str; |
|
} |
|
|
|
bool |
|
utilCopyCStr(char* dest, const char* src, uint max_len) |
|
{ |
|
assert(std::strlen(src) < MAX_STRING_LENGTH && max_len <= MAX_STRING_LENGTH); |
|
if (std::strlen(src) + 1 > max_len) |
|
return false; |
|
|
|
std::memcpy(dest, src, std::strlen(src) + 1); |
|
return true; |
|
} |
|
|
|
bool |
|
utilConcatPath(char* out, const char* base_dir, const char* file_name, uint max_len) |
|
{ |
|
size_t l1 = std::strlen(base_dir); |
|
size_t l2 = std::strlen(file_name); |
|
size_t padded = l1 + l2 + 2; // NOTE: null term + '/' |
|
|
|
if (padded <= MAX_STRING_LENGTH && padded <= max_len) { |
|
int c = std::snprintf(out, padded, "%s/%s", base_dir, file_name); |
|
if (c > 0) |
|
return true; |
|
else |
|
return false; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool |
|
utilMatchPrefix(const char* lhs, const char* rhs, int sz) |
|
{ |
|
int rc = strncmp(lhs, rhs, sz); |
|
return (rc >= 0); |
|
} |
|
|
|
//----------------- |
|
// Memory allocation |
|
|
|
void * |
|
utilLogAlloc(uint item_count, uint type_size, const char* file_name, const int line) |
|
{ |
|
assert(item_count > 0); // that was a fun bug |
|
|
|
void* mem = std::calloc(item_count, type_size); |
|
|
|
if (mem == nullptr) { |
|
LOG(ERROR) << "Memory allocation failed, called from " |
|
<< file_name << ":" << line; |
|
} |
|
|
|
assert(mem != nullptr); // might as well stop execution here |
|
|
|
return mem; |
|
} |
|
|
|
void utilSafeFree(const void* mem) |
|
{ |
|
if (mem != nullptr) { |
|
std::free((void *) mem); |
|
mem = nullptr; |
|
} |
|
} |
|
|
|
//----------------- |
|
// File I/O |
|
|
|
// TODO: don't use ftell() to get filesize |
|
// https://wiki.sei.cmu.edu/confluence/display/c/FIO19-C.+Do+not+use+fseek%28%29+and+ftell%28%29+to+compute+the+size+of+a+regular+file |
|
char * |
|
utilDumpTextFile(const char* filename) |
|
{ |
|
LOG(INFO) << "loading filename, " << filename << "\n"; |
|
std::FILE* fp = std::fopen(filename, "rt"); |
|
assert(fp); |
|
|
|
std::fseek(fp, 0, SEEK_END); |
|
uint length = std::ftell(fp); |
|
std::fseek(fp, 0, SEEK_SET); |
|
assert(length < MAX_FILESIZE); |
|
// TODO: check error codes for fseek and ftell |
|
|
|
char* buf = UTIL_ALLOC(length, char); |
|
assert(buf); |
|
|
|
std::fread(buf, sizeof(char), length, fp); |
|
// TODO: check fp w/ ferror() here |
|
|
|
return buf; |
|
} |
|
|
|
// TODO: might want to do the base_dir concat in this function to prevent clobbering |
|
// user files on accident |
|
bool |
|
utilWriteTextFile(const char* filename, const char* text) |
|
{ |
|
size_t text_len = std::strlen(text); |
|
|
|
if (text_len >= MAX_FILESIZE) { |
|
LOG(ERROR) << "that string is too big\n"; |
|
return false; |
|
} |
|
|
|
std::FILE* fp = fopen(filename, "wt"); |
|
if (fp) { |
|
size_t written = fwrite(text, sizeof(char), text_len, fp); |
|
fclose(fp); |
|
if (written == text_len) { |
|
LOG(DEBUG) << "successfuly wrote " << written << " bytes\n"; |
|
return true; |
|
} else { |
|
LOG(ERROR) << "error writing to file: " << filename << "\n"; |
|
return false; |
|
} |
|
} |
|
|
|
LOG(DEBUG) << text << "\n"; |
|
return false; |
|
} |
|
|
|
//----------------- |
|
// utilImage |
|
|
|
util_image |
|
utilLoadImage(const char* base_dir, const char* filename) |
|
{ |
|
const uint max_path = 256; // NOTE: util_image.file_path is char[256] |
|
const char* base_name = utilBaseName(filename); |
|
// NOTE: +1 for '/' char, +1 for null |
|
uint l = std::strlen(base_dir) + std::strlen(base_name) + 2; |
|
assert(l < max_path); |
|
char full_path[l]; |
|
std::memcpy(full_path, base_dir, std::strlen(base_dir) + 1); |
|
std::strcat(std::strcat(full_path, "/"), base_name); |
|
LOG(INFO) << "Loading Image: " << full_path << "\n"; |
|
|
|
util_image image; |
|
std::memcpy(image.file_path, full_path, std::strlen(full_path) + 1); |
|
stbi_set_flip_vertically_on_load(1); |
|
image.pixels = stbi_load(full_path, &image.w, &image.h, &image.num_channels, 0); |
|
image.bits_per_channel = 8; |
|
image.data_len = image.w * image.h * image.num_channels; // NOTE: assumes 8 bits per channel |
|
|
|
if (image.pixels == 0) { |
|
LOG(ERROR) << stbi_failure_reason() << "\n"; |
|
utilFreeImage(image); |
|
return image; |
|
} |
|
|
|
LOG(INFO) << "Image properties, data_len: " << image.data_len |
|
<< ", width: " << image.w << ", height: " << image.h << "\n"; |
|
|
|
return image; |
|
} |
|
|
|
void |
|
utilFreeImage(util_image image) |
|
{ |
|
image.w = image.h = image.data_len = 0; |
|
image.bits_per_channel = 8; |
|
image.num_channels = 4; |
|
std::memset(image.file_path, 0, std::strlen(image.file_path) + 1); |
|
utilSafeFree(image.pixels); |
|
} |
|
|
|
// need to init default values from scene file, or finally make default config? |
|
v2f |
|
utilGetPaletteCoords(util_image& palette_image, uint color_index) |
|
{ |
|
v2i tex_coords; |
|
v2f normalized; |
|
|
|
if (PALETTE_ROWS * PALETTE_COLUMNS < color_index) { |
|
LOG(ERROR) << "index out of range\n"; |
|
return normalized; |
|
} |
|
|
|
if (palette_image.pixels == nullptr) { |
|
LOG(ERROR) << "palatte image not loaded\n"; |
|
return normalized; |
|
} |
|
|
|
// NOTE: we're aiming to sample near the middle of the palette 'square' so GL_NEAREST |
|
// doesn't try to sample the border color |
|
v2i palette_square; |
|
palette_square.x = palette_image.w / PALETTE_ROWS; |
|
palette_square.y = palette_image.h / PALETTE_COLUMNS; |
|
uint v_row = (uint) std::floor(color_index / PALETTE_ROWS); |
|
uint u_column = color_index % PALETTE_ROWS; |
|
tex_coords.x = u_column * palette_square.x + palette_square.x / 2; |
|
tex_coords.y = v_row * palette_square.y + palette_square.y / 2; |
|
// NOTE: flip y coord |
|
tex_coords.y = palette_image.h - tex_coords.y; |
|
|
|
normalized.x = (float) tex_coords.x / palette_image.w; |
|
normalized.y = (float) tex_coords.y / palette_image.h; |
|
|
|
return normalized; |
|
}
|
|
|