load and display bitmaps
This commit is contained in:
parent
857f368970
commit
0f5e6d9a4d
255
src/assets.c++
Normal file
255
src/assets.c++
Normal file
@ -0,0 +1,255 @@
|
||||
#include <cstdio>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <string>
|
||||
|
||||
#include "ass.h"
|
||||
#include "rect-pack.h"
|
||||
#include "batch.h"
|
||||
|
||||
#include <glad/gles2.h>
|
||||
|
||||
#include "main.h++"
|
||||
|
||||
static int read_vlq(unsigned char const *buf, size_t size, size_t &out) {
|
||||
size_t outsize = buf[0];
|
||||
unsigned outsizebytes = 1;
|
||||
for (unsigned m = 0x80; outsize & m; m = m >> 1) {
|
||||
outsizebytes++;
|
||||
outsize &= ~m;
|
||||
}
|
||||
if (outsizebytes > size) {
|
||||
return -outsizebytes;
|
||||
}
|
||||
for (unsigned i = 1; i < outsizebytes; i++) {
|
||||
outsize = (outsize << 8) | buf[i];
|
||||
}
|
||||
out = outsize;
|
||||
return outsizebytes;
|
||||
}
|
||||
|
||||
struct {
|
||||
std::vector<std::string> filename;
|
||||
std::vector<struct rect> rect;
|
||||
std::vector<std::vector<unsigned char>> data;
|
||||
} pictures = {};
|
||||
|
||||
static int decode_pic(struct rect const &r, std::vector<unsigned char> &data, unsigned char *out, size_t const xstride, size_t const ystride) {
|
||||
if (data.size() != r.w * r.h) {
|
||||
return 1;
|
||||
}
|
||||
for (size_t y = 0; y < r.h; y++) {
|
||||
for (size_t x = 0; x < r.w; x++) {
|
||||
out[y * ystride + x * xstride] = data[y * r.w + x];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLuint util_compileShader(char *name, GLsizei size, GLchar const **strings, GLint *lengths, GLenum shaderType) {
|
||||
GLuint shader = glCreateShader(shaderType);
|
||||
if (shader == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
glShaderSource(shader, size, strings, lengths);
|
||||
|
||||
glCompileShader(shader);
|
||||
|
||||
int success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (success == false) {
|
||||
char message[512];
|
||||
glGetShaderInfoLog(shader, sizeof (message), NULL, message);
|
||||
fprintf(stderr, "error: %s: %s\n", name, message);
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
GLuint util_linkProgram(char *name, GLuint Vshader, GLuint Fshader) {
|
||||
GLuint program = glCreateProgram();
|
||||
if (program == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
glAttachShader(program, Vshader);
|
||||
glAttachShader(program, Fshader);
|
||||
|
||||
glLinkProgram(program);
|
||||
|
||||
int success;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &success);
|
||||
if (success == false) {
|
||||
char message[512];
|
||||
glGetProgramInfoLog(program, sizeof (message), NULL, message);
|
||||
fprintf(stderr, "error: %s: %s\n", name, message);
|
||||
glDeleteProgram(program);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
int assets_load(FILE *file, char const *name) {
|
||||
enum ass_status ret = assread(file, [](char const *filename_, size_t length, void const *data_, size_t size, void *ctx) -> int {
|
||||
(void) ctx;
|
||||
std::string filename = std::string(filename_, length);
|
||||
auto pos = filename.find_last_of('.');
|
||||
std::string const name = filename.substr(0, pos);
|
||||
std::string const extension = pos == filename.npos? "": filename.substr(pos);
|
||||
unsigned char const *data = (unsigned char const *) data_;
|
||||
if (extension == ".shader") {
|
||||
char const *vsrc = (char const *) data;
|
||||
size_t len = strnlen(vsrc, size);
|
||||
if (len == size) {
|
||||
return 1;
|
||||
}
|
||||
char const *fsrc = vsrc + len + 1;
|
||||
len = size - len - 1;
|
||||
if (strnlen(fsrc, len) != len - 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
GLuint vshader = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vshader, 1, &vsrc, NULL);
|
||||
glCompileShader(vshader);
|
||||
GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(fshader, 1, &fsrc, NULL);
|
||||
glCompileShader(fshader);
|
||||
|
||||
int vsuccess, fsuccess;
|
||||
glGetShaderiv(vshader, GL_COMPILE_STATUS, &vsuccess);
|
||||
glGetShaderiv(fshader, GL_COMPILE_STATUS, &fsuccess);
|
||||
if (vsuccess == false || fsuccess == false) {
|
||||
char message[1024];
|
||||
if (vsuccess == false) {
|
||||
glGetShaderInfoLog(vshader, sizeof (message), NULL, message);
|
||||
fprintf(stderr, "error: %s.vert: %s\n", name.c_str(), message);
|
||||
}
|
||||
if (fsuccess == false) {
|
||||
glGetShaderInfoLog(fshader, sizeof (message), NULL, message);
|
||||
fprintf(stderr, "error: %s.frag: %s\n", name.c_str(), message);
|
||||
}
|
||||
} else {
|
||||
GLuint program = glCreateProgram();
|
||||
glAttachShader(program, vshader);
|
||||
glAttachShader(program, fshader);
|
||||
glLinkProgram(program);
|
||||
|
||||
int success;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &success);
|
||||
if (success == false) {
|
||||
char message[1024];
|
||||
glGetProgramInfoLog(program, sizeof (message), NULL, message);
|
||||
fprintf(stderr, "error: %s: %s\n", name.c_str(), message);
|
||||
glDeleteProgram(program);
|
||||
} else {
|
||||
programs[name] = program;
|
||||
}
|
||||
}
|
||||
|
||||
glDeleteShader(vshader);
|
||||
glDeleteShader(fshader);
|
||||
return vsuccess == false || fsuccess == false;
|
||||
} else if (extension == ".picture") {
|
||||
if (memcmp(data, "\xdd\xddpic\n\0\0", 8)) {
|
||||
return 1;
|
||||
}
|
||||
data += 8; size -= 8;
|
||||
size_t value;
|
||||
struct rect r = {};
|
||||
int result = read_vlq(data, size, value);
|
||||
r.w = value + 1;
|
||||
if (result < 0 || r.w - 1 != value) {
|
||||
return 1;
|
||||
}
|
||||
data += result; size -= result;
|
||||
result = read_vlq(data, size, value);
|
||||
r.h = value + 1;
|
||||
if (result < 0 || r.h - 1 != value) {
|
||||
return 1;
|
||||
}
|
||||
data += result; size -= result;
|
||||
std::vector<unsigned char> v(data, data + size);
|
||||
pictures.filename.push_back(name);
|
||||
pictures.rect.push_back(r);
|
||||
pictures.data.push_back(v);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}, NULL);
|
||||
|
||||
if (ret != ASS_OK) {
|
||||
switch (ret) {
|
||||
case ASS_OK: break;
|
||||
case ASS_BAD: fprintf(stderr, "error: %s: malformed asset pack\n", name); break;
|
||||
case ASS_USER: fprintf(stderr, "error: %s: malformed asset\n", name); break;
|
||||
case ASS_TRUNC: fprintf(stderr, "error: %s: truncated asset pack\n", name); break;
|
||||
case ASS_LAZY: fprintf(stderr, "error: %s: this asset pack will not load, sorry\n", name); break;
|
||||
case ASS_MEM: fprintf(stderr, "error: %s: memory exhausted while trying to load asset pack\n", name); break;
|
||||
case ASS_UNSUPPORTED: fprintf(stderr, "error: %s: unsupported asset pack version\n", name); break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct pack_result res = guilotine(pictures.rect.data(), pictures.rect.size(), 512, 1);
|
||||
if (res.bins == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t const stride = res.last_bin_size;
|
||||
unsigned char *tex = new unsigned char [stride * stride];
|
||||
memset(tex, 69, stride * stride);
|
||||
for (size_t i = 0; i < pictures.rect.size(); i++) {
|
||||
struct rect const &r = pictures.rect[i];
|
||||
//fprintf(stderr, "%s: %ux%u +%u+%u @ %u %c\n", pictures.filename[i].c_str(), r.w, r.h, r.x, r.y, r.bin, r.flipped? '+': '-');
|
||||
|
||||
int ret = decode_pic(r, pictures.data[i], tex + r.y * stride + r.x, r.flipped? stride: 1, r.flipped? 1: stride);
|
||||
if (ret) {
|
||||
delete[] tex;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
//fprintf(stderr, "info: %u %u %u %u\n", res.bins, res.last_bin_size, res.last_bin_width, res.last_bin_height);
|
||||
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, stride, stride, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, tex);
|
||||
delete[] tex;
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
#if 1
|
||||
unsigned tsize = 65536 / stride;
|
||||
#else
|
||||
unsigned tsize = 0;
|
||||
for (unsigned s = stride; s & 0xffff; s <<= 1, tsize++)
|
||||
;
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < pictures.rect.size(); i++) {
|
||||
struct rect const &r = pictures.rect[i];
|
||||
struct texture t = {.texture = texture, .x = r.x, .y = r.y, .size = tsize, .flipped = r.flipped};
|
||||
textures[pictures.filename[i]] = t;
|
||||
}
|
||||
|
||||
pictures.rect.clear();
|
||||
pictures.rect.shrink_to_fit();
|
||||
pictures.data.clear();
|
||||
pictures.data.shrink_to_fit();
|
||||
pictures.filename.clear();
|
||||
pictures.filename.shrink_to_fit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
5
src/assets.h++
Normal file
5
src/assets.h++
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
int assets_load(FILE *file, char const *name);
|
||||
71
src/main.c++
71
src/main.c++
@ -9,39 +9,22 @@
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
|
||||
#include "ass.h"
|
||||
#include "batch.h"
|
||||
#include "assets.h++"
|
||||
#include "main.h++"
|
||||
|
||||
#define WINDOW_WIDTH 128
|
||||
#define WINDOW_HEIGHT 128
|
||||
|
||||
#if 0
|
||||
// something something string_view faster but actually cryptic error messages so w/e future me problem
|
||||
#include <string_view>
|
||||
struct string_hash {
|
||||
using is_transparent = void;
|
||||
[[nodiscard]] size_t operator()(const char *txt) const {
|
||||
return std::hash<std::string_view>{}(txt);
|
||||
}
|
||||
[[nodiscard]] size_t operator()(std::string_view txt) const {
|
||||
return std::hash<std::string_view>{}(txt);
|
||||
}
|
||||
[[nodiscard]] size_t operator()(const std::string &txt) const {
|
||||
return std::hash<std::string>{}(txt);
|
||||
}
|
||||
};
|
||||
std::unordered_map<std::string, unsigned, string_hash, std::equal_to<>> programs, textures;
|
||||
#endif
|
||||
#define BATCH_SIZE 1024
|
||||
|
||||
struct state {
|
||||
SDL_Window *window;
|
||||
SDL_GLContext context;
|
||||
float color;
|
||||
std::unordered_map<std::string, unsigned> programs, textures;
|
||||
};
|
||||
std::unordered_map<std::string, unsigned> programs;
|
||||
std::unordered_map<std::string, struct texture> textures;
|
||||
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
SDL_AppResult SDL_AppInit(void **, int argc, char **argv) {
|
||||
clock_t begin = clock();
|
||||
auto state = new (struct state);
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
@ -50,10 +33,10 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, "60");
|
||||
|
||||
state->window = SDL_CreateWindow("suwi", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL /*| SDL_WINDOW_RESIZABLE*/ | SDL_WINDOW_TRANSPARENT);
|
||||
SDL_SetWindowMinimumSize(state->window, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
window = SDL_CreateWindow("suwi", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL /*| SDL_WINDOW_RESIZABLE*/ | SDL_WINDOW_TRANSPARENT);
|
||||
SDL_SetWindowMinimumSize(window, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
|
||||
state->context = SDL_GL_CreateContext(state->window);
|
||||
context = SDL_GL_CreateContext(window);
|
||||
|
||||
int version = gladLoadGLES2((GLADloadfunc) SDL_GL_GetProcAddress);
|
||||
if (version == 0) {
|
||||
@ -75,35 +58,28 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
perror(path.c_str());
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
if (assets_load(file, path.c_str())) {
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
} else {
|
||||
file = fopen(argv[1], "rb");
|
||||
if (file == NULL) {
|
||||
perror(argv[1]);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
}
|
||||
if (assread(file, [](char const *name, size_t length, void const *data, size_t size, /* struct state *state */ void *ctx) -> int {
|
||||
auto &state = *(struct state *) ctx;
|
||||
std::string filename = std::string(name, length);
|
||||
(void) name;
|
||||
(void) length;
|
||||
(void) data;
|
||||
(void) size;
|
||||
if (filename.ends_with(".glsl")) {
|
||||
state.programs[filename];
|
||||
}
|
||||
state.textures[filename];
|
||||
return 0;
|
||||
}, state)) {
|
||||
if (assets_load(file, argv[1])) {
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
batch_init(BATCH_SIZE, programs.at("spr2D"));
|
||||
|
||||
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
SDL_ShowWindow(state->window);
|
||||
SDL_ShowWindow(window);
|
||||
|
||||
fprintf(stderr, "info: init took %lu ms\n", ((clock() - begin) * 1000) / CLOCKS_PER_SEC);
|
||||
|
||||
*appstate = state;
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
@ -146,12 +122,15 @@ SDL_AppResult SDL_AppEvent(void *, SDL_Event *evt) {
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppIterate(void *appstate) {
|
||||
auto &state = *(struct state *) appstate;
|
||||
SDL_AppResult SDL_AppIterate(void *) {
|
||||
glClearColor(0.5, 0.5, 0.5, 0.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
SDL_GL_SwapWindow(state.window);
|
||||
struct crop c = {0, 0, 64, 64};
|
||||
batch_blit(-32, -32, &textures.at("turret"), &c);
|
||||
batch_flush();
|
||||
|
||||
SDL_GL_SwapWindow(window);
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
32
src/main.h++
Normal file
32
src/main.h++
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <glad/gles2.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
#include "batch.h"
|
||||
|
||||
#if 0
|
||||
// something something string_view faster but actually cryptic error messages so w/e future me problem
|
||||
#include <string_view>
|
||||
struct string_hash {
|
||||
using is_transparent = void;
|
||||
[[nodiscard]] size_t operator()(const char *txt) const {
|
||||
return std::hash<std::string_view>{}(txt);
|
||||
}
|
||||
[[nodiscard]] size_t operator()(std::string_view txt) const {
|
||||
return std::hash<std::string_view>{}(txt);
|
||||
}
|
||||
[[nodiscard]] size_t operator()(const std::string &txt) const {
|
||||
return std::hash<std::string>{}(txt);
|
||||
}
|
||||
};
|
||||
std::unordered_map<std::string, unsigned, string_hash, std::equal_to<>> programs, textures;
|
||||
#endif
|
||||
|
||||
extern SDL_Window *window;
|
||||
extern SDL_GLContext context;
|
||||
extern std::unordered_map<std::string, unsigned> programs;
|
||||
extern std::unordered_map<std::string, struct texture> textures;
|
||||
@ -95,7 +95,7 @@ struct pack_result guilotine(struct rect *rects, size_t items, unsigned maxsize,
|
||||
}
|
||||
|
||||
static struct pack_result guilotine_(struct rect **left_base, size_t left_count, unsigned maxwidth, unsigned maxheight, unsigned bins, unsigned maxbins) {
|
||||
fprintf(stderr, "%zu %ux%u, %u\n", left_count, maxwidth, maxheight, maxbins - bins);
|
||||
//fprintf(stderr, "%zu %ux%u, %u\n", left_count, maxwidth, maxheight, maxbins - bins);
|
||||
struct rect **left = left_base;
|
||||
unsigned in_last_bin = 0;
|
||||
size_t slot_count = 1;
|
||||
@ -233,28 +233,8 @@ static struct pack_result guilotine_(struct rect **left_base, size_t left_count,
|
||||
//free(other_left);
|
||||
free(tighter);
|
||||
|
||||
#if 0
|
||||
fprintf(stdout, "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 %u %u\">", (maxsize + 16) * bins - 16, maxsize);
|
||||
for (unsigned i = 0; i < bins; i++) {
|
||||
unsigned binx = (maxsize + 16) * i, biny = 0;
|
||||
fprintf(stdout, "<view id=\"%u\" viewBox=\"%u %u %u %u\"/>", i, binx, biny, maxsize, maxsize);
|
||||
}
|
||||
fputs("<style>rect{stroke:black;}.bg{fill:gray;}.flipped{fill:green;}.normal{fill:blue}text{font-family:monospace;text-anchor:middle;alignment-baseline:central;}</style>", stdout);
|
||||
for (unsigned i = 0; i < items; i++) {
|
||||
unsigned w = rects[i].flipped? rects[i].h: rects[i].w;
|
||||
unsigned h = rects[i].flipped? rects[i].w: rects[i].h;
|
||||
unsigned binx = (maxsize + 16) * rects[i].bin, biny = 0;
|
||||
fprintf(stdout, "<rect x=\"%u.5\" y=\"%u.5\" width=\"%u\" height=\"%u\" class=\"%s\"/><text x=\"%u\" y=\"%u\" style=\"font-size: %upx\">%u</text>", rects[i].x + binx, rects[i].y + biny, w - 1, h - 1, rects[i].flipped? "flipped": "normal", rects[i].x + w / 2 + binx, rects[i].y + h / 2 + biny, h > 20? 20: h, i);
|
||||
}
|
||||
for (unsigned i = 0; i < slot_count; i++) {
|
||||
unsigned binx = (maxsize + 16) * slots[i].bin, biny = 0;
|
||||
fprintf(stdout, "<rect x=\"%u.5\" y=\"%u.5\" width=\"%u\" height=\"%u\" class=\"bg\"/>", slots[i].x + binx, slots[i].y + biny, slots[i].w - 1, slots[i].h - 1);
|
||||
}
|
||||
fputs("</svg>", stdout);
|
||||
#endif
|
||||
|
||||
free(left_base);
|
||||
free(slots);
|
||||
fprintf(stderr, "%u %u %u %u\n", bins, last_bin.last_bin_size, last_bin.last_bin_width, last_bin.last_bin_height);
|
||||
//fprintf(stderr, "%u %u %u %u\n", bins, last_bin.last_bin_size, last_bin.last_bin_width, last_bin.last_bin_height);
|
||||
return (struct pack_result) {bins, last_bin.last_bin_size, last_bin.last_bin_width, last_bin.last_bin_height};
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ attribute vec2 aTexCoord;
|
||||
|
||||
void main(void) {
|
||||
vTexCoord = aTexCoord;
|
||||
gl_Position = vec4(aVertCoord * vec2(1.0 / 256.0), 0.0, 1.0);
|
||||
gl_Position = vec4(aVertCoord * vec2(1.0 / 64.0), 0.0, 1.0);
|
||||
}
|
||||
|
||||
##
|
||||
@ -23,8 +23,9 @@ void main(void) {
|
||||
uniform sampler2D uTex;
|
||||
|
||||
void main(void) {
|
||||
if (texture2D(uTex, vTexCoord).r < 0.5) {
|
||||
float pixel = texture2D(uTex, vTexCoord).r;
|
||||
if (pixel < 0.5) {
|
||||
discard;
|
||||
}
|
||||
gl_FragColor = vec4(1.0);
|
||||
gl_FragColor = step(vec4(0.75, 0.75, 0.75, 0.5), vec4(pixel));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user