diff options
| author | 2020-10-01 04:28:13 -0400 | |
|---|---|---|
| committer | 2020-10-01 04:28:13 -0400 | |
| commit | 4c200513158ad0b974cda961deffe6192830ecae (patch) | |
| tree | 110c4fde4b0d47d0a273de7f3cbe98528e8e7e9e | |
| parent | f114f3ad193313676be1de0d9388b9062a8559ab (diff) | |
| download | mauri-4c200513158ad0b974cda961deffe6192830ecae.tar.gz mauri-4c200513158ad0b974cda961deffe6192830ecae.tar.bz2 mauri-4c200513158ad0b974cda961deffe6192830ecae.zip | |
switch to c++, add dxt5 and dxt1 handling
64 files changed, 4798 insertions, 3761 deletions
diff --git a/.clang-format b/.clang-format new file mode 100755 index 0000000..1190ef5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,4 @@ +BasedOnStyle: Microsoft +IndentWidth: 4 +ColumnLimit: 0 + diff --git a/.gitmodules b/.gitmodules index 26f663b..dbdbfee 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,6 @@ -[submodule "subprojects/cglm"] - path = subprojects/cglm - url = https://github.com/recp/cglm.git [submodule "subprojects/glfw"] path = subprojects/glfw url = https://github.com/glfw/glfw.git -[submodule "subprojects/jansson"] - path = subprojects/jansson - url = https://github.com/akheron/jansson.git -[submodule "subprojects/moro"] - path = subprojects/moro - url = git@github.com:Pizzabelly/moro.git +[submodule "subprojects/fmt"] + path = subprojects/fmt + url = https://github.com/fmtlib/fmt.git @@ -1,2 +1,3 @@ if version 2 is defined as a combo, g_Alpha and g_Color need to be set from the object visible should be a draw time check not handled when parsing +particles diff --git a/meson.build b/meson.build index e21a94b..76b54e7 100644 --- a/meson.build +++ b/meson.build @@ -1,45 +1,62 @@ -project('mauri', 'c', +project('mauri', 'cpp', version : '0.1', - default_options : ['warning_level=2', 'c_std=c99']) - -add_project_arguments('-D_GNU_SOURCE', language : 'c') + default_options : ['warning_level=1', 'cpp_std=gnu++2a']) if get_option('buildtype').startswith('debug') - add_project_arguments('-D_DEBUG_', language : 'c') + add_project_arguments('-D_DEBUG_', language : 'cpp') endif -add_project_arguments('-Wno-sign-compare', language : 'c') -add_project_arguments('-Wno-unused-but-set-variable', language : 'c') -add_project_arguments('-Wno-unused-parameter', language : 'c') +compiler = meson.get_compiler('cpp') + +cmake = import('cmake') + +add_project_arguments('-Wno-sign-compare', language : 'cpp') +add_project_arguments('-Wno-unused-parameter', language : 'cpp') +add_project_arguments('-Wno-missing-field-initializers', language : 'cpp') +add_project_arguments('-Wno-non-virtual-dtor', language : 'cpp') -moro = subproject('moro').get_variable('moro') stbi = subproject('stb_image').get_variable('stbi') glad = subproject('glad').get_variable('glad') +s3tc = subproject('s3tc-dxt-decompression').get_variable('s3tc') + +fmt = cmake.subproject('fmt').dependency('fmt') + +dl = compiler.find_library('dl') -jansson = dependency('jansson') lz4 = dependency('liblz4') -cglm = dependency('cglm') +glm = dependency('glm') glfw = dependency('glfw3') +jsoncpp = dependency('jsoncpp') + +X11 = dependency('X11') +Xrender = dependency('xrender') +Xext = dependency('xext') +GLX = dependency('glx') -deps = [jansson, cglm, lz4, moro, stbi, glad, glfw] +deps = [dl, jsoncpp, glm, lz4, stbi, glad, glfw, fmt, s3tc, X11, Xrender, Xext, GLX] src = [ - 'src/mauri.c', - 'src/assets.c', - 'src/util.c', - 'src/json.c', - 'src/gl.c', - 'src/texture.c', - 'src/shader.c', - 'src/context.c', - 'src/context_glfw.c', - 'src/engine.c', - 'src/objects/scene.c', - 'src/objects/object.c', - 'src/objects/model.c', - 'src/objects/material.c', - 'src/objects/pass.c', - 'src/objects/effect.c' + 'src/assets.cc', + 'src/mauri.cc', + 'src/util.cc', + 'src/gl.cc', + 'src/shader.cc', + 'src/engine.cc', + 'src/texture.cc', + 'src/context.cc', + 'src/json.cc', + 'src/context_glfw.cc', + 'src/context_x11.cc', + 'src/objects/scene.cc', + 'src/objects/object.cc', + 'src/objects/model.cc', + 'src/objects/material.cc', + 'src/objects/pass.cc', + 'src/objects/effect.cc' ] -executable('mauri', src, dependencies: deps, install: true) +executable('mauri', src, + dependencies: deps, + include_directories: include_directories('src'), + install: true +) diff --git a/src/assets.c b/src/assets.c deleted file mode 100644 index 1e18eb9..0000000 --- a/src/assets.c +++ /dev/null @@ -1,181 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> - -#include "log.h" -#include "assets.h" -#include "moro_str.h" -#include "moro_vec.h" -#include "util.h" - -mri_asset_manager asset_manager; - -static mri_asset asset_void = { - .type = ASSET_VOID, - .size = 0, - .pos = 0, - .data = NULL, - .hash = 0 -}; - -static mri_asset asset_from_slice(mstr *path, unsigned char* begin, unsigned char *end) { - return (mri_asset) { - .type = ASSET_SLICE, - .size = end - begin, - .pos = 0, - .data = begin, - .hash = mstr_hash(path) - }; -} - -static void asset_from_path(mstr *path, mri_asset *asset) { - char *path_c = mstr_to_cstr(path); - - FILE *asset_file = fopen(path_c, "rb"); - if (asset_file == NULL) { - *asset = asset_void; - free(path_c); - return; - } - - free(path_c); - - asset->type = ASSET_FILE; - asset->pos = 0; - asset->hash = mstr_hash(path); - - fseek(asset_file, 0, SEEK_END); - - asset->size = ftell(asset_file); - asset->data = (unsigned char *)malloc(asset->size); - - rewind(asset_file); - - size_t res = fread(asset->data, 1, asset->size, asset_file); - - fclose(asset_file); - - if (res != asset->size) { - log_error("failed to read file (%.*s)", MSTR_PRINTF(path)); - free(asset->data); - return; - } -} - -static void mri_asset_manager_init() { - memset(&asset_manager.pkg, 0, sizeof(mri_asset)); - mvec_init(asset_manager.files); -} - -bool mri_asset_manager_load_pkg(mstr *path) { - mri_asset_manager_init(); - - asset_from_path(path, &asset_manager.pkg); - - if (asset_manager.pkg.type == ASSET_VOID) { - log_error("failed to load pkg file: (%.*s)", MSTR_PRINTF(path)); - return false; - } - - mstr signature = mri_asset_str(&asset_manager.pkg, - mri_asset_value(&asset_manager.pkg, int)); - - if (!mstr_eqn(&signature, mstr_c("PKGV"), 4)) { - log_error("invalid pkg file: (%.*s)", MSTR_PRINTF(path)); - return false; - } - - int count = mri_asset_value(&asset_manager.pkg, int); - - mri_pkg_file *pkg_files = (mri_pkg_file *)malloc(sizeof(mri_pkg_file) * count); - - for (int i = 0; i < count; i++) { - pkg_files[i] = (mri_pkg_file) { - .path = mri_asset_str(&asset_manager.pkg, mri_asset_value(&asset_manager.pkg, int)), - .length = mri_asset_value(&asset_manager.pkg, unsigned int), - .offset = mri_asset_value(&asset_manager.pkg, unsigned int) - }; - } - - for (int i = 0; i < count; i++) { - mri_pkg_file *file = &pkg_files[i]; - - mri_asset asset = asset_from_slice(&file->path, - mri_asset_ptr(&asset_manager.pkg) + file->offset, - mri_asset_ptr(&asset_manager.pkg) + file->offset + file->length); - - log_debug("loaded file (%.*s)", MSTR_PRINTF(&file->path)); - - mvec_push(asset_manager.files, asset); - } - - log_info("successfully loaded pkg file (%.*s)", MSTR_PRINTF(path)); - - free(pkg_files); - - return true; -} - -mri_asset *mri_asset_manager_get(mstr *part, mri_file_type_hint hint) { - mstr *path = mstr_clone(part); - - switch (hint) { - case TEXTURE: - mstr_prepend(path, mstr_c("materials/")); - mstr_cat(path, mstr_c(".tex")); - break; - case SHADER: - mstr_prepend(path, mstr_c("shaders/")); - break; - case FRAGMENT_SHADER: - mstr_prepend(path, mstr_c("shaders/")); - mstr_cat(path, mstr_c(".frag")); - break; - case VERTEX_SHADER: - mstr_prepend(path, mstr_c("shaders/")); - mstr_cat(path, mstr_c(".vert")); - break; - case NONE: - break; - } - - unsigned long path_hash = mstr_hash(path); - - // true if this asset has already been loaded - bool asset_loaded = false; - - mri_asset *asset = NULL; - mvec_loop_ptr(asset_manager.files, asset, i) { - if (asset->hash == path_hash) { - asset_loaded = true; - break; - } - } - - if (!asset_loaded) { - mstr_prepend(path, mstr_c("assets/")); - asset = mvec_next(asset_manager.files); - asset_from_path(path, asset); - if (asset->type == ASSET_VOID) { - asset = &mvec_pop(asset_manager.files); - } - } - - mstr_free(path); - - return asset; -} - -void mri_asset_unload(mri_asset *asset) { - if (asset->type == ASSET_FILE) { - free(asset->data); - } -} - -void mri_asset_manager_unload() { - mri_asset *asset; - mvec_loop_ptr(asset_manager.files, asset, i) { - mri_asset_unload(asset); - } - mvec_free(asset_manager.files); - mri_asset_unload(&asset_manager.pkg); -} diff --git a/src/assets.cc b/src/assets.cc new file mode 100644 index 0000000..6b34c03 --- /dev/null +++ b/src/assets.cc @@ -0,0 +1,206 @@ +#include "assets.h" +#include "log.h" +#include "util.h" + +#include <array> +#include <fstream> +#include <iostream> + +using namespace Mauri; + +Asset::Asset() +{ + this->type = ASSET_VOID; + this->size = 0; + this->pos = 0; + this->data = nullptr; + this->hash = 0; +} + +static Asset asset_void = Asset(); + +Asset::Asset(std::string path, byte *begin, byte *end) +{ + this->type = ASSET_SLICE; + this->size = end - begin; + this->pos = 0; + this->data = begin; + this->hash = std::hash<std::string>{}(path); +} + +Asset::Asset(std::string path) +{ + //FILE *file = fopen(path.c_str(), "rb"); + + //if (file == NULL) + // return; + + //this->type = ASSET_FILE; + //this->pos = 0; + //this->hash = std::hash<std::string>{}(path); + + //fseek(file, 0, SEEK_END); + + //this->size = ftell(file); + //this->data = (unsigned char *)malloc(this->size); + + //rewind(file); + + //size_t res = fread(this->data, 1, this->size, file); + + //fclose(file); + + //if (res != this->size) { + // log_error("failed to read file (%s)", path.c_str()); + // free(this->data); + // return; + //} + + std::ifstream file(path, std::ios::binary | std::ios::ate); + + if (file.bad() || file.fail()) + { + this->type = ASSET_VOID; + file.close(); + return; + } + + this->size = file.tellg(); + this->data = new byte[this->size]; + + file.seekg(0, std::ios::beg); + if (!file.read(reinterpret_cast<char *>(this->data), this->size)) + { + this->type = ASSET_VOID; + file.close(); + return; + } + + file.close(); + + this->pos = 0; + this->type = ASSET_FILE; + + this->hash = std::hash<std::string>{}(path); +} + +Asset::~Asset() +{ + if (this->type == ASSET_FILE) + { + delete[] this->data; + } +} + +bool AssetManager::load_package(std::string path) +{ + this->pkg = new Asset(path); + + if (this->pkg->type == ASSET_VOID) + { + log_error("failed to load pkg file: (%s)", path.c_str()); + return false; + }; + + auto signature = this->pkg->reads(this->pkg->read<u32>()); + + if (signature.compare(0, 4, "PKGV") != 0) + { + log_error("invalid pkg file (%s)", path.c_str()); + return false; + } + + auto count = this->pkg->read<u32>(); + + std::vector<PkgFile> pkg_files; + pkg_files.reserve(count); + + for (auto i = 0; i < count; i++) + { + pkg_files.emplace_back((PkgFile) { + this->pkg->reads(this->pkg->read<u32>()), + this->pkg->read<u32>(), + this->pkg->read<u32>() + }); + } + + for (auto file : pkg_files) + { + this->files.push_back( + new Asset(std::string(file.path), + this->pkg->ptr() + file.offset, + this->pkg->ptr() + file.offset + file.length)); + + log_debug("loaded file (%s)", std::string(file.path).c_str()); + } + + return true; +} + +Asset *AssetManager::get_file(std::string path, FileTypeHint hint) +{ + switch (hint) + { + case FileTypeHint::TEXTURE: + path.insert(0, "materials/"); + path.append(".tex"); + break; + case FileTypeHint::SHADER: + path.insert(0, "shaders/"); + break; + case FileTypeHint::FRAGMENT_SHADER: + path.insert(0, "shaders/"); + path.append(".frag"); + break; + case FileTypeHint::VERTEX_SHADER: + path.insert(0, "shaders/"); + path.append(".vert"); + break; + case NONE: + default: + break; + } + + u64 hash = std::hash<std::string>{}(path); + + for (auto &file : this->files) + { + if (file->hash == hash) + { + return file; + } + } + + path.insert(0, "assets/"); + + Asset *asset = new Asset(path); + + if (asset->type == ASSET_VOID) + { + delete asset; + return &asset_void; + } + + this->files.push_back(std::move(asset)); + + return this->files.back(); +} + +AssetManager::~AssetManager() +{ + for (auto &file : this->files) + { + delete file; + } + delete this->pkg; +} + +namespace Mauri +{ +static AssetManager manager; + +AssetManager *asset_manager() +{ + return &manager; +} +} // namespace Mauri diff --git a/src/assets.h b/src/assets.h index 5fbc995..57fde5a 100644 --- a/src/assets.h +++ b/src/assets.h @@ -1,76 +1,115 @@ #ifndef _ASSET_H #define _ASSET_H -#include <stdbool.h> -#include <moro.h> +#include <cstring> +#include <iostream> +#include <sstream> +#include <vector> +#include <jsoncpp/json/json.h> #include "util.h" -typedef enum { - ASSET_FILE, - ASSET_SLICE, - ASSET_VOID -} mri_asset_type; +namespace Mauri +{ +enum AssetType +{ + ASSET_FILE, + ASSET_SLICE, + ASSET_VOID +}; -typedef enum { - TEXTURE, - SHADER, - FRAGMENT_SHADER, - VERTEX_SHADER, - NONE -} mri_file_type_hint; +enum FileTypeHint +{ + TEXTURE, + SHADER, + FRAGMENT_SHADER, + VERTEX_SHADER, + NONE +}; -typedef struct { - // If this is ASSET_FILE, the data should be - // free'd along with this asset. - mri_asset_type type; +struct Asset +{ + Asset(); + Asset(std::string path, byte *begin, byte *end); + Asset(std::string path); - unsigned int size; - unsigned int pos; - unsigned char *data; + ~Asset(); - unsigned long hash; -} mri_asset; + // If this is ASSET_FILE, the data should be + // free'd along with this asset. + AssetType type; -typedef struct { - mstr path; - unsigned int offset; - unsigned int length; -} mri_pkg_file; + u32 size; + u32 pos; + byte *data; -typedef struct { - // scene.pkg of the current wallpaper - mri_asset pkg; + u64 hash; - mvec(mri_asset) files; -} mri_asset_manager; + std::string_view str; -// not thread-safe -extern mri_asset_manager asset_manager; + void seek(s32 n) + { + this->pos += n; + } -#define mri_asset_ptr(asset) &((asset)->data[(asset)->pos]) -#define mri_asset_seek(asset, n) (asset)->pos += n + byte *ptr() const + { + return &this->data[this->pos]; + } -#define mri_asset_value(asset, type) ({ \ - type val = *((type *)(mri_asset_ptr(asset))); \ - mri_asset_seek((asset), sizeof(type)); \ - val; \ -}) + template <typename T> + T read() + { + T val = *((T *)this->ptr()); + this->seek(sizeof(T)); + return val; + } -#define mri_asset_str(a, n) ({ \ - size_t _n = n; \ - mstr *s = mstr_w(mri_asset_ptr(a), _n); \ - mri_asset_seek(a, _n); \ - (*s); \ -}) + std::string_view reads(u32 n) + { + std::string_view s((char *)this->ptr(), n); + this->seek(n); + return s; + } + + std::string as_string() + { + return std::string((char *)this->ptr(), this->size); + } -#define mri_asset_as_str(a) mstr_w(mri_asset_ptr(a), (a)->size) + Json::Value as_json() + { + Json::Value root; + std::stringstream(std::string((char *)this->ptr(), this->size)) >> root; + return root; + } +}; -bool mri_asset_manager_load_pkg(mstr *path); +struct PkgFile +{ + std::string_view path; + u32 offset; + u32 length; +}; -void mri_asset_unload(mri_asset *asset); -void mri_asset_manager_unload(); +class AssetManager +{ + public: + AssetManager() {}; + ~AssetManager(); -mri_asset *mri_asset_manager_get(mstr *path, mri_file_type_hint hint); + bool load_package(std::string path); + + Asset *get_file(std::string path, FileTypeHint hint); + + private: + Asset *pkg; + + std::vector<Asset *> files; +}; + +extern AssetManager *asset_manager(); + +} // namespace Mauri #endif // _ASSET_H diff --git a/src/context.c b/src/context.c deleted file mode 100644 index 292061d..0000000 --- a/src/context.c +++ /dev/null @@ -1,27 +0,0 @@ -#include <glad/gl.h> - -#include "log.h" -#include "context.h" - -void print_gl_info() { - log_info("OpenGL loaded"); - log_info("Vendor: %s", glGetString(GL_VENDOR)); - log_info("Renderer: %s", glGetString(GL_RENDERER)); - log_info("Version: %s", glGetString(GL_VERSION)); -} - -GLenum gl_check_error() { - GLenum error_code; - while ((error_code = glGetError()) != GL_NO_ERROR) { - const char *error; - switch (error_code) { - case GL_INVALID_ENUM: error = "INVALID_ENUM"; break; - case GL_INVALID_VALUE: error = "INVALID_VALUE"; break; - case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break; - case GL_OUT_OF_MEMORY: error = "OUT_OF_MEMORY"; break; - case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break; - } - log_debug("%s", error); - } - return error_code; -} diff --git a/src/context.cc b/src/context.cc new file mode 100644 index 0000000..6de03a9 --- /dev/null +++ b/src/context.cc @@ -0,0 +1,41 @@ +#include "context.h" +#include "log.h" + +using namespace Mauri; + +void Context::print_gl_info() +{ + log_info("OpenGL loaded"); + log_info("Vendor: %s", glGetString(GL_VENDOR)); + log_info("Renderer: %s", glGetString(GL_RENDERER)); + log_info("Version: %s", glGetString(GL_VERSION)); +} + +GLenum Context::check_error() +{ + GLenum error_code; + while ((error_code = glGetError()) != GL_NO_ERROR) + { + std::string error; + switch (error_code) + { + case GL_INVALID_ENUM: + error = "INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + error = "INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + error = "INVALID_OPERATION"; + break; + case GL_OUT_OF_MEMORY: + error = "OUT_OF_MEMORY"; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + error = "INVALID_FRAMEBUFFER_OPERATION"; + break; + } + log_error("%s", error.c_str()); + } + return error_code; +} diff --git a/src/context.h b/src/context.h index a6e4dc0..a4f2342 100644 --- a/src/context.h +++ b/src/context.h @@ -1,32 +1,41 @@ #ifndef _CONTEXT_H #define _CONTEXT_H -#include <stdbool.h> #include <glad/gl.h> +#include <iostream> #define GL_VERSION_MAJOR 3 #define GL_VERSION_MINOR 3 -typedef struct mri_context mri_context; +namespace Mauri +{ -struct mri_context { - float width, height; +class Context +{ + public: + Context(int width, int height, std::string name, bool wallpaper) {} - bool (*create_window)(mri_context *, float, float, const char *, bool); - void (*resize_window)(mri_context *, float, float); + virtual ~Context() {} - void (*close_window)(mri_context *); - bool (*should_close)(mri_context *); + int width, height; - void (*swap_buffers)(mri_context *); + virtual void resize_window(int width, int height) = 0; - void (*process_input)(mri_context *); - void (*cursor_pos)(mri_context *, float *, float *); + virtual void close_window() = 0; + virtual bool should_close() const = 0; - double (*current_time)(mri_context *); + virtual void swap_buffers() = 0; + + virtual void process_input() = 0; + virtual void cursor_pos(float *x, float *y) = 0; + + virtual double current_time() = 0; + + protected: + GLenum check_error(); + void print_gl_info(); }; -void print_gl_info(); -GLenum gl_check_error(); +} // namespace Mauri #endif // _CONTEXT_H diff --git a/src/context_glfw.c b/src/context_glfw.c deleted file mode 100644 index d40b595..0000000 --- a/src/context_glfw.c +++ /dev/null @@ -1,105 +0,0 @@ -#include <stdlib.h> -#include <glad/gl.h> - -#include "log.h" -#include "context_glfw.h" -#include "context.h" - -static bool create_window(mri_context *c, float width, float height, const char *name, bool wallpaper) { - if (!glfwInit()) { - log_error("%s", "failed to init GLFW"); - return false; - } - - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GL_VERSION_MAJOR); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GL_VERSION_MINOR); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - - glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE); - - glfwWindowHint(GLFW_SAMPLES, 2); - - c->width = width; - c->height = height; - - GLFWwindow *window = glfwCreateWindow(width, height, name, NULL, NULL); - - ((mri_glfw_context *)c)->window = window; - - glfwMakeContextCurrent(window); - glfwSwapInterval(1); - - if (!gladLoadGL((GLADloadfunc)glfwGetProcAddress)) { - log_info("%s", "couldn't load opengl"); - return false; - } - - glEnable(GL_BLEND); - glEnable(GL_MULTISAMPLE); - - glDisable(GL_CULL_FACE); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - print_gl_info(); - - return true; -} - -static void close_window(mri_context *c) { - glfwSetWindowShouldClose(((mri_glfw_context *)c)->window, GLFW_TRUE); -} - -static void resize_window(mri_context *c, float width, float height) { - c->width = width; - c->height = height; - glfwSetWindowSize(((mri_glfw_context *)c)->window, width, height); -} - -static bool should_close(mri_context *c) { - return glfwWindowShouldClose(((mri_glfw_context *)c)->window); -} - -static void cursor_pos(mri_context *c, float *x, float *y) { - double _x, _y; - glfwGetCursorPos(((mri_glfw_context *)c)->window, &_x, &_y); - *x = (float)_x; - *y = (float)_y; -} - -static void swap_buffers(mri_context *c) { - glfwSwapBuffers(((mri_glfw_context *)c)->window); -} - -static void process_input(mri_context *c) { - glfwPollEvents(); -} - -static double current_time(mri_context *c) { - return glfwGetTime(); -} - -mri_context *mri_glfw_context_new() { - mri_glfw_context *c = (mri_glfw_context *)malloc(sizeof(mri_glfw_context)); - mri_glfw_context _c = { - ._c = { - .create_window = &create_window, - .resize_window = &resize_window, - .close_window = &close_window, - .should_close = &should_close, - .swap_buffers = &swap_buffers, - .process_input = &process_input, - .cursor_pos = &cursor_pos, - .current_time = ¤t_time - }, - .window = NULL - }; - memcpy(c, &_c, sizeof(mri_glfw_context)); - return (mri_context *)(c); -} - -void mri_glfw_context_unload(mri_context *c) { - glfwDestroyWindow(((mri_glfw_context *)c)->window); - glfwTerminate(); - free((mri_glfw_context *)c); -} diff --git a/src/context_glfw.cc b/src/context_glfw.cc new file mode 100644 index 0000000..2900f15 --- /dev/null +++ b/src/context_glfw.cc @@ -0,0 +1,90 @@ +#include "context_glfw.h" +#include "context.h" +#include "log.h" +#include "src/objects/pass.h" +#include <GLFW/glfw3.h> + +using namespace Mauri; + +void ContextGLFW::close_window() +{ + glfwSetWindowShouldClose(this->window, GLFW_TRUE); +} + +void ContextGLFW::resize_window(int width, int height) +{ + this->width = width; + this->height = height; + glfwSetWindowSize(this->window, width, height); +} + +bool ContextGLFW::should_close() const +{ + return glfwWindowShouldClose(this->window); +} + +void ContextGLFW::cursor_pos(f32 *x, f32 *y) +{ + f64 _x, _y; + glfwGetCursorPos(this->window, &_x, &_y); + *x = (f32)_x; + *y = (f32)_y; +} + +void ContextGLFW::swap_buffers() +{ + glfwSwapBuffers(this->window); +} + +void ContextGLFW::process_input() +{ + glfwPollEvents(); +} + +double ContextGLFW::current_time() +{ + return glfwGetTime(); +} + +ContextGLFW::ContextGLFW(int width, int height, std::string name, bool wallpaper) + : Context(width, height, name, wallpaper) +{ + this->width = width; + this->height = height; + + if (!glfwInit()) + { + log_print("err", "failed to init glfw"); + return; + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GL_VERSION_MAJOR); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GL_VERSION_MINOR); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE); + + glfwWindowHint(GLFW_SAMPLES, 4); + + this->window = glfwCreateWindow(width, height, name.c_str(), nullptr, nullptr); + + glfwMakeContextCurrent(window); + + if (!gladLoadGL((GLADloadfunc)glfwGetProcAddress)) + { + log_print("err", "could not load opengl"); + return; + } + + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glEnable(GL_MULTISAMPLE); + + print_gl_info(); +} + +ContextGLFW::~ContextGLFW() +{ + glfwDestroyWindow(this->window); + glfwTerminate(); +} diff --git a/src/context_glfw.h b/src/context_glfw.h index f68b284..30a60f2 100644 --- a/src/context_glfw.h +++ b/src/context_glfw.h @@ -1,17 +1,37 @@ -#ifndef _CONTEXT_GLFW_H -#define _CONTEXT_GLFW_H +#ifndef _CONTEXT_GLFW_HPP +#define _CONTEXT_GLFW_HPP #define GLFW_INCLUDE_NONE #include <GLFW/glfw3.h> #include "context.h" +#include "util.h" -typedef struct { - mri_context _c; - GLFWwindow *window; -} mri_glfw_context; +namespace Mauri +{ -mri_context *mri_glfw_context_new(); -void mri_glfw_context_unload(mri_context *c); +class ContextGLFW : public Context +{ + public: + ContextGLFW(int width, int height, std::string name, bool wallpaper); + ~ContextGLFW(); -#endif // _CONTEXT_GLFW_H + void close_window(); + void resize_window(int width, int height); + + bool should_close() const; + void process_input(); + + void cursor_pos(float *x, float *y); + + void swap_buffers(); + + double current_time(); + + private: + GLFWwindow *window; +}; + +} // namespace Mauri + +#endif // _CONTEXT_GLFW_HPP diff --git a/src/context_x11.cc b/src/context_x11.cc new file mode 100644 index 0000000..898aa86 --- /dev/null +++ b/src/context_x11.cc @@ -0,0 +1,290 @@ +#include "context_x11.h" + +#include "context.h" +#include "log.h" + +using namespace Mauri; + +static Atom ATOM_WM_PROTOCOLS, ATOM_WM_DELETE_WINDOW, ATOM__NET_ACTIVE_WINDOW; + +void ContextX11::disable_input() +{ + XWMHints wmHint; + + wmHint.flags = InputHint | StateHint; + wmHint.input = false; + wmHint.initial_state = NormalState; + + XSetWMProperties(x11_d, window, NULL, NULL, NULL, + 0, NULL, &wmHint, NULL); +} + +ContextX11::ContextX11(int width, int height, std::string name, bool wallpaper) + : Context(width, height, name, wallpaper) +{ + this->width = width; + this->height = height; + + x11_d = XOpenDisplay(NULL); + + ATOM_WM_DELETE_WINDOW = XInternAtom(x11_d, "WM_DELETE_WINDOW", true); + + if (!x11_d) + { + log_error("%s", "could not open display"); + return; + } + + int screen = XDefaultScreen(x11_d); + Window root = RootWindow(x11_d, screen); + + static int gl_attrs[] = { + GLX_X_RENDERABLE, True, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_DOUBLEBUFFER, True, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + None + }; + + GLint context_attrs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, GL_VERSION_MAJOR, + GLX_CONTEXT_MINOR_VERSION_ARB, GL_VERSION_MINOR, + None + }; + + // https://github.com/jarcode-foss/glava/blob/master/glava/glx_wcb.c#L383 + int fb_sz, best = -1, samp = -1; + + GLXFBConfig *fbc = glXChooseFBConfig(x11_d, screen, gl_attrs, &fb_sz); + if (!fbc) + return; + + for (int t = 0; t < fb_sz; ++t) + { + XVisualInfo* xvi = glXGetVisualFromFBConfig(x11_d, fbc[t]); + if (xvi) + { + int samp_buf, samples; + glXGetFBConfigAttrib(x11_d, fbc[t], GLX_SAMPLE_BUFFERS, &samp_buf); + glXGetFBConfigAttrib(x11_d, fbc[t], GLX_SAMPLES, &samples); + XRenderPictFormat* fmt = XRenderFindVisualFormat(x11_d, xvi->visual); + + if (!fmt) continue; + + if (best < 0 || (samp_buf && samples > samp)) + { + best = t; + samp = samples; + } + XFree(xvi); + } + } + + if (best == -1) + { + log_error("%s", "XRender could not find suitable format"); + return; + } + + GLXFBConfig config = fbc[best]; + XFree(fbc); + + XVisualInfo *vi = glXGetVisualFromFBConfig(x11_d, config); + + Colormap cmap = XCreateColormap(x11_d, root, vi->visual, AllocNone); + + XSetWindowAttributes swa; + + swa.colormap = cmap; + swa.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask | + PropertyChangeMask | VisibilityChangeMask; + + swa.background_pixmap = None; + swa.border_pixel = 0; + + window = XCreateWindow(x11_d, root, 0, 0, width, height, 0, vi->depth, InputOutput, + vi->visual, CWColormap | CWEventMask | CWBackPixmap | CWBorderPixel, &swa); + + XFree(vi); + + //disable_input(); + + XStoreName(x11_d, window, name.c_str()); + + glXCreateContextAttribsARBProc glXCreateContextAttribsARB = NULL; + glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) + glXGetProcAddressARB((const GLubyte*) "glXCreateContextAttribsARB"); + + if (!glXCreateContextAttribsARB) + return; + + if (!(glc = glXCreateContextAttribsARB(x11_d, config, 0, True, context_attrs))) + return; + + glXMakeCurrent(x11_d, window, glc); + + if (!gladLoadGL((GLADloadfunc)glXGetProcAddressARB)) + { + log_error("%s", "failed to load glad"); + return; + } + + if (wallpaper) + { + //Atom desktop = XInternAtom(x11_d, "_NET_WM_DESKTOP", false); + + //unsigned long all_desktops = XWIN_ALL_DESKTOPS; + //XChangeProperty(x11_d, window, desktop, + // XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&all_desktops, 1); + + Atom desktop_type = XInternAtom(x11_d, "_NET_WM_WINDOW_TYPE_DESKTOP", false); + + XChangeProperty(x11_d, window, XInternAtom(x11_d, "_NET_WM_WINDOW_TYPE", false), + XA_ATOM, 32, PropModeReplace, (unsigned char*)&desktop_type, 1); + + //XEvent event; + //event.xclient.type = ClientMessage; + //event.xclient.serial = 0; + //event.xclient.send_event = True; + //event.xclient.display = x11_d; + //event.xclient.window = window; + //event.xclient.message_type = XInternAtom(x11_d, "_NET_WM_STATE", false); + //event.xclient.format = 32; + + //event.xclient.data.l[0] = _NET_WM_STATE_ADD; + //event.xclient.data.l[1] = XInternAtom(x11_d, "_NET_WM_STATE_BELOW", false); + //event.xclient.data.l[2] = 0; //unused. + //event.xclient.data.l[3] = 0; + //event.xclient.data.l[4] = 0; + + //XSendEvent(x11_d, root, false, + // SubstructureRedirectMask|SubstructureNotifyMask, &event); + + //event.xclient.data.l[1] = XInternAtom(x11_d, "_NET_WM_STATE_STICKY", false); + + //XSendEvent(x11_d, root, false, + // SubstructureRedirectMask|SubstructureNotifyMask, &event); + + //event.xclient.data.l[1] = XInternAtom(x11_d, "_NET_WM_STATE_FULLSCREEN", false); + } + + XSetWMProtocols(x11_d, window, &ATOM_WM_DELETE_WINDOW, 1); + + XMoveWindow(x11_d, window, 1080, 0); + + XMapWindow(x11_d, window); + XFlush(x11_d); + + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glEnable(GL_MULTISAMPLE); + + print_gl_info(); + + start = system_clock::now(); +} + +void ContextX11::resize_window(int w, int h) +{ + width = w; + height = h; +} + +void ContextX11::swap_buffers() +{ + glXSwapBuffers(x11_d, window); +} + +void ContextX11::raise() { + XClientMessageEvent ev = { + .type = ClientMessage, + .serial = 0, + .send_event = true, + .display = x11_d, + .window = window, + .message_type = ATOM__NET_ACTIVE_WINDOW, + .format = 32, + .data = { .l = { + 1, /* source indication -- `1` when coming from an application */ + 0, /* timestamp -- `0` to (attempt to) ignore */ + (long)window /* requestor's currently active window -- `0` for none */ + } + } + }; + /* Send the client message as defined by EWMH standards (usually works) */ + XSendEvent(x11_d, DefaultRootWindow(x11_d), false, StructureNotifyMask, (XEvent*) &ev); + /* Raise the client in the X11 stacking order (sometimes works, can be blocked by the WM) */ + XRaiseWindow(x11_d, window); + XFlush(x11_d); +} + +void ContextX11::set_clickthrough() +{ + Window win = window, root = DefaultRootWindow(x11_d); + while (win != None) + { + Region region; + if ((region = XCreateRegion())) + { + XShapeCombineRegion(x11_d, window, ShapeInput, 0, 0, region, ShapeSet); + XDestroyRegion(region); + } + + Window parent, *children = NULL; + unsigned int num_children; + + if (XQueryTree(x11_d, win, &root, &parent, &children, &num_children)) + { + if (children) + XFree(children); + } + + win = (parent == root ? None : parent); + } + + XFlush(x11_d); +} + +void ContextX11::close_window() +{ + this->_should_close = true; + +} + +void ContextX11::process_input() +{ + XEvent xev; + + while (XPending(x11_d) > 0) + { + XNextEvent(x11_d, &xev); + + switch (xev.type) + { + case ClientMessage: + if (xev.xclient.message_type == ATOM_WM_PROTOCOLS + && xev.xclient.data.l[0] == ATOM_WM_DELETE_WINDOW) + { + _should_close = true; + } + break; + case MapNotify: // make window not clickable + //set_clickthrough(); + XFlush(x11_d); + break; + } + } +} + +ContextX11::~ContextX11() +{ + glXMakeCurrent(x11_d, None, NULL); + glXDestroyContext(x11_d, glc); + XDestroyWindow(x11_d, window); + XCloseDisplay(x11_d); +} diff --git a/src/context_x11.h b/src/context_x11.h new file mode 100644 index 0000000..c0fd015 --- /dev/null +++ b/src/context_x11.h @@ -0,0 +1,86 @@ +#ifndef _CONTEXT_X11_H +#define _CONTEXT_X11_H + +#include "context.h" + +#include <chrono> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xrender.h> +#include <X11/extensions/shape.h> + +#include <glad/gl.h> + +#include <GL/glx.h> +#include <GL/glu.h> + +#define XWIN_ALL_DESKTOPS 0xFFFFFFFF + +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 + +#define _NET_WM_STATE_REMOVE 0 +#define _NET_WM_STATE_ADD 1 +#define _NET_WM_STATE_TOGGLE 2 + + +typedef GLXContext (*glXCreateContextAttribsARBProc)(Display *, GLXFBConfig, GLXContext, Bool, const int *); + +using std::chrono::time_point; +using std::chrono::system_clock; + +namespace Mauri { + +class ContextX11 : public Context { +public: + ContextX11(int width, int height, std::string name, bool wallpaper); + ~ContextX11(); + + bool create_window(int width, int height, std::string name, bool wallpaper); + void close_window(); + + void resize_window(int width, int height); + + bool should_close() const + { + return _should_close; + } + + void swap_buffers(); + void process_input(); + + void cursor_pos(float *x, float *y) + { + *x = 0.f; + *y = 0.f; + } + + double current_time() + { + std::chrono::duration<double> diff = (system_clock::now() - start); + return diff.count() * 0.5; + } + +private: + bool _should_close = false; + + time_point<system_clock> start; + + void raise(); + + // make the window shape 0x0 so that its unclickable + void set_clickthrough(); + + // attempt to disable input by setting window hints + void disable_input(); + + Window window; + Display *x11_d; + + GLXContext glc; +}; + +} // namespace Mauri + +#endif // _CONTEXT_X11_H diff --git a/src/engine.c b/src/engine.c deleted file mode 100644 index f8cb450..0000000 --- a/src/engine.c +++ /dev/null @@ -1,99 +0,0 @@ -#include "engine.h" - -#include "moro_vec.h" -#include "objects/object.h" -#include "objects/scene.h" -#include "src/context.h" -#include "src/objects/types.h" -#include "src/shader.h" -#include "src/texture.h" - -#include <unistd.h> -#include <cglm/cglm.h> - -void mri_engine_load(mri_engine *engine, mri_scene *scene) { - engine->scene = scene; - - mvec_init(engine->texture_cache); - mvec_init(engine->framebuffers); - - mvec_reserve(engine->texture_cache, 100); - mvec_reserve(engine->framebuffers, mri_scene_framebuffer_count(scene) + 1); - - engine->width = scene->width; - engine->height = scene->height; - - engine->texel_size[0] = 1.f / engine->width; - engine->texel_size[1] = 1.f / engine->height; - - engine->texel_half_size[0] = .5f / engine->width; - engine->texel_half_size[1] = .5f / engine->height; - - engine->combine_buffer = mri_engine_get_or_new_framebuffer(engine, - mstr_c("_rt_FullFrameBuffer"), engine->context->width, engine->context->height, 1.f); - - create_gl_object(&engine->default_gl_object, DEFAULT, 0, 0); -} - -mri_framebuffer *mri_engine_get_framebuffer(mri_engine *engine, mstr *name) { - mri_framebuffer *existing_buffer; - mvec_loop_ptr(engine->framebuffers, existing_buffer, i) { - if (mstr_eq(existing_buffer->name, name)) { - return existing_buffer; - } - } - return NULL; -} - -mri_framebuffer *mri_engine_get_or_new_framebuffer(mri_engine *engine, mstr *name, - float width, float height, float scale) { - - mri_framebuffer *buffer = mri_engine_get_framebuffer(engine, name); - - if (!buffer) { - buffer = mvec_next(engine->framebuffers); - mri_framebuffer_create(buffer, name, width, height, scale); - mri_framebuffer_load(buffer); - } - - return buffer; -} - -void mri_engine_update(mri_engine *engine) { - engine->context->process_input(engine->context); - engine->time = engine->context->current_time(engine->context); -} - -void mri_engine_draw(mri_engine *engine) { - mri_framebuffer_bind(engine->combine_buffer); - glClearBufferfv(GL_COLOR, 0, engine->scene->clear_color); - - mri_scene_draw(engine, engine->scene); - - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glBindFramebuffer(GL_READ_FRAMEBUFFER, engine->combine_buffer->buffer.id); - - // Drawing past the dimensions of the read buffer is an error. - glBlitFramebuffer(0, engine->context->height, engine->context->width, 0, 0, 0, - engine->context->width, engine->context->height, - GL_COLOR_BUFFER_BIT, GL_NEAREST); -} - -void mri_engine_run(mri_engine *engine) { - const float TARGET_FPS = 60.f; - - float time = engine->context->current_time(engine->context); - - while (!engine->context->should_close(engine->context)) { - mri_engine_update(engine); - mri_engine_draw(engine); - - while (engine->context->current_time(engine->context) < time + 1.f / TARGET_FPS) { - usleep(2000); // 2ms - } - - time = engine->context->current_time(engine->context); - - engine->context->swap_buffers(engine->context); - } -} diff --git a/src/engine.cc b/src/engine.cc new file mode 100644 index 0000000..fc76bdd --- /dev/null +++ b/src/engine.cc @@ -0,0 +1,138 @@ +#include "engine.h" +#include "context.h" +#include "shader.h" +#include "src/gl.h" +#include "texture.h" + +#include "objects/object.h" +#include "objects/scene.h" + +#include <glm/ext.hpp> +#include <glm/fwd.hpp> +#include <thread> + +using namespace Mauri; + +void View::center_scale(float w1, float h1) +{ + auto w0 = this->width; + auto h0 = this->height; + + auto w_scale = w1 / w0; + auto h_scale = h1 / h0; + + auto scale = (w_scale > h_scale) ? w_scale : h_scale; + + auto width = w0 * scale; + auto height = h0 * scale; + + auto wpad = (width - w1) / -2.f; + auto hpad = (height - h1) / -2.f; + + glViewport(wpad, hpad, width, height); +} + +void View::default_viewport(float w0, float h0) +{ + glViewport(0, 0, w0, h0); +} + +Engine::Engine(Scene *scene, Context *context) +{ + this->scene = scene; + this->context = context; + + this->view.width = scene->width; + this->view.height = scene->height; + + this->view.texel_size[0] = 1.f / scene->width; + this->view.texel_size[1] = 1.f / scene->height; + + this->view.texel_half_size[0] = .5f / scene->width; + this->view.texel_half_size[1] = .5f / scene->height; + + this->texture_cache.reserve(100); + + auto combine = std::string("_rt_FullFrameBuffer"); + + this->new_framebuffer(combine, this->context->width, this->context->height, 1.f); + this->combine_buffer = this->get_framebuffer(combine); + + this->default_object = new RenderObject(DEFAULT); + this->identity = mat4x4(1.f); + + this->scene->load(this); +} + +Framebuffer *Engine::get_framebuffer(std::string name) +{ + return this->framebuffers[name]; +} + +Texture *Engine::get_framebuffer_texture(std::string name) +{ + return this->framebuffers[name]->texture; +} + +void Engine::new_framebuffer(std::string name, f32 width, f32 height, f32 scale) +{ + this->framebuffers[name] = new Framebuffer(width, height, scale); +} + +void Engine::update() +{ + this->context->process_input(); + this->time = this->context->current_time(); + + for (auto &object: this->scene->objects) + { + object->update(this); + } +} + +void Engine::draw() +{ + for (auto& buffer : this->framebuffers) + { + if (buffer.second != nullptr) + buffer.second->buffer->clear((vec4){0.f, 0.f, 0.f, 0.f}); + } + + if (this->scene->clear_enabled) + this->combine_buffer->buffer->clear(this->scene->clear_color); + + this->scene->draw(this); + + this->combine_buffer->buffer->blit(this->context->width, this->context->height); +} + +void Engine::run() +{ + const f32 TARGET_FPS = 60.f; + + f32 time = this->context->current_time(); + + while (!this->context->should_close()) + { + this->update(); + this->draw(); + + while (this->context->current_time() < time + 1.f / TARGET_FPS) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + time = this->context->current_time(); + + this->context->swap_buffers(); + } +} + +Engine::~Engine() +{ + for (auto &buffer : this->framebuffers) + { + delete buffer.second; + } + delete this->default_object; +} diff --git a/src/engine.h b/src/engine.h index 1b5026a..c663e2d 100644 --- a/src/engine.h +++ b/src/engine.h @@ -1,49 +1,75 @@ #ifndef _ENGINE_H #define _ENGINE_H -#include <cglm/cglm.h> +#include <glm/glm.hpp> #include "context.h" -#include "texture.h" -#include "shader.h" #include "gl.h" +#include "shader.h" +#include "texture.h" -#include "objects/types.h" -#include "objects/object.h" - -struct mri_engine { - mri_context *context; - mri_scene *scene; +using namespace glm; - float width; - float height; +static const std::string COMBINE_BUFFER = "_rt_FullFrameBuffer"; - float time; +namespace Mauri +{ - vec2 texel_size; - vec2 texel_half_size; +class Scene; +class Object; - gl_object default_gl_object; +struct View +{ + float width; + float height; - mri_framebuffer *combine_buffer; + float shake_y = 0.f; + float shake_x = 0.f; - mvec(mri_framebuffer) framebuffers; + bool shake_y_switch = false; + bool shake_x_switch = false; - mvec(mri_texture) texture_cache; + vec2 texel_size; + vec2 texel_half_size; - mri_object *current_object; + void center_scale(float w0, float h0); + void default_viewport(float w0, float h0); }; -void mri_engine_load(mri_engine *engine, mri_scene *scene); +class Engine +{ + public: + Engine(Scene *scene, Context *context); + ~Engine(); + + View view; + + Context *context; + + float time; -mri_framebuffer *mri_engine_get_framebuffer(mri_engine *engine, mstr *name); -mri_framebuffer *mri_engine_get_or_new_framebuffer(mri_engine *engine, - mstr *name, float width, float height, float scale); + RenderObject *default_object; + mat4x4 identity; -void mri_engine_next_pass(mri_engine *engine); -void mri_engine_next_effect(mri_engine *engine); -void mri_engine_next_object(mri_engine *engine, mri_object *object); + std::vector<Texture> texture_cache; + + std::unordered_map<std::string, Framebuffer *> framebuffers; + + Framebuffer *combine_buffer; + + void new_framebuffer(std::string name, float width, float height, float scale); + + Framebuffer *get_framebuffer(std::string name); + Texture *get_framebuffer_texture(std::string name); + + void run(); + void draw(); + void update(); + + private: + Scene *scene; +}; -void mri_engine_run(mri_engine *engine); +} // namespace Mauri #endif // _ENGINE_H diff --git a/src/gl.c b/src/gl.c deleted file mode 100644 index b40880a..0000000 --- a/src/gl.c +++ /dev/null @@ -1,238 +0,0 @@ -#include <glad/gl.h> -#include <stddef.h> - -#include <moro.h> - -#include "cglm/vec4.h" -#include "gl.h" -#include "src/log.h" - -void bind_gl_texture(gl_texture *tex, unsigned int index) { - glActiveTexture(GL_TEXTURE0 + index); - glBindTexture(GL_TEXTURE_2D, tex->id); -} - -void create_gl_texture(gl_texture *tex, bool interp, bool clamp_uv, int mm_count) { - glGenTextures(1, &tex->id); - - bind_gl_texture(tex, 0); - - if (interp) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - if (clamp_uv) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mm_count - 1); -} - -void upload_gl_texture(gl_texture *tex, unsigned char *data, int width, int height, int level, - unsigned int format) { - bind_gl_texture(tex, 0); - - int _width = m_npof2(width); - int _height = m_npof2(height); - - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, _width, _height, 0, - format, GL_UNSIGNED_BYTE, NULL); - - glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, - format, GL_UNSIGNED_BYTE, data); -} - -void destroy_gl_texture(gl_texture *tex) { - glDeleteTextures(1, &tex->id); -} - -void bind_gl_framebuffer(gl_framebuffer *buffer) { - glBindFramebuffer(GL_FRAMEBUFFER, buffer->id); -} - -void create_gl_framebuffer(gl_framebuffer *buffer, float width, float height) { - glGenFramebuffers(1, &buffer->id); - - glGenTextures(1, &buffer->texture.id); - glBindTexture(GL_TEXTURE_2D, buffer->texture.id); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glBindFramebuffer(GL_FRAMEBUFFER, buffer->id); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - buffer->texture.id, 0); - - glDrawBuffers(1, (GLenum[]){GL_COLOR_ATTACHMENT0}); - - glClearBufferfv(GL_COLOR, 0, (vec4){0.f, 0.f, 0.f, 0.f}); -} - -void destroy_gl_framebuffer(gl_framebuffer *buffer) { - glDeleteFramebuffers(1, &buffer->id); -} - -static void set_gl_object(gl_object *obj, float *vertices, int vertex_count) { - glGenVertexArrays(1, &obj->vao); - glBindVertexArray(obj->vao); - - glGenBuffers(1, &obj->vbo); - glBindBuffer(GL_ARRAY_BUFFER, obj->vbo); - - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)12); - - glBufferData(GL_ARRAY_BUFFER, vertex_count, vertices, GL_STATIC_DRAW); -} - -void create_gl_object(gl_object *obj, gl_obj_type type, float width, float height) { - switch (type) { - case DEFAULT: { - float vertices[] = { - -1.f, 1.f, 0.f, 0.f, 1.f, - 1.f, -1.f, 0.f, 1.f, 0.f, - -1.f, -1.f, 0.f, 0.f, 0.f, - -1.f, 1.f, 0.f, 0.f, 1.f, - 1.f, 1.f, 0.f, 1.f, 1.f, - 1.f, -1.f, 0.f, 1.f, 0.f - }; - - set_gl_object(obj, vertices, sizeof(vertices)); - break; - } - case UV_SCALE: { - // Adjust UV coords here so that on the pass where this object is loadn - // the UV cuts out the padding used to make the texture dimensions powers of two. - float x_scale = width / (float)m_npof2(width); - float y_scale = height / (float)m_npof2(height); - - float vertices[] = { - -width, height, 0.f, 0.f, y_scale, - width, -height, 0.f, x_scale, 0.f, - -width, -height, 0.f, 0.f, 0.f, - -width, height, 0.f, 0.f, y_scale, - width, height, 0.f, x_scale, y_scale, - width, -height, 0.f, x_scale, 0.f - }; - - set_gl_object(obj, vertices, sizeof(vertices)); - break; - } - case OBJECT: { - float vertices[] = { - -width, -height, 0.f, 0.f, 1.f, - width, height, 0.f, 1.f, 0.f, - -width, height, 0.f, 0.f, 0.f, - -width, -height, 0.f, 0.f, 1.f, - width, -height, 0.f, 1.f, 1.f, - width, height, 0.f, 1.f, 0.f - }; - - set_gl_object(obj, vertices, sizeof(vertices)); - break; - } - } -} - -void bind_gl_object(gl_object *obj) { - glBindVertexArray(obj->vao); - glBindBuffer(GL_ARRAY_BUFFER, obj->vbo); -} - -static bool compile_check_error(unsigned int shader, char *source, char *error) { - glShaderSource(shader, 1, (const char * const*)&source, 0); - glCompileShader(shader); - - int status; - - glGetShaderiv(shader, GL_COMPILE_STATUS, &status); - - if (status == GL_FALSE) { - glGetShaderInfoLog(shader, 256, NULL, error); - glDeleteShader(shader); - - return false; - } - - return true; -} - -void create_gl_shader(gl_shader *shader, char *vs_source, char *fs_source) { - shader->program = glCreateProgram(); - - char error[256]; - - unsigned int vs_shader = glCreateShader(GL_VERTEX_SHADER); - if (!compile_check_error(vs_shader, vs_source, error)) { - log_error("failed to compile vertex shader (%s)", error); - return; - } - - unsigned int fs_shader = glCreateShader(GL_FRAGMENT_SHADER); - if (!compile_check_error(fs_shader, fs_source, error)) { - log_error("failed to compile fragment shader (%s)", error); - return; - } - - glAttachShader(shader->program, vs_shader); - glAttachShader(shader->program, fs_shader); - - glLinkProgram(shader->program); -} - -void bind_gl_shader(gl_shader *shader) { - glUseProgram(shader->program); -} - -void destroy_gl_shader(gl_shader *shader) { - glDeleteProgram(shader->program); -} - -void set_gl_uniform(unsigned int loc, uniform_type type, float *value) { - switch (type) { - case TYPE_FLOAT: - glUniform1f(loc, *value); - break; - case TYPE_SAMPLER2D: - glUniform1i(loc, (int)*value); - break; - case TYPE_VEC2: - glUniform2fv(loc, 1, value); - break; - case TYPE_VEC3: - glUniform3fv(loc, 1, value); - break; - case TYPE_VEC4: - glUniform4fv(loc, 1, value); - break; - case TYPE_MAT4: - glUniformMatrix4fv(loc, 1, GL_FALSE, value); - break; - } -} - -unsigned int get_uniform_loc(const char *name, gl_shader *shader) { - return glGetUniformLocation(shader->program, name); -} - -void set_gl_uniform_named(const char *name, gl_shader *shader, uniform_type type, - float *value) { - set_gl_uniform(get_uniform_loc(name, shader), type, value); -} diff --git a/src/gl.cc b/src/gl.cc new file mode 100644 index 0000000..9da7974 --- /dev/null +++ b/src/gl.cc @@ -0,0 +1,368 @@ +#include <glm/glm.hpp> +#include <glm/gtc/type_ptr.hpp> + +#include <glad/gl.h> + +#include "gl.h" +#include "log.h" +#include "util.h" + +using namespace Mauri; + +void RenderObject::set_data(f32 *vertices, s32 vertex_count) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glGenVertexArrays(1, &this->vao); + glBindVertexArray(this->vao); + + glGenBuffers(1, &this->vbo); + glBindBuffer(GL_ARRAY_BUFFER, this->vbo); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)12); + + glBufferData(GL_ARRAY_BUFFER, vertex_count, vertices, GL_STATIC_DRAW); +} + +void RenderObject::bind() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glBindVertexArray(this->vao); + glBindBuffer(GL_ARRAY_BUFFER, this->vbo); +} + +RenderObject::RenderObject(RenderObjType type, f32 width, f32 height) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + switch (type) + { + case RenderObjType::DEFAULT: { + f32 vertices[] = { + -1.f, 1.f, 0.f, 0.f, 1.f, + 1.f, -1.f, 0.f, 1.f, 0.f, + -1.f, -1.f, 0.f, 0.f, 0.f, + -1.f, 1.f, 0.f, 0.f, 1.f, + 1.f, 1.f, 0.f, 1.f, 1.f, + 1.f, -1.f, 0.f, 1.f, 0.f + }; + this->set_data(vertices, sizeof(vertices)); + break; + } + case RenderObjType::UV_SCALE: { + // Adjust UV coords here so that on the pass where this object is loadn + // the UV cuts out the padding used to make the texture dimensions powers of two. + f32 x_scale = width / (f32)next_power_of_two(width); + f32 y_scale = height / (f32)next_power_of_two(height); + + f32 vertices[] = { + -width, height, 0.f, 0.f, y_scale, + width, -height, 0.f, x_scale, 0.f, + -width, -height, 0.f, 0.f, 0.f, + -width, height, 0.f, 0.f, y_scale, + width, height, 0.f, x_scale, y_scale, + width, -height, 0.f, x_scale, 0.f + }; + this->set_data(vertices, sizeof(vertices)); + break; + } + case RenderObjType::OBJECT: { + f32 vertices[] = { + -width, -height, 0.f, 0.f, 1.f, + width, height, 0.f, 1.f, 0.f, + -width, height, 0.f, 0.f, 0.f, + -width, -height, 0.f, 0.f, 1.f, + width, -height, 0.f, 1.f, 1.f, + width, height, 0.f, 1.f, 0.f + }; + this->set_data(vertices, sizeof(vertices)); + break; + } + } +} + +RenderObject::~RenderObject() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glDeleteVertexArrays(1, &this->vao); + glDeleteBuffers(1, &this->vbo); +} + +bool RenderShader::compile(u32 program, const char *source, char *error) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glShaderSource(program, 1, &source, 0); + glCompileShader(program); + + s32 status; + + glGetShaderiv(program, GL_COMPILE_STATUS, &status); + + if (status == GL_FALSE) + { + glGetShaderInfoLog(program, 256, NULL, error); + glDeleteShader(program); + return false; + } + + return true; +} + +RenderShader::RenderShader(std::string vs_source, std::string fs_source) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + this->uniform_locations.clear(); + this->program = glCreateProgram(); + + char error[256]; + + u32 vs_shader = glCreateShader(GL_VERTEX_SHADER); + if (!this->compile(vs_shader, vs_source.c_str(), error)) + { + log_error("failed to compile vertex shader (%s)", error); + return; + } + + u32 fs_shader = glCreateShader(GL_FRAGMENT_SHADER); + if (!this->compile(fs_shader, fs_source.c_str(), error)) + { + log_error("failed to compile fragment shader (%s)", error); + return; + } + + glAttachShader(this->program, vs_shader); + glAttachShader(this->program, fs_shader); + + glLinkProgram(this->program); +} + +void RenderShader::bind() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glUseProgram(this->program); +} + +RenderShader::~RenderShader() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glDeleteShader(this->program); +} + +s32 RenderShader::uniform_loc(std::string &name) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + auto loc = this->uniform_locations.find(name); + + if (loc == this->uniform_locations.end()) + { + this->uniform_locations[name] = glGetUniformLocation(this->program, name.c_str()); + return this->uniform_locations[name]; + } + else + return loc->second; +} + +void RenderShader::set_uniform(std::string name, UniformType type, f32 *value) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + auto loc = this->uniform_loc(name); + + switch (type) + { + case TYPE_FLOAT: + glUniform1f(loc, *value); + break; + case TYPE_SAMPLER2D: + glUniform1i(loc, (s32)*value); + break; + case TYPE_VEC2: + glUniform2fv(loc, 1, value); + break; + case TYPE_VEC3: + glUniform3fv(loc, 1, value); + break; + case TYPE_VEC4: + glUniform4fv(loc, 1, value); + break; + case TYPE_MAT4: + glUniformMatrix4fv(loc, 1, GL_FALSE, value); + break; + case TYPE_INVALID: + break; + } +} + +void RenderTexture::bind(s32 index) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glActiveTexture(GL_TEXTURE0 + index); + glBindTexture(GL_TEXTURE_2D, this->id); +} + +RenderTexture::RenderTexture(bool interp, bool clamp_uv, s32 mm_count) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glGenTextures(1, &this->id); + + glBindTexture(GL_TEXTURE_2D, this->id); + + if (interp) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (clamp_uv) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mm_count - 1); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +void RenderTexture::upload(byte *data, s32 width, s32 height, s32 level, u32 format, bool scale, bool dxt) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + this->bind(0); + + if (scale) + { + s32 _width = next_power_of_two(width); + s32 _height = next_power_of_two(height); + + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, _width, _height, 0, + format, GL_UNSIGNED_BYTE, nullptr); + + if (dxt) + { + glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, + format, GL_UNSIGNED_INT_8_8_8_8, data); + } + else + { + glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, + format, GL_UNSIGNED_BYTE, data); + } + } + else + { + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, + format, GL_UNSIGNED_BYTE, data); + } +} + +RenderTexture::~RenderTexture() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glDeleteTextures(1, &this->id); +} + +RenderFramebuffer::RenderFramebuffer(f32 width, f32 height, f32 scale) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + this->width = width / scale; + this->height = height / scale; + + this->texture = new RenderTexture(false, false, 1); + + glGenFramebuffers(1, &this->id); + + this->texture->upload(nullptr, this->width, this->height, 0, GL_RGBA, false); + + glBindFramebuffer(GL_FRAMEBUFFER, this->id); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->texture->id, 0); + + GLenum attatchment[] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, attatchment); +} + +void RenderFramebuffer::bind() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glBindFramebuffer(GL_FRAMEBUFFER, this->id); +} + +void RenderFramebuffer::blit(f32 width, f32 height) +{ + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, this->id); + glBlitFramebuffer(0, this->height, this->width, 0, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); +} + +void RenderFramebuffer::clear(vec4 color) +{ + glBindFramebuffer(GL_FRAMEBUFFER, this->id); + glClearBufferfv(GL_COLOR, 0, glm::value_ptr(color)); +} + +RenderFramebuffer::~RenderFramebuffer() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + delete this->texture; + glDeleteFramebuffers(1, &this->id); +} + @@ -1,61 +1,103 @@ #ifndef _GL_H #define _GL_H -#include <stdbool.h> -#include <cglm/cglm.h> +#include <iostream> +#include <unordered_map> -typedef enum { - DEFAULT, - UV_SCALE, - OBJECT -} gl_obj_type; +#include <glm/glm.hpp> -typedef enum { - TYPE_FLOAT, - TYPE_VEC4, - TYPE_VEC3, - TYPE_VEC2, - TYPE_MAT4, - TYPE_SAMPLER2D -} uniform_type; +#include "util.h" -typedef struct { - unsigned int vao; - unsigned int vbo; -} gl_object; +using namespace glm; -typedef struct { - unsigned int program; -} gl_shader; +namespace Mauri +{ -typedef struct { - unsigned int id; -} gl_texture; +#ifdef __DEBUG__ +static bool gl_initialized = false; +#endif -typedef struct { - unsigned int id; - gl_texture texture; -} gl_framebuffer; +enum RenderObjType +{ + DEFAULT, + UV_SCALE, + OBJECT +}; -void bind_gl_texture(gl_texture *tex, unsigned int index); -void create_gl_texture(gl_texture *tex, bool interp, bool clamp_uv, int mm_count); -void upload_gl_texture(gl_texture *tex, unsigned char *data, int width, int height, int level, - unsigned int format); -void destroy_gl_texture(gl_texture *tex); +enum UniformType +{ + TYPE_FLOAT, + TYPE_VEC4, + TYPE_VEC3, + TYPE_VEC2, + TYPE_MAT4, + TYPE_SAMPLER2D, + TYPE_INVALID +}; -void bind_gl_framebuffer(gl_framebuffer *buffer); -void create_gl_framebuffer(gl_framebuffer *buffer, float width, float height); -void destroy_gl_framebuffer(gl_framebuffer *buffer); +class RenderObject +{ + public: + RenderObject(RenderObjType type, f32 width = 0.f, f32 height = 0.f); + ~RenderObject(); -void create_gl_object(gl_object *obj, gl_obj_type type, float width, float height); -void bind_gl_object(gl_object *obj); + void bind(); + void set_data(float *vertices, s32 vertex_count); -void create_gl_shader(gl_shader *shader, char *vs_source, char *fs_source); -void bind_gl_shader(gl_shader *shader); -void destroy_gl_shader(gl_shader *shader); + private: + u32 vao; + u32 vbo; +}; -void set_gl_uniform(unsigned int loc, uniform_type type, float *value); -unsigned int get_uniform_loc(const char *name, gl_shader *shader); -void set_gl_uniform_named(const char *name, gl_shader *shader, uniform_type type, float *value); +class RenderShader +{ + public: + RenderShader(std::string vs_source, std::string fs_source); + ~RenderShader(); + + bool compile(u32 program, const char *source, char *error); + + void set_uniform(std::string name, UniformType type, f32 *value); + s32 uniform_loc(std::string &name); + + void bind(); + + private: + u32 program = 0; + + std::unordered_map<std::string, s32> uniform_locations; +}; + +class RenderTexture +{ + public: + RenderTexture(bool interp, bool clamp_uv, s32 mm_count); + ~RenderTexture(); + + u32 id = 0; + + void bind(int index); + void upload(byte *data, s32 width, s32 height, s32 level, u32 format, bool scale, bool dxt = false); +}; + +class RenderFramebuffer +{ + public: + RenderFramebuffer(f32 width, f32 height, f32 scale); + ~RenderFramebuffer(); + + u32 id = 0; + + f32 width; + f32 height; + + RenderTexture *texture = nullptr; + + void bind(); + void blit(f32 width, f32 height); + void clear(vec4 color); +}; + +} // namespace Mauri #endif // _GL_H diff --git a/src/json.c b/src/json.c deleted file mode 100644 index ddebcc9..0000000 --- a/src/json.c +++ /dev/null @@ -1,84 +0,0 @@ -#include <cglm/cglm.h> -#include <jansson.h> - -#include "json.h" -#include "log.h" - -json_t *json_load_mstr(mstr *s) { - json_error_t error; - json_t *root = json_loadb(s->ptr, s->len, 0, &error); - - if (!root) { - log_error("err: failed to parse json on line %d: %s", error.line, error.text); - return NULL; - } - - return root; -} - -static json_t *json_value_user(json_t *value) { - if (json_is_object(value)) { - return json_object_get(value, "value"); - } - return value; -} - -void json_get_bool(bool *result, json_t *root, const char *name) { - json_t *value = json_value_user(json_object_get(root, name)); - if (json_is_boolean(value)) { - *result = json_boolean_value(value); - } else { - *result = false; - //log_debug("using default value for (%s)", name); - } -} - -void json_get_int(int *result, json_t *root, const char *name) { - json_t *value = json_value_user(json_object_get(root, name)); - if (json_is_integer(value)) { - *result = json_integer_value(value); - } else { - *result = 0; - //log_debug("using default value for (%s)", name); - } -} - -void json_get_float(float *result, json_t *root, const char *name) { - json_t *value = json_value_user(json_object_get(root, name)); - if (json_is_number(value)) { - *result = json_number_value(value); - } else { - *result = 0.f; - //log_debug("using default value for (%s)", name); - } -} - -void json_get_vec2(vec2 *result, json_t *root, const char *name) { - json_t *value = json_value_user(json_object_get(root, name)); - if (json_is_string(value)) { - sscanf_vec2(json_string_value(value), *result); - } else { - memset(*result, 0, sizeof(vec2)); - //log_debug("using default value for (%s)", name); - } -} - -void json_get_vec3(vec3 *result, json_t *root, const char *name) { - json_t *value = json_value_user(json_object_get(root, name)); - if (json_is_string(value)) { - sscanf_vec3(json_string_value(value), *result); - } else { - memset(*result, 0, sizeof(vec3)); - //log_debug("using default value for (%s)", name); - } -} - -void json_get_vec4(vec4 *result, json_t *root, const char *name) { - json_t *value = json_value_user(json_object_get(root, name)); - if (json_is_string(value)) { - sscanf_vec4(json_string_value(value), *result); - } else { - memset(*result, 0, sizeof(vec4)); - //log_debug("using default value for (%s)", name); - } -} diff --git a/src/json.cc b/src/json.cc new file mode 100644 index 0000000..2be9a0e --- /dev/null +++ b/src/json.cc @@ -0,0 +1,21 @@ +#include "json.h" + +namespace Mauri { + +Json::Value json_user_value(Json::Value value) +{ + if (value.isObject()) { + return value["value"]; + } + return value; +} + +void json_number(Json::Value value, float *res) +{ + if (value.isNumeric()) + *res = value.asFloat(); + else if (value.isString()) + sscanf_vec4(value.asCString(), res); +} + +} @@ -1,24 +1,17 @@ #ifndef _JSON_H #define _JSON_H -#include <cglm/cglm.h> -#include <stdbool.h> -#include <moro_str.h> +#include <jsoncpp/json/json.h> -json_t *json_load_mstr(mstr *s); - -#define json_array(value, array) \ - for (size_t i = 0; i < json_array_size(array) && (value = json_array_get(array, i), 1); i++) +namespace Mauri { #define sscanf_vec4(s, v) sscanf(s, "%f %f %f %f", &(v)[0], &(v)[1], &(v)[2], &(v)[3]) #define sscanf_vec3(s, v) sscanf(s, "%f %f %f", &(v)[0], &(v)[1], &(v)[2]) #define sscanf_vec2(s, v) sscanf(s, "%f %f", &(v)[0], &(v)[1]) - -void json_get_bool(bool *result, json_t *root, const char *name); -void json_get_int(int *result, json_t *root, const char *name); -void json_get_float(float *result, json_t *root, const char *name); -void json_get_vec2(vec2 *result, json_t *root, const char *name); -void json_get_vec3(vec3 *result, json_t *root, const char *name); -void json_get_vec4(vec4 *result, json_t *root, const char *name); + +extern Json::Value json_user_value(Json::Value value); +extern void json_number(Json::Value value, float *res); + +} // namespace Mauri #endif // _JSON_H @@ -8,16 +8,15 @@ #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) -#define log_print(t, fmt, ...) \ - do \ - { \ - PRINT("%s:%d %s(): ", __FILENAME__, __LINE__, __FUNCTION__); \ - PRINT("%s -> " fmt "\n", t, ##__VA_ARGS__); \ - } \ - while(0) +#define log_print(t, fmt, ...) \ + do \ + { \ + PRINT("%s:%d %s(): ", __FILENAME__, __LINE__, __FUNCTION__); \ + PRINT("%s -> " fmt "\n", t, ##__VA_ARGS__); \ + } while (0) -#define log_info(fmt, ...) log_print("info", fmt, ##__VA_ARGS__) -#define log_warn(fmt, ...) log_print("warn", fmt, ##__VA_ARGS__) +#define log_info(fmt, ...) log_print("info", fmt, ##__VA_ARGS__) +#define log_warn(fmt, ...) log_print("warn", fmt, ##__VA_ARGS__) #define log_error(fmt, ...) log_print("error", fmt, ##__VA_ARGS__) #ifdef _DEBUG_ diff --git a/src/mauri.c b/src/mauri.c deleted file mode 100644 index 75d9d47..0000000 --- a/src/mauri.c +++ /dev/null @@ -1,42 +0,0 @@ -#include <stdio.h> -#include <moro_str.h> - -#include "assets.h" -#include "context.h" -#include "engine.h" -#include "context_glfw.h" -#include "objects/scene.h" - -int main(int argc, char* argv[]) { - mri_context *context = mri_glfw_context_new(); - context->create_window(context, 1920, 1080, "mauri", false); - - if (argc == 2) { - if (!mri_asset_manager_load_pkg(mstr_c(argv[1]))) { - return 1; - } - - mri_scene scene = {0}; - mri_scene_parse(&scene); - - //context->resize_window(context, scene.width, scene.height); - - mri_engine engine = {0}; - - engine.context = context; - - mri_engine_load(&engine, &scene); - - mri_scene_load(&engine, &scene); - - mri_engine_run(&engine); - - mri_scene_unload(&scene); - mri_asset_manager_unload(); - } - - context->close_window(context); - mri_glfw_context_unload(context); - - return 0; -} diff --git a/src/mauri.cc b/src/mauri.cc new file mode 100644 index 0000000..f6f6703 --- /dev/null +++ b/src/mauri.cc @@ -0,0 +1,54 @@ +#include "assets.h" +#include "engine.h" +#include "gl.h" +#include "util.h" + +#include "context.h" +#include "context_glfw.h" +#include "context_x11.h" + +#include "objects/scene.h" + +#include <iostream> + +using namespace Mauri; + +static std::string scene_path = std::string("scene.json"); + +s32 main(s32 argc, char *argv[]) +{ + srand(time(NULL)); + +#ifdef __DEBUG__ + gl_initialized = true; +#endif + + //Context *context = new ContextX11(2560, 1440, "mauri", true); + Context *context = new ContextGLFW(720, 1280, "mauri", false); + //Context *context = new ContextGLFW(1920, 1080, "mauri", false); + + if (argc == 2) + { + if (!asset_manager()->load_package(std::string(argv[1]))) + { + delete context; + return 1; + } + + Scene *scene = new Scene(scene_path); + + //context->resize_window(scene->width, scene->height); + + Engine engine(scene, context); + + engine.run(); + + delete scene; + } + + context->close_window(); + + delete context; + + return 0; +} diff --git a/src/objects/effect.c b/src/objects/effect.c deleted file mode 100644 index d5fb747..0000000 --- a/src/objects/effect.c +++ /dev/null @@ -1,133 +0,0 @@ -#include "effect.h" - -#include "../engine.h" -#include "../json.h" -#include "../log.h" - -#include "cglm/mat4.h" -#include "moro_str.h" -#include "moro_vec.h" -#include "src/objects/pass.h" -#include "src/objects/types.h" -#include <jansson.h> - -void mri_effect_parse(mri_effect *effect, json_t *root) { - mvec_init(effect->passes); - mvec_init(effect->passes0); - mvec_init(effect->passes1); - - json_get_int(&effect->id, root, "id"); - - json_get_bool(&effect->visible, root, "visible"); - - json_t *pass0; - json_t *passes0 = json_object_get(root, "passes"); - json_array(pass0, passes0) { - mri_pass_parse(mvec_next(effect->passes0), pass0, EFFECT0_PASS); - } - - json_t *file = json_object_get(root, "file"); - if (json_is_string(file)) { - mri_asset *effect_asset = mri_asset_manager_get(mstr_c(json_string_value(file)), NONE); - - if (effect_asset->type == ASSET_VOID) - return; - - json_t *_root = json_load_mstr(mri_asset_as_str(effect_asset)); - - effect->name = mstr_from(json_string_value(json_object_get(_root, "name"))); - - json_t *pass1; - json_t *passes1 = json_object_get(_root, "passes"); - json_array(pass1, passes1) { - mri_pass_parse(mvec_next(effect->passes1), pass1, EFFECT1_PASS); - } - - mvec_init(effect->fbos); - - json_t *fbo; - json_t *fbos = json_object_get(_root, "fbos"); - json_array(fbo, fbos) { - mri_effect_buffer buffer = { - .name = mstr_from(json_string_value(json_object_get(fbo, "name"))), - .scale = json_number_value(json_object_get(fbo, "scale")) - }; - mvec_push(effect->fbos, buffer); - } - } -} - -mri_render_pass *mri_effect_next_pass(mri_effect *effect) { - mri_render_pass *pass = mvec_next(effect->passes); - mvec_init(pass->uniforms); - mvec_init(pass->combos); - return pass; -} - -void mri_effect_load(mri_engine *engine, mri_effect *effect, bool last_effect) { - mri_effect_buffer *buffer; - mvec_loop_ptr(effect->fbos, buffer, i) { - mri_object *object = engine->current_object; - - mstr *effect_buffer_id = mstr_sprintf("%i_%i", effect->id, object->id); - mstr_cat(buffer->name, effect_buffer_id); - mstr_free(effect_buffer_id); - - mri_engine_get_or_new_framebuffer(engine, buffer->name, - object->size[0], object->size[1], buffer->scale); - } - - for (unsigned int i = 0; i < mvec_size(effect->passes0); i++) { - mri_render_pass *pass = mri_effect_next_pass(effect); - - pass->object = engine->current_object; - pass->effect = effect; - - pass->combine = (i == mvec_size(effect->passes0) - 1) && last_effect; - - if (pass->combine) { - pass->gl_object = &engine->current_object->gl_object; - glm_mat4_copy(engine->current_object->model, pass->model); - } else { - pass->gl_object = &engine->default_gl_object; - glm_mat4_identity(pass->model); - } - - mri_pass_load(engine, mvec_at(effect->passes0, i), EFFECT0_PASS, pass); - mri_pass_load(engine, mvec_at(effect->passes1, i), EFFECT1_PASS, pass); - } -} - -void mri_effect_draw(mri_engine *engine, mri_effect *effect) { - mri_render_pass *pass; - mvec_loop_ptr(effect->passes, pass, i) { - //log_debug("effect (%.*s): %.*s %i", pass->object->name->len, pass->object->name->ptr, - // effect->name->len, effect->name->ptr, i + 1); - mri_render_pass_draw(engine, pass); - } -} - -void mri_effect_unload(mri_effect *effect) { - mstr_free(effect->name); - - mri_pass *pass; - - mvec_loop_ptr(effect->passes0, pass, i) { - mri_pass_unload(pass); - } - - mvec_loop_ptr(effect->passes1, pass, i) { - mri_pass_unload(pass); - } - - mvec_free(effect->passes0); - mvec_free(effect->passes1); - - mri_effect_buffer *buffer; - mvec_loop_ptr(effect->fbos, buffer, i) { - if (buffer->name) - mstr_free(buffer->name); - } - - mvec_free(effect->fbos); -} diff --git a/src/objects/effect.cc b/src/objects/effect.cc new file mode 100644 index 0000000..7f22ef0 --- /dev/null +++ b/src/objects/effect.cc @@ -0,0 +1,90 @@ +#include "../assets.h" +#include "../engine.h" +#include "../json.h" +#include "../log.h" + +#include "effect.h" +#include "fmt/core.h" +#include "object.h" + +#include "objects/pass.h" + +#include <jsoncpp/json/reader.h> + +#include <fmt/format.h> +#include <glm/fwd.hpp> + +using namespace Mauri; + +Effect::Effect(Json::Value root) +{ + this->id = root["id"].asInt(); + + this->visible = json_user_value(root.get("visible", true)).asBool(); + + for (auto &pass0 : root["passes"]) + { + this->passes0.emplace_back(new Pass(pass0, EFFECT0_PASS)); + } + + auto asset = asset_manager()->get_file(root["file"].asString(), NONE); + + if (asset->type == ASSET_VOID) + return; + + Json::Value file = asset->as_json(); + + this->name = file["name"].asString(); + + for (auto &pass1 : file["passes"]) + { + this->passes1.emplace_back(new Pass(pass1, EFFECT1_PASS)); + } + + for (auto &fbo : file["fbos"]) + { + this->fbos.emplace_back((EffectBuffer) { fbo["name"].asString(), fbo["scale"].asFloat() }); + } +} + +void Effect::load(Engine *engine, Object *object, bool last_effect) +{ + if (!this->visible) + return; + + this->buffer_id = fmt::format("{}_{}", this->id, object->id); + + for (auto &fbo : this->fbos) + { + fbo.name.append(this->buffer_id); + engine->new_framebuffer(fbo.name, object->size[0], object->size[1], fbo.scale); + } + + for (int i = 0; i < this->passes0.size(); i++) + { + Pass *pass = new Pass(); + + pass->combine = (i == (this->passes0.size() - 1)) && last_effect; + + pass->object = object; + pass->effect = this; + + this->passes0[i]->load(EFFECT0_PASS, pass); + this->passes1[i]->load(EFFECT1_PASS, pass); + + object->passes.push_back(pass); + } +} + +Effect::~Effect() +{ + for (auto &pass0 : this->passes0) + { + delete pass0; + } + + for (auto &pass1 : this->passes1) + { + delete pass1; + } +} diff --git a/src/objects/effect.h b/src/objects/effect.h index 4d31081..9edeea7 100644 --- a/src/objects/effect.h +++ b/src/objects/effect.h @@ -1,32 +1,43 @@ #ifndef _EFFECT_H #define _EFFECT_H -#include <moro.h> +#include <vector> -#include "types.h" -#include "pass.h" +#include "engine.h" -typedef struct { - mstr *name; - float scale; -} mri_effect_buffer; +namespace Mauri +{ -struct mri_effect { - mstr *name; - int id; +class Pass; + +struct EffectBuffer +{ + std::string name; + float scale; +}; + +class Effect +{ + public: + Effect(Json::Value root); + ~Effect(); + + u32 id; + std::string name; + + std::string buffer_id; + + void load(Engine *engine, Object *object, bool last_effect); - bool visible; + private: + bool visible; - mvec(mri_pass) passes0; - mvec(mri_pass) passes1; - mvec(mri_render_pass) passes; + std::vector<Pass *> passes0; + std::vector<Pass *> passes1; - mvec(mri_effect_buffer) fbos; + std::vector<EffectBuffer> fbos; }; -void mri_effect_parse(mri_effect *effect, json_t *root); -void mri_effect_load(mri_engine *engine, mri_effect *effect, bool last_effect); -void mri_effect_draw(mri_engine *engine, mri_effect *effect); -void mri_effect_unload(mri_effect *effect); +} // namespace Mauri #endif // _EFFECT_H diff --git a/src/objects/material.c b/src/objects/material.c deleted file mode 100644 index 75422d1..0000000 --- a/src/objects/material.c +++ /dev/null @@ -1,87 +0,0 @@ -#include "material.h" -#include "cglm/mat4.h" -#include "pass.h" - -#include <moro_str.h> - -#include "../log.h" -#include "../json.h" -#include "../engine.h" -#include "src/objects/types.h" - -void mri_material_parse(mri_material *material, json_t *value) { - mvec_init(material->passes); - mvec_init(material->passes0); - - if (json_is_string(value)) { - mri_asset *material_asset = mri_asset_manager_get(mstr_c(json_string_value(value)), NONE); - - if (material_asset->type == ASSET_VOID) - return; - - json_t *root = json_load_mstr(mri_asset_as_str(material_asset)); - - json_t *pass; - json_t *passes = json_object_get(root, "passes"); - json_array(pass, passes) { - mri_pass_parse(mvec_next(material->passes0), pass, MATERIAL_PASS); - } - - json_decref(root); - } -} - -mri_render_pass *mri_material_next_pass(mri_material *material) { - mri_render_pass *pass = mvec_next(material->passes); - mvec_init(pass->uniforms); - mvec_init(pass->combos); - return pass; -} - -void mri_material_load(mri_engine *engine, mri_material *material, mri_material_type t, - mri_render_pass *pass, bool no_effects) { - mri_pass *pass0; - mvec_loop_ptr(material->passes0, pass0, i) { - switch (t) { - case MATERIAL_EFFECT: - mri_pass_load(engine, pass0, MATERIAL_PASS, pass); - break; - case MATERIAL_MODEL: - pass = mri_material_next_pass(material); - - pass->object = engine->current_object; - pass->combine = no_effects; - - if (engine->current_object->passthrough || no_effects) { - glm_mat4_copy(engine->current_object->model, pass->model); - } else { - glm_mat4_copy(engine->current_object->ortho, pass->model); - } - - if (engine->current_object->passthrough) { - pass->gl_object = &engine->current_object->gl_object; - } else { - pass->gl_object = &engine->current_object->gl_uv_object; - } - - mri_pass_load(engine, pass0, MATERIAL_ONLY_PASS, pass); - break; - } - } -} - -void mri_material_draw(mri_engine *engine, mri_material *material) { - mri_render_pass *pass; - mvec_loop_ptr(material->passes, pass, i) { - //log_debug("background (%.*s): %i", pass->object->name->len, pass->object->name->ptr, i); - mri_render_pass_draw(engine, pass); - } -} - -void mri_material_unload(mri_material *material) { - mri_pass *pass0; - mvec_loop_ptr(material->passes0, pass0, i) { - mri_pass_unload(pass0); - } - mvec_free(material->passes); -} diff --git a/src/objects/material.cc b/src/objects/material.cc new file mode 100644 index 0000000..4c61cbc --- /dev/null +++ b/src/objects/material.cc @@ -0,0 +1,64 @@ +#include <jsoncpp/json/json.h> + +#include "assets.h" +#include "engine.h" +#include "json.h" +#include "log.h" + +#include "objects/material.h" +#include "objects/object.h" +#include "objects/pass.h" + +using namespace Mauri; + +Material::Material(std::string path) +{ + auto asset = asset_manager()->get_file(path, NONE); + + if (asset->type == ASSET_VOID) + { + log_error("failed to load material.json (%s)", path.c_str()); + return; + } + + Json::Value root = asset->as_json(); + + for (auto &pass : root["passes"]) + { + this->passes0.push_back(new Pass(pass, MATERIAL_PASS)); + } +} + +void Material::load(Object *object, MaterialType type, Pass *pass, bool no_effects) +{ + for (auto &pass0 : this->passes0) + { + switch (type) + { + case MATERIAL_EFFECT: + pass0->load(MATERIAL_PASS, pass); + break; + case MATERIAL_MODEL: + Pass *pass = new Pass(); + + pass->combine = no_effects; + + pass->object = object; + pass->effect = nullptr; + + pass0->load(MATERIAL_ONLY_PASS, pass); + + object->passes.push_back(pass); + + break; + } + } +} + +Material::~Material() +{ + for (auto &pass0 : this->passes0) + { + delete pass0; + } +} diff --git a/src/objects/material.h b/src/objects/material.h index e8bc13d..31b3be0 100644 --- a/src/objects/material.h +++ b/src/objects/material.h @@ -1,25 +1,32 @@ #ifndef _MATERIAL_H #define _MATERIAL_H -#include <jansson.h> +#include <iostream> -#include "types.h" -#include "pass.h" +#include "engine.h" -typedef enum { - MATERIAL_MODEL, - MATERIAL_EFFECT -} mri_material_type; +namespace Mauri +{ -struct mri_material { - mvec(mri_pass) passes0; - mvec(mri_render_pass) passes; +class Pass; + +enum MaterialType +{ + MATERIAL_MODEL, + MATERIAL_EFFECT +}; + +class Material +{ + public: + Material(std::string path); + ~Material(); + + void load(Object *object, MaterialType type, Pass *pass, bool no_effects); + + std::vector<Pass *> passes0; }; -void mri_material_parse(mri_material *material, json_t *value); -void mri_material_load(mri_engine *engine, mri_material *material, mri_material_type t, mri_render_pass *pass, - bool no_effects); -void mri_material_draw(mri_engine *engine, mri_material *material); -void mri_material_unload(mri_material *material); +} // namespace Mauri #endif // MATERIAL_H diff --git a/src/objects/model.c b/src/objects/model.c deleted file mode 100644 index 45317f2..0000000 --- a/src/objects/model.c +++ /dev/null @@ -1,43 +0,0 @@ -#include <jansson.h> -#include <moro_str.h> - -#include "../json.h" -#include "../assets.h" -#include "../log.h" -#include "../engine.h" - -#include "model.h" -#include "src/objects/types.h" - -void mri_model_parse(mri_model *model, json_t *value) { - if (json_is_string(value)) { - mri_asset *model_asset = mri_asset_manager_get(mstr_c(json_string_value(value)), NONE); - - if (model_asset->type == ASSET_VOID) - return; - - json_t *root = json_load_mstr(mri_asset_as_str(model_asset)); - - json_get_bool(&model->autosize, root, "autosize"); - json_get_bool(&model->fullscreen, root, "fullscreen"); - json_get_bool(&model->passthrough, root, "passthrough"); - - mri_material_parse(&model->material, json_object_get(root, "material")); - - json_decref(root); - } else { - memset(model, 0, sizeof(mri_model)); - } -} - -void mri_model_load(mri_engine *engine, mri_model *model, bool no_effects) { - mri_material_load(engine, &model->material, MATERIAL_MODEL, NULL, no_effects); -} - -void mri_model_draw(mri_engine *engine, mri_model *model) { - mri_material_draw(engine, &model->material); -} - -void mri_model_unload(mri_model *model) { - mri_material_unload(&model->material); -} diff --git a/src/objects/model.cc b/src/objects/model.cc new file mode 100644 index 0000000..59c1cb3 --- /dev/null +++ b/src/objects/model.cc @@ -0,0 +1,40 @@ +#include <jsoncpp/json/json.h> + +#include "assets.h" +#include "json.h" +#include "log.h" + +#include "objects/material.h" +#include "objects/model.h" + +using namespace Mauri; + +Model::Model(std::string path) +{ + auto asset = asset_manager()->get_file(path, NONE); + + if (asset->type == ASSET_VOID) + { + log_error("failed to load model.json (%s)", path.c_str()); + return; + } + + Json::Value root = asset->as_json(); + + this->autosize = root.get("autosize", true).asBool(); + this->fullscreen = root.get("fullscreen", false).asBool(); + this->passthrough = root.get("passthrough", false).asBool(); + + this->material = new Material(root["material"].asString()); +} + +void Model::load(Object *object, bool no_effects) +{ + this->material->load(object, MATERIAL_MODEL, NULL, no_effects); +} + +Model::~Model() +{ + if (this->material != nullptr) + delete this->material; +} diff --git a/src/objects/model.h b/src/objects/model.h index 9daed21..e25a592 100644 --- a/src/objects/model.h +++ b/src/objects/model.h @@ -1,22 +1,31 @@ #ifndef _MODEL_H #define _MODEL_H -#include <jansson.h> +#include <iostream> -#include "types.h" -#include "pass.h" -#include "material.h" +#include "engine.h" -struct mri_model { - bool autosize; - bool fullscreen; - bool passthrough; - mri_material material; +#include "objects/material.h" + +namespace Mauri +{ + +class Model +{ + public: + Model(std::string path); + ~Model(); + + bool autosize; + bool fullscreen; + bool passthrough; + + void load(Object *object, bool no_effects); + + private: + Material *material = nullptr; }; -void mri_model_parse(mri_model *model, json_t *value); -void mri_model_load(mri_engine *engine, mri_model *model, bool no_effects); -void mri_model_draw(mri_engine *engine, mri_model *model); -void mri_model_unload(mri_model *model); +} // namespace Mauri #endif // _MODEL_H diff --git a/src/objects/object.c b/src/objects/object.c index 431852e..69c5411 100644 --- a/src/objects/object.c +++ b/src/objects/object.c @@ -1,174 +1 @@ -#include <jansson.h> -#include <cglm/cglm.h> - -#include "moro_str.h" -#include "src/shader.h" -#include "src/texture.h" - -#include "types.h" -#include "object.h" -#include "model.h" -#include "scene.h" - -#include "../log.h" -#include "../gl.h" -#include "../engine.h" -#include "../json.h" - -void mri_object_parse(mri_object *object, json_t *root) { - json_get_int(&object->id, root, "id"); - - object->name = mstr_from(json_string_value(json_object_get(root, "name"))); - - mvec_init(object->deps); - - json_t *dep; - json_t *deps = json_object_get(root, "dependencies"); - json_array(dep, deps) { - int value = json_integer_value(dep); - mvec_push(object->deps, value); - } - - json_get_float(&object->alpha, root, "alpha"); - json_get_vec3(&object->angles, root, "angles"); - json_get_vec3(&object->color, root, "color"); - json_get_vec2(&object->size, root, "size"); - json_get_vec3(&object->scale, root, "scale"); - json_get_vec3(&object->origin, root, "origin"); - object->origin[2] = 0.f; - - json_get_int(&object->color_blend_mode, root, "colorblendmode"); - json_get_bool(&object->copy_background, root, "copybackground"); - - json_get_bool(&object->visible, root, "visible"); - - json_t *config = json_object_get(root, "config"); - if (json_is_object(config)) { - json_get_bool(&object->passthrough, config, "passthrough"); - } - - mri_model_parse(&object->image, json_object_get(root, "image")); - - mvec_init(object->effects); - - json_t *effect; - json_t *effects = json_object_get(root, "effects"); - json_array(effect, effects) { - mri_effect_parse(mvec_next(object->effects), effect); - } -} - -void mri_object_get_model(mri_object *object, mat4 model, float width, float height) { - glm_mat4_identity(model); - - glm_translate_to(model, (vec3){-width, -height, 0.f}, model); - - vec3 origin; - glm_vec3_scale(object->origin, 2.f, origin); - glm_translate_to(model, origin, model); - - glm_rotate(model, object->angles[0], (vec3){1.f, 0.f, 0.f}); - glm_rotate(model, object->angles[1], (vec3){0.f, 1.f, 0.f}); - glm_rotate(model, object->angles[2], (vec3){0.f, 0.f, 1.f}); - - glm_scale(model, object->scale); - - mat4 ortho; - glm_ortho(-width, width, height, -height, 0.f, 1.f, ortho); - glm_mat4_mul(ortho, model, model); -} - -void mri_object_load(mri_engine *engine, mri_object *object) { - if (object->loaded) return; - - engine->current_object = object; - - float width = object->size[0]; - float height = object->size[1]; - - mstr *abuffer = mstr_sprintf("_rt_imageLayerComposite_%i_a", object->id); - mstr *bbuffer = mstr_sprintf("_rt_imageLayerComposite_%i_b", object->id); - - object->abuffer = mri_engine_get_or_new_framebuffer(engine, - abuffer, width, height, 1.f); - - object->bbuffer = mri_engine_get_or_new_framebuffer(engine, - bbuffer, width, height, 1.f); - - mstr_free(abuffer); - mstr_free(bbuffer); - - object->buffer_switch = false; - - mri_object_get_model(object, engine->current_object->model, - engine->width, engine->height); - - glm_ortho(-width, width, -height, height, 0.f, 1.f, - engine->current_object->ortho); - - create_gl_object(&engine->current_object->gl_uv_object, - UV_SCALE, width, height); - - create_gl_object(&engine->current_object->gl_object, - OBJECT, width, height); - - mri_model_load(engine, &object->image, mvec_size(object->effects) == 0); - - if (object->image.fullscreen) { - object->fullscreen = true; - object->size[0] = engine->width; - object->size[1] = engine->height; - object->origin[0] = object->size[0] / 2.f; - object->origin[1] = object->size[1] / 2.f; - } - - if (object->image.passthrough) { - object->passthrough = true; - } - - mri_effect *effect; - mvec_loop_ptr(object->effects, effect, i) { - mri_effect_load(engine, effect, i == (mvec_size(object->effects) - 1)); - } - - object->loaded = true; -} - -void mri_object_switch_buffer(mri_object *object) { - object->buffer_switch = !object->buffer_switch; -} - -mri_framebuffer *mri_object_get_target_buffer(mri_object *object) { - return (object->buffer_switch) ? object->abuffer : object->bbuffer; -} - -mri_texture *mri_object_get_texture_buffer(mri_object *object) { - return &((object->buffer_switch) ? object->bbuffer : object->abuffer)->texture; -} - -void mri_object_draw(mri_engine *engine, mri_object *object) { - mri_model_draw(engine, &object->image); - - mri_effect *effect; - mvec_loop_ptr(object->effects, effect, i) { - mri_effect_draw(engine, effect); - } -} - -void mri_object_unload(mri_object *object) { - mstr_free(object->name); - - mri_effect *effect; - mvec_loop_ptr(object->effects, effect, i) { - mri_effect_unload(effect); - } - - mvec_free(object->effects); - mvec_free(object->deps); - - mri_framebuffer_unload(object->abuffer); - mri_framebuffer_unload(object->bbuffer); - - mri_model_unload(&object->image); -} - +//#include "../engine.h" diff --git a/src/objects/object.cc b/src/objects/object.cc new file mode 100644 index 0000000..a356637 --- /dev/null +++ b/src/objects/object.cc @@ -0,0 +1,174 @@ +#include <glm/fwd.hpp> +#include <glm/trigonometric.hpp> +#include <jsoncpp/json/json.h> + +#include <glm/ext/matrix_transform.hpp> +#include <glm/glm.hpp> +#include <glm/gtc/type_ptr.hpp> + +#include <fmt/core.h> + +#include "../gl.h" +#include "../json.h" +#include "../log.h" + +#include "objects/effect.h" +#include "objects/object.h" +#include "objects/scene.h" +#include "objects/pass.h" + +using namespace Mauri; + +Object::Object(Json::Value root) +{ + this->id = root["id"].asInt(); + this->name = root["name"].asString(); + + for (auto &id : root["dependencies"]) + this->deps.push_back(id.asInt()); + + sscanf_vec3(json_user_value(root.get("angles", "")).asCString(), this->angles); + sscanf_vec3(json_user_value(root.get("colors", "")).asCString(), this->color); + sscanf_vec2(json_user_value(root.get("size", "")).asCString(), this->size); + sscanf_vec3(json_user_value(root.get("scale", "")).asCString(), this->scale); + sscanf_vec3(json_user_value(root.get("origin", "")).asCString(), this->origin); + + this->color_blend_mode = (BlendMode)root.get("colorblendmode", 0).asInt(); + + this->visible = json_user_value(root.get("visible", true)).asBool(); + + auto _image = root["image"]; + + if (!_image.isNull()) + this->image = new Model(_image.asString()); + + auto config = root["config"]; + + if (!config.isNull()) + this->passthrough = config.get("passthrough", false).asBool(); + + for (auto &effect : root["effects"]) + { + this->effects.emplace_back(new Effect(effect)); + } +} + +void Object::get_model(f32 width, f32 height, bool flip) +{ + this->model = mat4x4(1.f); + + this->model = glm::translate(this->model, (vec3){-width, -height, 0.f}); + this->model = glm::translate(this->model, this->origin * 2.f); + + this->model = glm::rotate(this->model, this->angles[0], (vec3){1.f, 0.f, 0.f}); + this->model = glm::rotate(this->model, this->angles[1], (vec3){0.f, 1.f, 0.f}); + this->model = glm::rotate(this->model, this->angles[2], (vec3){0.f, 0.f, 1.f}); + + this->model = glm::scale(this->model, this->scale); + + mat4x4 _ortho = glm::ortho(-width, width, height, -height, 0.f, 1.f); + + if (flip) + { + this->model = glm::scale(this->model, (vec3){1.f, -1.f, 1.f}); + } + + this->model = _ortho * this->model; +} + +void Object::load(Engine *engine) +{ + if (this->loaded || !this->visible) + return; + + f32 width = this->size[0]; + f32 height = this->size[1]; + + this->abuffer = fmt::format("_rt_imageLayerComposite_{}_a", this->id); + this->bbuffer = fmt::format("_rt_imageLayerComposite_{}_b", this->id); + + engine->new_framebuffer(this->abuffer, width, height, 1.f); + engine->new_framebuffer(this->bbuffer, width, height, 1.f); + + this->get_model(engine->view.width, engine->view.height, this->effects.size() == 0); + + this->ortho = glm::ortho(-width, width, -height, height, 0.f, 1.f); + + this->gl_object = new RenderObject(OBJECT, width, height); + this->gl_uv_object = new RenderObject(UV_SCALE, width, height); + + if (this->image) + { + this->image->load(this, this->effects.size() == 0); + + if (this->image->fullscreen) + { + this->fullscreen = true; + this->size[0] = engine->view.width; + this->size[1] = engine->view.height; + this->origin[0] = this->size[0] / 2.f; + this->origin[1] = this->size[1] / 2.f; + } + + if (this->image->passthrough) + this->passthrough = true; + } + + for (int i = 0; i < this->effects.size(); i++) + { + this->effects[i]->load(engine, this, i == (this->effects.size() - 1)); + } + + this->loaded = true; +} + +void Object::update(Engine *engine) +{ + //if (engine->time > this->shake_tick) + //{ + //} +} + +void Object::draw(Engine *engine) +{ + if (!this->visible) + return; + + for (auto &pass : this->passes) + { + pass->draw(engine); + } +} + +std::string Object::get_target_buffer() const +{ + if (this->buffer_switch) + return this->abuffer; + else + return this->bbuffer; +} + +std::string Object::get_texture_buffer() const +{ + if (this->buffer_switch) + return this->bbuffer; + else + return this->abuffer; +} + +Object::~Object() +{ + if (this->image != nullptr) + delete this->image; + + if (this->gl_object != nullptr) + delete this->gl_object; + + if (this->gl_uv_object != nullptr) + delete this->gl_uv_object; + + for (auto &effect : this->effects) + { + delete effect; + } +} diff --git a/src/objects/object.h b/src/objects/object.h index fbaae60..fe44e03 100644 --- a/src/objects/object.h +++ b/src/objects/object.h @@ -1,76 +1,90 @@ #ifndef _OBJECT_H #define _OBJECT_H -#include <moro.h> -#include <jansson.h> -#include <stdbool.h> -#include <cglm/cglm.h> +#include <glm/glm.hpp> +#include <jsoncpp/json/json.h> + +#include "../engine.h" -#include "src/texture.h" -#include "types.h" -#include "model.h" -#include "pass.h" #include "effect.h" +#include "model.h" -#include "../gl.h" +using namespace glm; -typedef enum { - CENTER, -} mri_object_alignment; +namespace Mauri +{ -struct mri_object { - int id; - mstr *name; +enum ObjectAlignment +{ + CENTER, +}; - mvec(int) deps; +enum BlendMode +{ + TRANSLUCENT = 0, + NORMAL = 1 +}; - mri_object_alignment alignment; +class Object +{ + public: + Object(Json::Value root); + ~Object(); - float alpha; + u32 id; + std::string name; - vec3 angles; - vec3 color; + std::vector<int> deps; - int color_blend_mode; + bool passthrough = false; + bool fullscreen = false; - bool copy_background; + vec2 size; - mri_model image; + void get_model(f32 width, f32 height, bool flip); - vec2 size; - vec3 origin; - vec3 scale; + mat4x4 ortho; + mat4x4 model; - bool visible; + RenderObject *gl_uv_object = nullptr; + RenderObject *gl_object = nullptr; - bool passthrough; - bool fullscreen; + BlendMode color_blend_mode; - gl_object gl_uv_object; - gl_object gl_object; - - mat4 ortho; - mat4 model; + std::string get_target_buffer() const; + std::string get_texture_buffer() const; - mri_framebuffer *abuffer; - mri_framebuffer *bbuffer; + void swap_buffer() { + this->buffer_switch = !this->buffer_switch; + } - bool buffer_switch; + std::vector<Pass *> passes; - bool loaded; + void load(Engine *engine); + void update(Engine *engine); + void draw(Engine *engine); - mvec(mri_effect) effects; -}; + private: + bool visible; -void mri_object_get_model(mri_object *object, mat4 model, float width, float height); + Model *image = nullptr; -void mri_object_switch_buffer(mri_object *object); -mri_framebuffer *mri_object_get_target_buffer(mri_object *object); -mri_texture *mri_object_get_texture_buffer(mri_object *object); + vec3 angles; + vec3 color; + + vec3 origin; + vec3 scale; + + std::string abuffer; + std::string bbuffer; + + bool buffer_switch = false; + + bool loaded = false; + + std::vector<Effect *> effects; +}; -void mri_object_parse(mri_object *object, json_t *root); -void mri_object_load(mri_engine *engine, mri_object *object); -void mri_object_draw(mri_engine *engine, mri_object *object); -void mri_object_unload(mri_object *object); +} // namespace Mauri #endif // _OBJECT_H diff --git a/src/objects/pass.c b/src/objects/pass.c deleted file mode 100644 index df3b70d..0000000 --- a/src/objects/pass.c +++ /dev/null @@ -1,405 +0,0 @@ -#include <jansson.h> -#include <unistd.h> - -#include "moro_str.h" -#include "moro_vec.h" -#include "pass.h" -#include "material.h" -#include "model.h" -#include "object.h" -#include "scene.h" - -#include "../json.h" -#include "../texture.h" -#include "../engine.h" -#include "../assets.h" -#include "../gl.h" -#include "../objects/types.h" -#include "../shader.h" - -void mri_pass_parse(mri_pass *pass, json_t *root, mri_pass_stage stage) { - memset(pass, 0, sizeof(mri_pass)); - - switch (stage) { - case EFFECT0_PASS: { - mvec_init(pass->uniforms); - - json_t *uniform; - json_t *uniforms = json_object_get(root, "constantshadervalues"); - - void *iter = json_object_iter(uniforms); - - while (iter) { - uniform = json_object_iter_value(iter); - - mri_uniform u = { - .name = mstr_from(json_object_iter_key(iter)) - }; - - if (json_is_object(uniform)) { - uniform = json_object_get(uniform, "value"); - } - - if (json_is_number(uniform)) { - u.value[0] = json_number_value(uniform); - } else if (json_is_string(uniform)) { - sscanf_vec4(json_string_value(uniform), u.value); - } - - mvec_push(pass->uniforms, u); - - iter = json_object_iter_next(uniforms, iter); - } - - break; - } - case EFFECT1_PASS: { - json_t *material = json_object_get(root, "material"); - if (json_is_string(material)) { - pass->material = (mri_material *)calloc(1, sizeof(mri_material)); - mri_material_parse(pass->material, material); - } - - json_t *target = json_object_get(root, "target"); - if (json_is_string(target)) { - pass->target = mstr_from(json_string_value(target)); - } - - json_t *bind; - json_t *binds = json_object_get(root, "bind"); - json_array(bind, binds) { - int index = json_integer_value(json_object_get(bind, "index")); - pass->binds[index] = mstr_from(json_string_value(json_object_get(bind, "name"))); - } - - break; - } - case MATERIAL_ONLY_PASS: - case MATERIAL_PASS: { - json_t *shader = json_object_get(root, "shader"); - if (json_is_string(shader)) { - pass->shader = (mri_shader *)calloc(1, sizeof(mri_shader)); - mri_shader_load(pass->shader, mstr_c(json_string_value(shader))); - } - - json_t *blend = json_object_get(root, "blending"); - if (json_is_string(blend)) { - if (mstr_eq(mstr_c(json_string_value(blend)), mstr_c("normal"))) { - pass->blend = NORMAL; - } else if (mstr_eq(mstr_c(json_string_value(blend)), mstr_c("translucent"))) { - pass->blend = TRANSLUCENT; - } - } - - break; - } - } - - json_t *texture; - json_t *textures = json_object_get(root, "textures"); - json_array(texture, textures) { - if (json_is_string(texture)) { - pass->textures[i] = mstr_from(json_string_value(texture)); - } - } - - mvec_init(pass->combos); - - json_t *combo; - json_t *combos = json_object_get(root, "combos"); - - void *iter = json_object_iter(combos); - - while (iter) { - combo = json_object_iter_value(iter); - - mri_combo c = { - .name = mstr_from(json_object_iter_key(iter)) - }; - - if (json_is_integer(combo)) { - c.value = json_integer_value(combo); - } - - mvec_push(pass->combos, c); - - iter = json_object_iter_next(combo, iter); - } -} - -void mri_pass_load(mri_engine *engine, mri_pass *pass, mri_pass_stage stage, mri_render_pass *rpass) { - mri_object *object = engine->current_object; - - for (int i = 0; i < 7; i++) { - if (pass->textures[i]) { - mri_asset *texture_asset = mri_asset_manager_get( - pass->textures[i], TEXTURE); - if (texture_asset->type != ASSET_VOID) { - rpass->textures[i] = mri_texture_load(engine, texture_asset); - } else { - mri_framebuffer *buffer = mri_engine_get_framebuffer( - engine, pass->textures[i]); - if (buffer) { - rpass->textures[i] = &buffer->texture; - } - } - } - } - - mri_combo *combo; - mvec_loop_ptr(pass->combos, combo, i) { - mvec_push(rpass->combos, *combo); - } - - switch (stage) { - case EFFECT0_PASS: { - mri_uniform *uniform; - mvec_loop_ptr(pass->uniforms, uniform, i) { - mvec_push(rpass->uniforms, *uniform); - } - break; - } - case EFFECT1_PASS: - // If there is an explicit target framebuffer, set it. - if (!pass->target) { - if (rpass->combine) { - rpass->target = engine->combine_buffer; - } else { - // If no target was specified pick the next buffer in the - // alternating AB buffers. - rpass->target = mri_object_get_target_buffer(object); - } - rpass->textures[0] = mri_object_get_texture_buffer(object); - } else { - mstr *effect_buffer_id = mstr_sprintf("%i_%i", rpass->effect->id, rpass->object->id); - - mstr_cat(pass->target, effect_buffer_id); - mstr_free(effect_buffer_id); - - rpass->target = mri_engine_get_framebuffer(engine, pass->target); - } - - for (int i = 0; i < 7; i++) { - if (!pass->binds[i]) continue; - // If there are explicit binds given for this pass, set them. - if (mstr_eq(pass->binds[i], mstr_c("previous"))) { - rpass->textures[i] = mri_object_get_texture_buffer(object); - } else { - mstr *effect_buffer_id = mstr_sprintf("%i_%i", rpass->effect->id, rpass->object->id); - - mstr_cat(pass->binds[i], effect_buffer_id); - mstr_free(effect_buffer_id); - - rpass->textures[i] = &mri_engine_get_framebuffer(engine, pass->binds[i])->texture; - } - } - - // If we selected one of the AB buffers, switch the flag _after_ - // setting binds to keep the ordering correct. - if (!pass->target) mri_object_switch_buffer(object); - - // EFFECT1 passes has the material with the final draw passes. - mri_material_load(engine, pass->material, MATERIAL_EFFECT, rpass, false); - break; - case MATERIAL_ONLY_PASS: - // There is no EFFECT1 pass before this to set the target, so set it here. - if (rpass->combine) { - rpass->target = engine->combine_buffer; - } else { - rpass->target = mri_object_get_target_buffer(object); - mri_object_switch_buffer(object); - } - rpass->combine_blend = pass->blend; - __attribute__ ((fallthrough)); - case MATERIAL_PASS: { - rpass->shader = *pass->shader; - - mvec_clone(rpass->shader.uniforms, pass->shader->uniforms); - mvec_clone(rpass->shader.combos, pass->shader->combos); - - for (int i = 0; i < 7; i++) { - // As the last step for shaders, set any unset texture slots - // with the default textures from the shader is there is any. - if (!rpass->textures[i] && rpass->shader.textures[i]) { - mri_asset *texture_asset = mri_asset_manager_get( - rpass->shader.textures[i], TEXTURE); - if (texture_asset->type != ASSET_VOID) { - rpass->textures[i] = mri_texture_load(engine, texture_asset); - } - } - - // Set TEX0FORMAT in-case extra steps are needed when sampling, - // see "common_fragment.h". - if (rpass->textures[i] && i == 0) { - mri_combo c = { - .name = mstr_from("TEX0FORMAT"), - .value = rpass->textures[i]->format - }; - mvec_push(rpass->shader.combos, c); - } - } - - mri_uniform *pass_uniform, *shader_uniform; - mvec_loop_ptr(rpass->shader.uniforms, shader_uniform, i) { - // ex: g_Texture2Resolution (vec4) - // ^ index - if (mstr_eqn(shader_uniform->uname, mstr_c("g_Texture"), 9) && - mstr_cmp(shader_uniform->uname, mstr_c("Resolution"), 10, 10) == 0) { - int index = shader_uniform->uname->ptr[9] - '0'; - if (rpass->textures[index]) { - memcpy(shader_uniform->value, - rpass->textures[index]->texture_resolution, sizeof(vec4)); - } - } - } - - // Handle uniform value overrides. - mvec_loop_ptr(rpass->uniforms, pass_uniform, i) - mvec_loop_ptr(rpass->shader.uniforms, shader_uniform, s) { - if (!shader_uniform->name) continue; - if (mstr_eq(pass_uniform->name, shader_uniform->name)) { - memcpy(shader_uniform->value, pass_uniform->value, sizeof(vec4)); - break; - } - } - - mri_combo *pass_combo, *shader_combo; - mvec_loop_ptr(rpass->shader.combos, shader_combo, s) { - if (mstr_eq(shader_combo->name, mstr_c("AUDIOPROCESSING"))) { - shader_combo->value = 0; - break; - } - } - - // Handle combo value overrides. - mvec_loop_ptr(rpass->combos, pass_combo, i) { - bool override = false; - mvec_loop_ptr(rpass->shader.combos, shader_combo, s) { - if (mstr_eq(pass_combo->name, shader_combo->name)) { - shader_combo->value = pass_combo->value; - override = true; - break; - } - } - if (!override) { - mvec_push(rpass->shader.combos, *pass_combo); - } - } - - // compile the shader with the new combo values. - mri_shader_compile(&rpass->shader); - - break; - } - } -} - -static void viewport_center_scale(float w0, float h0, float w1, float h1) { - float w_scale = w1 / w0; - float h_scale = h1 / h0; - - float scale = (w_scale > h_scale) ? w_scale : h_scale; - - float width = w0 * scale; - float height = h0 * scale; - - float wpad = (width - w1) / -2.f; - float hpad = (height - h1) / -2.f; - - glViewport(wpad, hpad, width, height); -} - -static void viewport_default(float w0, float h0) { - glViewport(0, 0, w0, h0); -} - -void mri_render_pass_draw(mri_engine *engine, mri_render_pass *pass) { - mri_framebuffer_bind(pass->target); - mri_shader_bind(engine, &pass->shader, pass->model); - - bind_gl_object(pass->gl_object); - - for (int i = 0; i < pass->shader.texture_count; i++) { - if (pass->textures[i]) mri_texture_bind(pass->textures[i], i); - } - - mri_pass_blend_mode blend; - - if (pass->combine) { - blend = pass->combine_blend; - } else { - blend = NORMAL; - } - - switch (blend) { - case NORMAL: - glBlendFunc(GL_ONE, GL_ZERO); - glColorMask(1.f, 1.f, 1.f, 1.f); - break; - case TRANSLUCENT: - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColorMask(1.f, 1.f, 1.f, 0.f); - break; - } - - if (pass->combine) { - viewport_center_scale(engine->width, engine->height, engine->context->width, engine->context->height); - } else { - viewport_default(pass->target->width, pass->target->height); - } - - if (pass->object->visible) - glDrawArrays(GL_TRIANGLES, 0, 6); - - for (int i = 0; i < pass->shader.texture_count; i++) { - glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(GL_TEXTURE_2D, 0); - } - -#if 0 - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->target->buffer.id); - - glBlitFramebuffer(0, engine->context->height, engine->context->width, 0, 0, 0, - engine->context->width, engine->context->height, - GL_COLOR_BUFFER_BIT, GL_NEAREST); - - engine->context->swap_buffers(engine->context); - - usleep(1400000); -#endif -} - -void mri_pass_unload(mri_pass *pass) { - // TODO render pass unloading - - for (int i = 0; i < 7; i++) { - if (pass->textures[i]) mstr_free(pass->textures[i]); - } - - mri_uniform *uniform; - mvec_loop_ptr(pass->uniforms, uniform, i) { - mstr_free(uniform->name); - } - - mri_combo *combo; - mvec_loop_ptr(pass->combos, combo, i) { - mstr_free(combo->name); - } - - mvec_free(pass->uniforms); - mvec_free(pass->combos); - - if (pass->shader) { - mri_shader_unload(pass->shader); - free(pass->shader); - } - - if (pass->material) { - mri_material_unload(pass->material); - free(pass->material); - } - - if (pass->target) - mstr_free(pass->target); -} diff --git a/src/objects/pass.cc b/src/objects/pass.cc new file mode 100644 index 0000000..7825602 --- /dev/null +++ b/src/objects/pass.cc @@ -0,0 +1,346 @@ +#include <fmt/core.h> +#include <glm/fwd.hpp> +#include <glm/gtc/type_ptr.hpp> +#include <thread> + +#include "assets.h" +#include "engine.h" +#include "gl.h" +#include "json.h" +#include "shader.h" +#include "json.h" +#include "texture.h" + +#include "objects/pass.h" +#include "objects/material.h" +#include "objects/model.h" +#include "objects/object.h" +#include "objects/scene.h" + +using namespace Mauri; + +Pass::Pass(Json::Value root, PassStage stage) +{ + switch (stage) + { + case EFFECT0_PASS: { + auto uniforms = root["constantshadervalues"]; + + for (auto &name : uniforms.getMemberNames()) + { + auto uniform = Uniform(); + + uniform.name = name; + + json_number(json_user_value(uniforms[name]), + glm::value_ptr(uniform.value)); + + this->uniforms.push_back(uniform); + } + + break; + } + case EFFECT1_PASS: { + auto material = root["material"]; + + if (material.isString()) + this->material = new Material(material.asString()); + + this->target = root.get("target", "previous").asString(); + + for (auto &bind : root["bind"]) + { + auto index = bind["index"].asInt(); + this->binds[index] = bind["name"].asString(); + } + + break; + } + case MATERIAL_ONLY_PASS: + this->target = "previous"; + [[fallthrough]]; + case MATERIAL_PASS: { + this->shader = root["shader"].asString(); + + auto blend = root.get("blending", "normal").asString(); + + if (blend == "normal") + this->blend = NORMAL; + else if (blend == "translucent") + this->blend = TRANSLUCENT; + + break; + } + } + + auto _textures = root["textures"]; + + for (int i = 0; i < textures.size(); i++) + { + auto texture = _textures[i]; + if (texture.isString()) + this->textures[i] = texture.asString(); + } + + auto _combos = root["combos"]; + + for (auto &name : _combos.getMemberNames()) + { + this->combos.emplace_back((Combo){name, _combos[name].asInt()}); + } +} + +void Pass::load(PassStage stage, Pass *pass) +{ + for (int i = 0; i < 8; i++) + { + if (this->textures[i].empty()) + continue; + + pass->textures[i] = this->textures[i]; + } + + for (auto &combo : this->combos) + { + pass->combos.push_back(combo); + } + + switch (stage) + { + case EFFECT0_PASS: + for (auto &uniform : this->uniforms) + { + pass->uniforms.push_back(uniform); + } + break; + case EFFECT1_PASS: + pass->target = this->target; + + if (pass->target == "previous") + { + if (pass->combine) + pass->target = COMBINE_BUFFER; + else + pass->target = pass->object->get_target_buffer(); + + pass->textures[0] = pass->object->get_texture_buffer(); + } + else + pass->target.append(pass->effect->buffer_id); + + for (int i = 0; i < 8; i++) + { + if (this->binds[i].empty()) + continue; + + if (this->binds[i] == "previous") + pass->textures[i] = pass->object->get_texture_buffer(); + else + pass->textures[i] = this->binds[i] + pass->effect->buffer_id; + } + + if (this->target == "previous") + pass->object->swap_buffer(); + + if (this->material != nullptr) + this->material->load(pass->object, MATERIAL_EFFECT, pass, false); + + break; + case MATERIAL_ONLY_PASS: + if (pass->combine) + pass->target = COMBINE_BUFFER; + else + { + pass->target = pass->object->get_target_buffer(); + pass->object->swap_buffer(); + } + [[fallthrough]]; + case MATERIAL_PASS: + pass->shader = this->shader; + break; + } +} + +void Pass::generate_pass(Engine *engine) +{ + this->rpass = new RenderPass(); + + this->rpass->combine = this->combine; + this->rpass->combine_blend = this->object->color_blend_mode; + + this->rpass->shader = new Shader(this->shader); + + auto rshader = this->rpass->shader; + + for (int i = 0; i < 8; i++) + { + if (this->textures[i].empty() && !rshader->textures[i].empty()) + this->textures[i] = rshader->textures[i]; + + if (this->textures[i].empty()) + continue; + + auto asset = asset_manager()->get_file(this->textures[i], TEXTURE); + + if (asset->type != ASSET_VOID) + this->rpass->textures[i] = new Texture(asset); + else + this->rpass->textures[i] = engine->get_framebuffer_texture(this->textures[i]); + } + + for (auto &uniform : rshader->uniforms) + { + // ex: g_Texture2Resolution (vec4) + // ^ index + if (uniform.uname.compare(0, 9, "g_Texture") == 0 && + uniform.uname.compare(10, 10, "Resolution") == 0) + { + auto index = uniform.uname[9] - '0'; + if (this->rpass->textures[index] != nullptr) + uniform.value = this->rpass->textures[index]->texture_resolution; + } + } + + for (auto &pass_uniform : this->uniforms) + { + for (auto &shader_uniform : rshader->uniforms) + { + if (shader_uniform.name.empty()) + continue; + if (pass_uniform.name == shader_uniform.name) + { + shader_uniform.value = pass_uniform.value; + break; + } + } + } + + for (auto &combo : rshader->combos) + { + if (combo.name == "AUDIOPROCESSING") + { + combo.value = 0; + break; + } + } + + for (auto &pass_combo : this->combos) + { + auto add = true; + for (auto &shader_combo : rshader->combos) + { + if (pass_combo.name == shader_combo.name) + { + shader_combo.value = pass_combo.value; + add = false; + break; + } + } + if (add) + rshader->combos.push_back(pass_combo); + } + + this->rpass->shader->compile(); + + this->rpass->target = engine->get_framebuffer(this->target); + + if (this->effect == nullptr) + { + if (this->object->passthrough || this->combine) + this->rpass->model = &this->object->model; + else + this->rpass->model = &this->object->ortho; + + if (this->object->passthrough) + this->rpass->robject = this->object->gl_object; + else + this->rpass->robject = this->object->gl_uv_object; + } + else + { + if (this->combine) + { + this->rpass->robject = this->object->gl_object; + this->rpass->model = &this->object->model; + } + else + { + this->rpass->robject = engine->default_object; + this->rpass->model = &engine->identity; + } + } +} + +void Pass::draw(Engine *engine) +{ + if (this->rpass != nullptr) + this->rpass->draw(engine); +} + +Pass::~Pass() +{ + if (this->material != nullptr) + delete this->material; + + if (this->rpass != nullptr) + delete this->rpass; +} + +void RenderPass::draw(Engine *engine) +{ + this->target->buffer->bind(); + this->shader->bind(engine, this->model); + + this->robject->bind(); + + for (int i = 0; i < this->shader->texture_count; i++) + { + if (this->textures[i] != nullptr) + this->textures[i]->bind(i); + } + + BlendMode blend; + + if (this->combine) + blend = this->combine_blend; + else + blend = NORMAL; + + switch (blend) + { + case NORMAL: + glBlendFunc(GL_ONE, GL_ZERO); + glColorMask(1.f, 1.f, 1.f, 1.f); + break; + case TRANSLUCENT: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColorMask(1.f, 1.f, 1.f, 0.f); + break; + } + + if (this->combine) + engine->view.center_scale(engine->context->width, engine->context->height); + else + engine->view.default_viewport(this->target->buffer->width, this->target->buffer->height); + + glDrawArrays(GL_TRIANGLES, 0, 6); + +#if 0 + engine->combine_buffer->buffer->blit(this->context->width, this->context->height); + engine->context->swap_buffers(); + std::this_thread::sleep_for(std::chrono::seconds(1)); +#endif +} + +RenderPass::~RenderPass() +{ + if (this->shader != nullptr) + delete this->shader; + + for (int i = 0; i < 8; i++) + { + if (this->textures[i] != nullptr && !this->textures[i]->wrapped) + delete this->textures[i]; + } +} + diff --git a/src/objects/pass.h b/src/objects/pass.h index 59dcdba..f7d5129 100644 --- a/src/objects/pass.h +++ b/src/objects/pass.h @@ -1,79 +1,86 @@ #ifndef _PASS_H #define _PASS_H -#include <jansson.h> +#include <glm/glm.hpp> +#include <jsoncpp/json/json.h> -#include "src/gl.h" -#include "types.h" +#include "context.h" +#include "engine.h" +#include "gl.h" +#include "shader.h" +#include "texture.h" -#include "../context.h" -#include "../shader.h" -#include "../texture.h" +#include "objects/object.h" -typedef enum { - EFFECT0_PASS, - EFFECT1_PASS, - MATERIAL_PASS, - MATERIAL_ONLY_PASS -} mri_pass_stage; +namespace Mauri +{ -typedef enum { - BACKGROUND, - BACKGROUND_ONLY, - TRANSFORM, - COMBINE, - PASS, -} mri_render_pass_stage; +enum PassStage +{ + EFFECT0_PASS, + EFFECT1_PASS, + MATERIAL_PASS, + MATERIAL_ONLY_PASS +}; -typedef enum { - TRANSLUCENT, - NORMAL -} mri_pass_blend_mode; +struct RenderPass; -struct mri_pass { - int id; +class Pass +{ + public: + Pass() {} + Pass(Json::Value root, PassStage stage); - mri_material *material; + ~Pass(); - mri_shader *shader; + std::string target; - mstr *textures[7]; + Object *object; + Effect *effect; - mstr *target; - mstr *binds[7]; + bool combine; - mri_pass_blend_mode blend; + void load(PassStage stage, Pass *pass); + void draw(Engine *engine); - mvec(mri_uniform) uniforms; - mvec(mri_combo) combos; -}; + void generate_pass(Engine *engine); -struct mri_render_pass { - mri_shader shader; + private: + std::string shader; - mri_object *object; - mri_effect *effect; + Material *material = nullptr; - mat4 model; - gl_object *gl_object; + std::array<std::string, 8> textures; + std::array<std::string, 8> binds; - mri_texture *textures[7]; - mri_framebuffer *target; + BlendMode blend; - bool combine; + std::vector<Uniform> uniforms; + std::vector<Combo> combos; - mri_pass_blend_mode combine_blend; + RenderPass *rpass = nullptr; +}; - mri_render_pass_stage stage; +struct RenderPass +{ + RenderPass() {}; + ~RenderPass(); - mvec(mri_uniform) uniforms; - mvec(mri_combo) combos; -}; + Shader *shader; + Framebuffer *target; + + mat4x4 *model; + RenderObject *robject; -void mri_pass_parse(mri_pass *pass, json_t *root, mri_pass_stage stage); -void mri_pass_load(mri_engine *engine, mri_pass *pass, mri_pass_stage stage, mri_render_pass *rpass); -void mri_pass_unload(mri_pass *pass); + bool combine; + + BlendMode combine_blend; + + std::array<Texture *, 8> textures = {0}; + + void draw(Engine *engine); +}; -void mri_render_pass_draw(mri_engine *engine, mri_render_pass *pass); +} // namespace Mauri #endif // _PASS_H diff --git a/src/objects/scene.c b/src/objects/scene.c deleted file mode 100644 index 3bcd6ac..0000000 --- a/src/objects/scene.c +++ /dev/null @@ -1,104 +0,0 @@ -#include <jansson.h> -#include <moro_vec.h> - -#include "scene.h" -#include "object.h" - -#include "../json.h" -#include "../log.h" -#include "../engine.h" -#include "src/objects/types.h" - -void mri_scene_parse(mri_scene *scene) { - mri_asset *scene_asset = mri_asset_manager_get(mstr_c("scene.json"), NONE); - - if (scene_asset->type == ASSET_VOID) { - log_error("err: failed to open scene.json"); - return; - } - - json_t *root = json_load_mstr(mri_asset_as_str(scene_asset)); - - json_t *camera = json_object_get(root, "camera"); - json_t *general = json_object_get(root, "general"); - json_t *ortho = json_object_get(general, "orthogonalprojection"); - - json_get_vec3(&scene->camera.center, camera, "center"); - json_get_vec3(&scene->camera.eye, camera, "eye"); - json_get_vec3(&scene->camera.up, camera, "up"); - - json_get_bool(&scene->clear_enabled, general, "clearenabled"); - - json_get_vec3((vec3 *)&scene->clear_color, general, "clearcolor"); - scene->clear_color[3] = 1.f; - - json_get_vec3(&scene->ambient_color, general, "ambientcolor"); - json_get_vec3(&scene->skylight_color, general, "skylightcolor"); - - json_get_float(&scene->width, ortho, "width"); - json_get_float(&scene->height, ortho, "height"); - - mvec_init(scene->objects); - - json_t *object; - json_t *objects = json_object_get(root, "objects"); - json_array(object, objects) { - mri_object_parse(mvec_next(scene->objects), object); - } - - json_decref(root); -} - -int mri_scene_framebuffer_count(mri_scene *scene) { - int total = 0; - - mri_object *object; - mvec_loop_ptr(scene->objects, object, i) { - mri_effect *effect; - mvec_loop_ptr(object->effects, effect, s) { - mri_pass *pass; - mvec_loop_ptr(effect->passes1, pass, j) { - if (pass->target) { - total++; - } - } - } - total += 2; - } - - return total; -} - -void mri_scene_load(mri_engine *engine, mri_scene *scene) { - engine->width = scene->width; - engine->height = scene->height; - - mri_object *object; - mvec_loop_ptr(scene->objects, object, i) { - int dep; - mvec_loop(object->deps, dep, s) { - mri_object *dep_object; - mvec_loop_ptr(scene->objects, dep_object, j) { - if (dep_object->id == dep) { - mri_object_load(engine, dep_object); - } - } - } - mri_object_load(engine, object); - } -} - -void mri_scene_draw(mri_engine *engine, mri_scene *scene) { - mri_object *object; - mvec_loop_ptr(scene->objects, object, i) { - mri_object_draw(engine, object); - } -} - -void mri_scene_unload(mri_scene *scene) { - mri_object *object; - mvec_loop_ptr(scene->objects, object, i) { - mri_object_unload(object); - } - mvec_free(scene->objects); -} diff --git a/src/objects/scene.cc b/src/objects/scene.cc new file mode 100644 index 0000000..5c00147 --- /dev/null +++ b/src/objects/scene.cc @@ -0,0 +1,120 @@ +#include <istream> +#include <jsoncpp/json/json.h> +#include <sstream> + +#include "assets.h" +#include "json.h" +#include "log.h" + +#include "objects/scene.h" +#include "objects/pass.h" + +using namespace Mauri; + +Scene::Scene(std::string path) +{ + auto asset = asset_manager()->get_file(path, NONE); + + if (asset->type == ASSET_VOID) + { + log_error("err: failed to open scene.json"); + return; + } + + Json::Value root = asset->as_json(); + + auto camera = root["camera"]; + auto general = root["general"]; + auto ortho = general["orthogonalprojection"]; + + sscanf_vec3(json_user_value(camera.get("center", "")).asCString(), this->camera.center); + sscanf_vec3(json_user_value(camera.get("eye", "")).asCString(), this->camera.eye); + sscanf_vec3(json_user_value(camera.get("up", "")).asCString(), this->camera.up); + + this->clear_enabled = general.get("clearenabled", false).asBool(); + + sscanf_vec3(json_user_value(general.get("clearcolor", "")).asCString(), this->clear_color); + this->clear_color[3] = 1.f; + + this->width = json_user_value(ortho.get("width", 0.f)).asFloat(); + this->height = json_user_value(ortho.get("height", 0.f)).asFloat(); + + for (auto &object : root["objects"]) + { + this->objects.emplace_back(new Object(object)); + } +} + +Object *Scene::get_object_by_id(u32 id) +{ + for (auto &object : this->objects) + { + if (object->id == id) + { + return object; + } + } + + return nullptr; +} + +void Scene::load(Engine *engine) +{ + engine->view.width = this->width; + engine->view.height = this->height; + + for (auto &object : this->objects) + { + for (auto &dep : object->deps) + { + auto dep_object = this->get_object_by_id(dep); + if (dep_object) + dep_object->load(engine); + } + + object->load(engine); + } + + for (auto &object : this->objects) + { + for (auto &pass : object->passes) + pass->generate_pass(engine); + } +} + +void Scene::draw(Engine *engine) +{ + for (auto &object : this->objects) + { + object->draw(engine); + } +} + +Scene::~Scene() +{ + for (auto &object : this->objects) + { + delete object; + } +} + +s32 Scene::estimated_buffer_count() +{ + return 0; +// s32 total = 0; +// +// for (auto &object : this->objects) +// { +// for (auto &effect : object->effects) +// { +// for (auto &pass1 : effect->passes1) +// { +// if (!pass1->target.empty()) +// total++; +// } +// } +// total += 2; +// } +// +// return total; +} diff --git a/src/objects/scene.h b/src/objects/scene.h index 9514580..47aa19a 100644 --- a/src/objects/scene.h +++ b/src/objects/scene.h @@ -1,47 +1,54 @@ #ifndef _SCENE_H #define _SCENE_H -#include <cglm/cglm.h> -#include <moro_vec.h> +#include <glm/glm.hpp> -#include "types.h" -#include "object.h" +#include <vector> -#include "../assets.h" +#include "assets.h" +#include "engine.h" -struct mri_scene { - float width; - float height; +#include "objects/object.h" - struct { - vec3 center; - vec3 eye; - vec3 up; +using namespace glm; - float farz; - float nearz; - float fov; - } camera; +namespace Mauri +{ - bool clear_enabled; +class Scene +{ + public: + Scene(std::string path); + ~Scene(); - vec4 clear_color; - vec3 ambient_color; - vec3 skylight_color; + f32 width; + f32 height; - bool bloom; + bool clear_enabled; + vec4 clear_color; - float bloom_strength; - float bloom_threshold; + void load(Engine *engine); + void draw(Engine *engine); - mvec(mri_object) objects; -}; + s32 estimated_buffer_count(); + + Object *get_object_by_id(u32 id); + + std::vector<Object *> objects; -void mri_scene_parse(mri_scene *scene); -void mri_scene_load(mri_engine *engine, mri_scene *scene); -void mri_scene_draw(mri_engine *engine, mri_scene *scene); -void mri_scene_unload(mri_scene *scene); + private: + struct + { + vec3 center; + vec3 eye; + vec3 up; + + float farz; + float nearz; + float fov; + } camera; +}; -int mri_scene_framebuffer_count(mri_scene *scene); +} // namespace Mauri #endif // _SCENE_H diff --git a/src/objects/types.h b/src/objects/types.h deleted file mode 100644 index 76fd8eb..0000000 --- a/src/objects/types.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _TYPES_H -#define _TYPES_H - -typedef struct mri_scene mri_scene; -typedef struct mri_object mri_object; -typedef struct mri_model mri_model; -typedef struct mri_material mri_material; -typedef struct mri_pass mri_pass; -typedef struct mri_effect mri_effect; - -typedef struct mri_render_pass mri_render_pass; - -typedef struct mri_engine mri_engine; - -#endif // _TYPES_H diff --git a/src/shader.c b/src/shader.c deleted file mode 100644 index a89eebc..0000000 --- a/src/shader.c +++ /dev/null @@ -1,306 +0,0 @@ -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <jansson.h> -#include <glad/gl.h> - -#include <moro.h> - -#include "moro_str.h" -#include "shader.h" -#include "assets.h" -#include "json.h" -#include "log.h" -#include "gl.h" -#include "engine.h" - -#include "objects/object.h" - -static const char *gl_compat = - "#define highp\n" - "#define mediump\n" - "#define lowp\n" - "#define mul(x, y) (y * x)\n" - "#define frac fract\n" - "#define CAST2(x) vec2(x)\n" - "#define CAST3(x) vec3(x)\n" - "#define CAST4(x) vec4(x)\n" - "#define CAST3X3(x) (mat3(x))\n" - "#define saturate(x) (clamp(x, 0.0, 1.0))\n" - "#define texSample2D texture2D\n" - "#define texSample2DLod texture2DLod\n" - "#define texture2DLod texture2D\n" - "#define atan2 atan\n" - "#define ddx dFdx\n" - "#define ddy(x) dFdy(-(x))\n" - "#define GLSL 1\n"; - -static const char *version = "#version 120\n"; - -static mstr *build_shader_source(mri_shader *shader, mstr *isource, bool root) { - mstr *source = mstr_from("\0"); - - // Append compatablity macros once per shader - if (root) mstr_cat(source, mstr_c(gl_compat)); - - mstr line = {0}; - - while (mstr_get_line(isource, &line)) { - bool cut_line = false; - - if (mstr_eqn(&line, mstr_c("#include"), 8)) { - mstr inc_name = mstr_substr(&line, mstr_find(&line, '"') + 1, mstr_rfind(&line, '"')); - - mri_asset *inc_asset = mri_asset_manager_get(&inc_name, SHADER); - mstr *inc = build_shader_source(shader, mri_asset_as_str(inc_asset), false); - - mstr_cat(source, inc); - mstr_free(inc); - - cut_line = true; - } else if (mstr_eqn(&line, mstr_c("uniform"), 7)) { - mri_uniform *u = mvec_next(shader->uniforms); - - mstr type = mstr_substr(&line, mstr_find(&line, ' ') + 1, mstr_len(&line)); - - if (mstr_eqn(&type, mstr_c("float"), 5)) u->type = TYPE_FLOAT; - else if (mstr_eqn(&type, mstr_c("vec4"), 4)) u->type = TYPE_VEC4; - else if (mstr_eqn(&type, mstr_c("vec3"), 4)) u->type = TYPE_VEC3; - else if (mstr_eqn(&type, mstr_c("vec2"), 4)) u->type = TYPE_VEC2; - else if (mstr_eqn(&type, mstr_c("mat4"), 4)) u->type = TYPE_MAT4; - else if (mstr_eqn(&type, mstr_c("sampler2D"), 9)) { - u->type = TYPE_SAMPLER2D; - u->value[0] = (float)shader->texture_count++; - } - - u->uname = mstr_substr_copy(&type, mstr_find(&type, ' ') + 1, mstr_find(&type, ';')); - - ptrdiff_t combo_start = mstr_find(&line, '/'); - - if (combo_start >= 0) { - mstr combo_str = mstr_substr(&line, combo_start + 2, mstr_len(&line)); - - json_t *root = json_load_mstr(&combo_str); - - if (!root) { - log_error("err: failed to load the combo for (%.*s)", MSTR_PRINTF(u->uname)); - continue; - } - - // If material is present, it's always used to reference this - // uniform for setting its values. If material is not present, - // label is used. - json_t *label = json_object_get(root, "label"); - json_t *material = json_object_get(root, "material"); - - if (json_is_string(material)) { - u->name = mstr_clone(mstr_c(json_string_value(material))); - } else if (json_is_string(label)) { - u->name = mstr_clone(mstr_c(json_string_value(label))); - } - - switch (u->type) { - case TYPE_SAMPLER2D: { - int index = (int)u->value[0]; - - json_t *texture = json_object_get(root, "default"); - if (json_is_string(texture)) { - shader->textures[index] = mstr_from(json_string_value(texture)); - } - - // NOTE: I assume the only case where this is wrong would - // be if "combo" is present and not "default". - json_t *combo = json_object_get(root, "combo"); - if (json_is_string(combo)) { - mri_combo c = { - .name = mstr_from(json_string_value(combo)), - .value = 1 - }; - mvec_push(shader->combos, c); - } - - break; - } - case TYPE_FLOAT: - json_get_float(&u->value[0], root, "default"); - break; - case TYPE_VEC4: - json_get_vec4(&u->value, root, "default"); - break; - case TYPE_VEC3: - json_get_vec3((vec3 *)&u->value, root, "default"); - break; - case TYPE_VEC2: - json_get_vec2((vec2 *)&u->value, root, "default"); - break; - case TYPE_MAT4: - default: - memset(&u->value, 0, sizeof(vec4)); - break; - } - - json_decref(root); - } - } else if (mstr_eqn(&line, mstr_c("// [COMBO]"), 10)) { - mstr combo = mstr_substr(&line, mstr_find(&line, '{'), mstr_len(&line)); - - json_t *root = json_load_mstr(&combo); - - if (!root) { - log_error("%s", "err: failed to load [COMBO]"); - continue; - } - - json_t *_combo = json_object_get(root, "combo"); - json_t *_value = json_object_get(root, "default"); - - if (json_is_string(_combo) && json_is_integer(_value)) { - mri_combo c = { - .name = mstr_from(json_string_value(_combo)), - .value = json_integer_value(_value) - }; - - mvec_push(shader->combos, c); - } - - json_decref(root); - } - - if (!cut_line) { - mstr_cat(source, &line); - mstr_cat(source, mstr_c("\n")); - } - } - - return source; -} - -static mstr *mri_combo_to_str(mri_combo *combo) { - return mstr_sprintf("#define %.*s %i\n", (int)combo->name->len, - combo->name->ptr, combo->value); -} - -void mri_shader_compile(mri_shader *shader) { - mstr *vs_source = mstr_clone(shader->vs_source); - mstr *fs_source = mstr_clone(shader->fs_source); - - mri_combo *combo; - mvec_loop_ptr(shader->combos, combo, i) { - mstr *combo_str = mri_combo_to_str(combo); - mstr_prepend(vs_source, combo_str); - mstr_prepend(fs_source, combo_str); - mstr_free(combo_str); - } - - mstr_prepend(vs_source, mstr_c(version)); - mstr_prepend(fs_source, mstr_c(version)); - - char *vs_source_c = mstr_to_cstr(vs_source); - char *fs_source_c = mstr_to_cstr(fs_source); - - create_gl_shader(&shader->gl_shader, vs_source_c, fs_source_c); - - free(vs_source_c); - free(fs_source_c); - - mstr_free(vs_source); - mstr_free(fs_source); -} - -void mri_shader_load(mri_shader *shader, mstr *name) { - mvec_init(shader->uniforms); - mvec_init(shader->combos); - - mri_asset *vs_asset = mri_asset_manager_get(name, VERTEX_SHADER); - - if (vs_asset->type != ASSET_VOID) { - shader->vs_source = build_shader_source(shader, mri_asset_as_str(vs_asset), true); - } else { - log_error("failed to load shader file (%.*s.vert)", MSTR_PRINTF(name)); - } - - mri_asset *fs_asset = mri_asset_manager_get(name, FRAGMENT_SHADER); - - if (fs_asset->type != ASSET_VOID) { - shader->fs_source = build_shader_source(shader, mri_asset_as_str(fs_asset), true); - } else { - log_error("failed to load shader file (%.*s.frag)", MSTR_PRINTF(name)); - } -} - -void mri_uniform_set(mri_shader *shader, mri_uniform *uniform) { - if (!uniform->loc) { - char *name = mstr_to_cstr(uniform->uname); - uniform->loc = get_uniform_loc(name, &shader->gl_shader); - free(name); - } - - set_gl_uniform(uniform->loc, uniform->type, uniform->value); -} - -void mri_shader_bind(mri_engine *engine, mri_shader *shader, mat4 mvp) { - bind_gl_shader(&shader->gl_shader); - - mri_uniform *uniform; - mvec_loop_ptr(shader->uniforms, uniform, i) { - if (mstr_find(uniform->uname, '[') >= 0) - continue; - mri_uniform_set(shader, uniform); - //printf("%.*s %f %f %f %f %i\n", uniform->uname->len, uniform->uname->ptr, - // uniform->value[0], uniform->value[1], uniform->value[2], uniform->value[3], uniform->type); - } - - set_gl_uniform_named("g_Time", &shader->gl_shader, TYPE_FLOAT, &engine->time); - - set_gl_uniform_named("g_TexelSize", &shader->gl_shader, TYPE_VEC2, engine->texel_size); - set_gl_uniform_named("g_TexelSizeHalf", &shader->gl_shader, TYPE_VEC2, - engine->texel_half_size); - - mat4 _mvp, _inverse; - - if (!mvp) - glm_mat4_identity(_mvp); - else - glm_mat4_copy(mvp, _mvp); - - glm_mat4_inv(_mvp, _inverse); - - set_gl_uniform_named("g_ModelViewProjectionMatrix", - &shader->gl_shader, TYPE_MAT4, (float *)_mvp); - - set_gl_uniform_named("g_ModelViewProjectionMatrixInverse", - &shader->gl_shader, TYPE_MAT4, (float *)_inverse); -} - -void mri_shader_unload(mri_shader *shader) { - for (int i = 0; i < 7; i++) { - if (shader->textures[i]) mstr_free(shader->textures[i]); - } - - if (shader->vs_source) - mstr_free(shader->vs_source); - if (shader->fs_source) - mstr_free(shader->fs_source); - - mri_uniform *uniform; - mvec_loop_ptr(shader->uniforms, uniform, i) { - if (uniform->uname) - mstr_free(uniform->uname); - if (uniform->name) - mstr_free(uniform->name); - } - - mri_combo *combo; - mvec_loop_ptr(shader->combos, combo, i) { - if (combo->name) - mstr_free(combo->name); - } - - mvec_free(shader->uniforms); - mvec_free(shader->combos); - - //destroy_gl_shader(&shader->gl_shader); -} - - - diff --git a/src/shader.cc b/src/shader.cc new file mode 100644 index 0000000..6a27954 --- /dev/null +++ b/src/shader.cc @@ -0,0 +1,249 @@ +#include <glad/gl.h> +#include <glm/ext.hpp> +#include <glm/fwd.hpp> +#include <glm/matrix.hpp> +#include <jsoncpp/json/json.h> + +#include <fmt/core.h> + +#include "assets.h" +#include "gl.h" +#include "json.h" +#include "log.h" +#include "shader.h" + +#include "objects/object.h" + +static const char *gl_compat = "#define highp\n" + "#define mediump\n" + "#define lowp\n" + "#define mul(x, y) (y * x)\n" + "#define frac fract\n" + "#define CAST2(x) vec2(x)\n" + "#define CAST3(x) vec3(x)\n" + "#define CAST4(x) vec4(x)\n" + "#define CAST3X3(x) (mat3(x))\n" + "#define saturate(x) (clamp(x, 0.0, 1.0))\n" + "#define texSample2D texture2D\n" + "#define texSample2DLod texture2DLod\n" + "#define texture2DLod texture2D\n" + "#define atan2 atan\n" + "#define ddx dFdx\n" + "#define ddy(x) dFdy(-(x))\n" + "#define GLSL 1\n"; + +static const char *version = "#version 120\n"; + +using namespace Mauri; + +std::string Shader::build_shader_source(std::string isource, bool root) +{ + std::stringstream stream(isource), source; + + if (root) source << gl_compat; + + for (std::string line; std::getline(stream, line);) + { + auto cut_line = false; + + if (line.compare(0, 8, "#include") == 0) + { + auto open = line.find('"') + 1; + auto include_name = line.substr(open, line.rfind('"') - open); + + auto asset = asset_manager()->get_file(include_name, SHADER); + auto include_source = this->build_shader_source(asset->as_string(), false); + + source << include_source; + + cut_line = true; + } + else if (line.compare(0, 7, "uniform") == 0) + { + auto type_string = line.substr(line.find(' ') + 1); + + auto type = TYPE_INVALID; + auto value = vec4(0.f); + + if (type_string.compare(0, 5, "float") == 0) + type = TYPE_FLOAT; + else if (type_string.compare(0, 4, "vec4") == 0) + type = TYPE_VEC4; + else if (type_string.compare(0, 4, "vec3") == 0) + type = TYPE_VEC3; + else if (type_string.compare(0, 4, "vec2") == 0) + type = TYPE_VEC2; + else if (type_string.compare(0, 4, "mat4") == 0) + type = TYPE_MAT4; + else if (type_string.compare(0, 9, "sampler2D") == 0) + { + type = TYPE_SAMPLER2D; + value[0] = this->texture_count++; + } + + auto open = type_string.find(' ') + 1; + auto uname = type_string.substr(open, type_string.find(';') - open); + + auto name = std::string(""); + + auto combo_start = line.find("// "); + + if (combo_start != std::string::npos) + { + auto combo_string = line.substr(combo_start + 2); + + Json::Value root; + + std::stringstream(combo_string) >> root; + + // If material is present, it's always used to reference this + // uniform for setting its values. If material is not present, + // label is used. + auto label = root["label"]; + auto material = root["material"]; + + if (!material.isNull()) + name = material.asString(); + else if (!label.isNull()) + name = label.asString(); + + switch (type) + { + case TYPE_SAMPLER2D: { + auto index = (int)value[0]; + + auto _default = root["default"]; + + if (!_default.isNull()) + this->textures[index] = _default.asString(); + + auto combo = root["combo"]; + + if (!combo.isNull()) + this->combos.emplace_back((Combo) { .name = combo.asString(), .value = 1 }); + + break; + } + case TYPE_FLOAT: { + auto _value = root["default"]; + if (_value.isString()) + sscanf_vec2(_value.asCString(), value); + else if (_value.isNumeric()) + value[0] = _value.asFloat(); + break; + } + case TYPE_VEC4: + sscanf_vec4(root.get("default", "").asCString(), value); + break; + case TYPE_VEC3: + sscanf_vec3(root.get("default", "").asCString(), value); + break; + case TYPE_VEC2: + sscanf_vec2(root.get("default", "").asCString(), value); + break; + case TYPE_MAT4: + case TYPE_INVALID: + value = glm::vec4(0.f); + break; + } + } + + this->uniforms.emplace_back((Uniform){type, name, uname, value}); + } + else if (line.compare(0, 10, "// [COMBO]") == 0) + { + auto open = line.find('{'); + auto combo = line.substr(open, line.length() - open); + + Json::Value root; + + std::stringstream(combo) >> root; + + auto _combo = root["combo"].asString(); + auto _value = root["default"].asInt(); + + this->combos.emplace_back((Combo) { _combo, _value }); + } + + if (!cut_line) source << line << "\n"; + } + + return source.str(); +} + +std::string Combo::to_string() +{ + return fmt::format("#define {} {}\n", this->name, this->value); +} + +void Shader::compile() +{ + auto vs_source = this->vs_source; + auto fs_source = this->fs_source; + + for (auto &combo : this->combos) + { + vs_source.insert(0, combo.to_string()); + fs_source.insert(0, combo.to_string()); + } + + vs_source.insert(0, version); + fs_source.insert(0, version); + + this->rshader = new RenderShader(vs_source, fs_source); +} + +Shader::Shader(std::string name) +{ + auto vs_asset = asset_manager()->get_file(name, VERTEX_SHADER); + auto fs_asset = asset_manager()->get_file(name, FRAGMENT_SHADER); + + if (vs_asset->type != ASSET_VOID) + this->vs_source = this->build_shader_source(vs_asset->as_string(), true); + else + log_error("failed to load shader file (%s)", name.c_str()); + + if (fs_asset->type != ASSET_VOID) + this->fs_source = this->build_shader_source(fs_asset->as_string(), true); + else + log_error("failed to load shader file (%s)", name.c_str()); +} + +void Shader::bind(Engine *engine, mat4x4 *model) +{ + this->rshader->bind(); + + + for (auto &uniform : this->uniforms) + { + if (uniform.uname.find('[') != std::string::npos) + continue; + //std::cout << uniform.uname << " " << uniform.value[0] << "\n"; + this->rshader->set_uniform(uniform.uname, uniform.type, glm::value_ptr(uniform.value)); + } + + this->rshader->set_uniform("g_Time", TYPE_FLOAT, &engine->time); + + this->rshader->set_uniform("g_TexelSize", TYPE_VEC2, glm::value_ptr(engine->view.texel_size)); + this->rshader->set_uniform("g_TexelSizeHalf", TYPE_VEC2, glm::value_ptr(engine->view.texel_half_size)); + + mat4x4 _model, _inverse; + + if (!model) + _model = mat4x4(1.f); + else + _model = *model; + + _inverse = glm::inverse(_model); + + this->rshader->set_uniform("g_ModelViewProjectionMatrix", TYPE_MAT4, glm::value_ptr(_model)); + this->rshader->set_uniform("g_ModelViewProjectionMatrixInverse", TYPE_MAT4, glm::value_ptr(_inverse)); +} + +Shader::~Shader() +{ + if (this->rshader) + { + delete this->rshader; + } +} diff --git a/src/shader.h b/src/shader.h index 30c9e6c..bd4a80c 100644 --- a/src/shader.h +++ b/src/shader.h @@ -1,44 +1,60 @@ #ifndef _SHADER_H #define _SHADER_H -#include <moro_str.h> +#include <glm/glm.hpp> -#include "objects/types.h" -#include "texture.h" #include "gl.h" +#include "texture.h" + +using namespace glm; + +namespace Mauri +{ + +struct Uniform +{ + UniformType type; + + std::string name; + std::string uname; + + vec4 value; +}; + +struct Combo +{ + std::string name; + int value; + std::string to_string(); +}; + +class Engine; + +class Shader +{ + public: + Shader(std::string name); + ~Shader(); -typedef struct mri_engine mri_engine; + std::vector<Uniform> uniforms; + std::vector<Combo> combos; -typedef struct { - uniform_type type; - mstr *name; - mstr *uname; - vec4 value; - unsigned int loc; -} mri_uniform; + std::array<std::string, 8> textures; -typedef struct { - mstr *name; - int value; -} mri_combo; + int texture_count = 0; -typedef struct { - mstr *vs_source; - mstr *fs_source; - - gl_shader gl_shader; + void bind(Engine *engine, mat4x4 *model); + void compile(); - int texture_count; + private: + std::string vs_source; + std::string fs_source; - mstr *textures[7]; + RenderShader *rshader = nullptr; - mvec(mri_combo) combos; - mvec(mri_uniform) uniforms; -} mri_shader; + std::string build_shader_source(std::string isource, bool root); +}; -void mri_shader_load(mri_shader *shader, mstr *name); -void mri_shader_compile(mri_shader *shader); -void mri_shader_bind(mri_engine *engine, mri_shader *shader, mat4 mvp); -void mri_shader_unload(mri_shader *shader); +} // namespace Mauri #endif // _SHADER_H diff --git a/src/texture.c b/src/texture.c deleted file mode 100644 index 42f0830..0000000 --- a/src/texture.c +++ /dev/null @@ -1,199 +0,0 @@ -#define STB_IMAGE_IMPLEMENTATION -#include <stb_image.h> -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include <stb_image_write.h> - -#include <lz4.h> -#include <moro.h> -#include <glad/gl.h> - -#include "util.h" -#include "texture.h" -#include "assets.h" -#include "gl.h" -#include "log.h" -#include "engine.h" - -static unsigned char *load_image_rgba(unsigned char *data, int len) { - int w, h, channels; - return stbi_load_from_memory(data, len, &w, &h, &channels, 4); -} - -//static uint32_t rgba_to_brga(uint32_t x) { -// return ((x) & 0xff00ff00) | -// ((x >> 16) & 0xFF) | -// ((x << 16) & 0xFF0000); -//} -// -//static uint32_t argb_to_brga(uint32_t x) { -// return ((x & 0xFF000000) >> 24) | //______AA -// ((x & 0x00FF0000) >> 8) | //____RR__ -// ((x & 0x0000FF00) << 8) | //__GG____ -// ((x & 0x000000FF) << 24); -//} - -mri_texture *mri_texture_load(mri_engine *engine, mri_asset *asset) { - mri_texture *previous_texture; - mvec_loop_ptr(engine->texture_cache, previous_texture, i) { - if (previous_texture->hash == asset->hash) { - return previous_texture; - } - } - - mri_texture *t = mvec_next(engine->texture_cache); - - t->hash = asset->hash; - - //mstr type = mri_asset_str(asset, 9); - //mstr itype = mri_asset_str(asset, 9); - mri_asset_seek(asset, 18); - - t->format = (mri_texture_format)mri_asset_value(asset, unsigned int); - - t->flags.integer = mri_asset_value(asset, unsigned int); - - t->texture_width = mri_asset_value(asset, unsigned int); - t->texture_height = mri_asset_value(asset, unsigned int); - - t->width = mri_asset_value(asset, unsigned int); - t->height = mri_asset_value(asset, unsigned int); - - t->texture_resolution[0] = t->texture_width; - t->texture_resolution[1] = t->texture_height; - t->texture_resolution[2] = t->width; - t->texture_resolution[3] = t->height; - - mri_asset_seek(asset, 4); - - mstr container = mri_asset_str(asset, 9); - - int iformat = -1; - - if (mstr_eqn(&container, mstr_c("TEXB0003"), 8)) { - t->version = TEXB0003; - mri_asset_seek(asset, 4); - iformat = mri_asset_value(asset, unsigned int); - } else if (mstr_eqn(&container, mstr_c("TEXB0002"), 8)) { - t->version = TEXB0002; - mri_asset_seek(asset, 4); - } else if (mstr_eqn(&container, mstr_c("TEXB0001"), 8)) { - t->version = TEXB0001; - mri_asset_seek(asset, 4); - } else { - log_error("%s", "unknown texture version"); - } - - int mm_count = mri_asset_value(asset, int); - - create_gl_texture(&t->texture, t->flags.map.NO_INTERPOLATION, - t->flags.map.CLAMP_UV, mm_count); - - for (int i = 0; i < mm_count; i++) { - unsigned int mwidth = mri_asset_value(asset, unsigned int); - unsigned int mheight = mri_asset_value(asset, unsigned int); - - bool lz4_compressed = false; - unsigned int decompressed_byte_count = 0; - - if (t->version == TEXB0002 || t->version == TEXB0003) { - lz4_compressed = mri_asset_value(asset, unsigned int) == 1; - decompressed_byte_count = mri_asset_value(asset, unsigned int); - } - - unsigned int byte_count = mri_asset_value(asset, unsigned int); - - if (!lz4_compressed) { - decompressed_byte_count = byte_count; - } - - unsigned char *buffer = mri_asset_ptr(asset); - - if (lz4_compressed) { - char *decompressed = (char *)malloc(decompressed_byte_count); - LZ4_decompress_safe((char *)buffer, decompressed, byte_count, decompressed_byte_count); - buffer = (unsigned char *)decompressed; - } - - if (iformat == -1) { - switch (t->format) { - case RGBA8888: { - upload_gl_texture(&t->texture, buffer, mwidth, mheight, i, GL_RGBA); - break; - } - case DXT5: - log_warn("%s", "DXT5 Texture"); - break; - case DXT1: - log_warn("%s", "DXT1 Texture"); - break; - case DXT3: - log_warn("%s", "DXT3 Texture"); - break; - case RG88: - upload_gl_texture(&t->texture, buffer, mwidth, mheight, i, GL_RG); - log_warn("%s", "RG88 Texture"); - break; - case R8: - upload_gl_texture(&t->texture, buffer, mwidth, mheight, i, GL_RED); - log_warn("%s", "RG8 Texture"); - break; - default: - log_warn("%s", "Uknown Texture"); - break; - } - } else { - unsigned char *data = load_image_rgba(buffer, decompressed_byte_count); - upload_gl_texture(&t->texture, data, mwidth, mheight, i, GL_RGBA); - free(data); - } - - if (lz4_compressed) { - free(buffer); - } - - mri_asset_seek(asset, byte_count); - } - - asset->pos = 0; - - return t; -} - -void mri_texture_unload(mri_texture *t) { - destroy_gl_texture(&t->texture); - free(t); -} - -void mri_texture_bind(mri_texture *t, int index) { - bind_gl_texture(&t->texture, index); -} - -void mri_framebuffer_create(mri_framebuffer *buffer, mstr *name, float width, float height, float scale) { - buffer->name = mstr_clone(name); - - buffer->width = width / scale; - buffer->height = height / scale; - - buffer->texture.texture_resolution[0] = buffer->width; - buffer->texture.texture_resolution[1] = buffer->height; - buffer->texture.texture_resolution[2] = buffer->width; - buffer->texture.texture_resolution[3] = buffer->height; -} - -void mri_framebuffer_load(mri_framebuffer *buffer) { - create_gl_framebuffer(&buffer->buffer, buffer->width, buffer->height); - buffer->texture.texture.id = buffer->buffer.texture.id; -} - -void mri_framebuffer_unload(mri_framebuffer *buffer) { - mstr_free(buffer->name); - destroy_gl_framebuffer(&buffer->buffer); - //free(buffer); -} - -void mri_framebuffer_bind(mri_framebuffer *buffer) { - bind_gl_framebuffer(&buffer->buffer); -} - - - diff --git a/src/texture.cc b/src/texture.cc new file mode 100644 index 0000000..c47352b --- /dev/null +++ b/src/texture.cc @@ -0,0 +1,201 @@ +#define STB_IMAGE_IMPLEMENTATION +extern "C" +{ +#include <stb_image.h> +} + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include <stb_image_write.h> + +#include <s3tc.h> + +#include <glad/gl.h> +#include <lz4.h> + +#include "assets.h" +#include "engine.h" +#include "gl.h" +#include "log.h" +#include "texture.h" +#include "util.h" + +using namespace Mauri; + +static byte *load_image_rgba(byte *data, s32 len) +{ + s32 w, h, channels; + return stbi_load_from_memory(data, len, &w, &h, &channels, 4); +} + +Texture::Texture(Asset *asset) +{ + this->hash = asset->hash; + + // auto type = asset->read_str(9); + // auto itype = asset->read_str(9); + + asset->seek(18); + + this->format = (TextureFormat)asset->read<u32>(); + + this->flags.integer = asset->read<u32>(); + + this->texture_width = asset->read<u32>(); + this->texture_height = asset->read<u32>(); + + this->width = asset->read<u32>(); + this->height = asset->read<u32>(); + + this->texture_resolution[0] = this->texture_width; + this->texture_resolution[1] = this->texture_height; + this->texture_resolution[2] = this->width; + this->texture_resolution[3] = this->height; + + asset->seek(4); + + auto container = asset->reads(9); + + auto iformat = -1; + + if (container.compare(0, 8, "TEXB0003") == 0) + { + this->version = TEXB0003; + asset->seek(4); + iformat = asset->read<u32>(); + } + else if (container.compare(0, 8, "TEXB0002") == 0) + { + this->version = TEXB0002; + asset->seek(4); + } + else if (container.compare(0, 8, "TEXB0001") == 0) + { + this->version = TEXB0001; + asset->seek(4); + } + else + { + log_error("%s", "unknown texture version"); + } + + auto mm_count = asset->read<u32>(); + + this->texture = new RenderTexture(this->flags.map.NO_INTERPOLATION, this->flags.map.CLAMP_UV, mm_count); + + for (int i = 0; i < mm_count; i++) + { + auto mwidth = asset->read<u32>(); + auto mheight = asset->read<u32>(); + + auto lz4_compressed = false; + auto decompressed_byte_count = 0; + + if (this->version == TEXB0002 || this->version == TEXB0003) + { + lz4_compressed = asset->read<u32>() == 1; + decompressed_byte_count = asset->read<u32>(); + } + + auto byte_count = asset->read<u32>(); + + if (!lz4_compressed) + decompressed_byte_count = byte_count; + + auto buffer = asset->ptr(); + + if (lz4_compressed) + { + auto decompressed = new char[decompressed_byte_count]; + LZ4_decompress_safe(reinterpret_cast<char *>(buffer), decompressed, + byte_count, decompressed_byte_count); + buffer = reinterpret_cast<byte *>(decompressed); + } + + if (iformat == -1) + { + switch (this->format) + { + case RGBA8888: + this->texture->upload(buffer, mwidth, mheight, i, GL_RGBA, true); + break; + case DXT5: { + auto decompressed = new u32[decompressed_byte_count]; + BlockDecompressImageDXT5(mwidth, mheight, buffer, decompressed); + this->texture->upload(reinterpret_cast<byte *>(decompressed), + mwidth, mheight, i, GL_RGBA, true, true); + delete[] decompressed; + log_warn("%s", "DXT5 Texture"); + break; + } + case DXT1: { + auto decompressed = new u32[decompressed_byte_count * 2]; + BlockDecompressImageDXT1(mwidth, mheight, buffer, decompressed); + this->texture->upload(reinterpret_cast<byte *>(decompressed), + mwidth, mheight, i, GL_RGBA, true, true); + delete[] decompressed; + log_warn("%s", "DXT1 Texture"); + break; + } + case DXT3: + log_warn("%s", "DXT3 Texture"); + break; + case RG88: + this->texture->upload(buffer, mwidth, mheight, i, GL_RG, true); + log_warn("%s", "RG88 Texture"); + break; + case R8: + this->texture->upload(buffer, mwidth, mheight, i, GL_RED, true); + log_warn("%s", "RG8 Texture"); + break; + default: + log_warn("%s", "Uknown Texture"); + break; + } + } + else + { + auto data = load_image_rgba(buffer, decompressed_byte_count); + this->texture->upload(data, mwidth, mheight, i, GL_RGBA, true); + free(data); + } + + if (lz4_compressed) + delete[] buffer; + + asset->seek(byte_count); + } + + asset->pos = 0; +} + +Texture::~Texture() +{ + if (!this->wrapped) + delete this->texture; +} + +void Texture::bind(s32 index) +{ + this->texture->bind(index); +} + +Framebuffer::Framebuffer(f32 width, f32 height, f32 scale) +{ + this->buffer = new RenderFramebuffer(width, height, scale); + + this->texture = new Texture(); + this->texture->wrapped = true; + + this->texture->texture = this->buffer->texture; + + this->texture->texture_resolution[0] = this->buffer->width; + this->texture->texture_resolution[1] = this->buffer->height; + this->texture->texture_resolution[2] = this->buffer->width; + this->texture->texture_resolution[3] = this->buffer->height; +} + +Framebuffer::~Framebuffer() +{ + delete this->texture; + delete this->buffer; +} diff --git a/src/texture.h b/src/texture.h index 316d7fd..d70b423 100644 --- a/src/texture.h +++ b/src/texture.h @@ -1,72 +1,89 @@ #ifndef _TEXTURE_H #define _TEXTURE_H -#include <stdbool.h> -#include <cglm/cglm.h> +#include <glm/glm.hpp> -#include "gl.h" #include "assets.h" +#include "gl.h" +#include "util.h" + +using namespace glm; + +namespace Mauri +{ +enum TextureFormat +{ + RGBA8888 = 0, + DXT5 = 4, + DXT3 = 6, + DXT1 = 7, + RG88 = 8, + R8 = 9 +}; + +enum TextureVersion +{ + TEXB0001, + TEXB0002, + TEXB0003 +}; -#include "objects/types.h" +struct TextureFlags +{ + bool NO_INTERPOLATION : 1; + bool CLAMP_UV : 1; + bool IS_GIF : 1; + u32 UNKNWON : 29; +}; -typedef enum { - RGBA8888 = 0, - DXT5 = 4, - DXT3 = 6, - DXT1 = 7, - RG88 = 8, - R8 = 9 -} mri_texture_format; +class Texture +{ + public: + Texture(Asset *asset); + Texture() + { + } -typedef enum { - TEXB0001, - TEXB0002, - TEXB0003 -} mri_texture_version; + ~Texture(); -typedef struct { - bool NO_INTERPOLATION:1; - bool CLAMP_UV:1; - bool IS_GIF:1; - unsigned int UNKNWON: 29; -} mri_texture_flags; + s32 width; + s32 height; -typedef struct { - int width, height; - int texture_width, texture_height; + bool wrapped = false; - unsigned long hash; + vec4 texture_resolution; - gl_texture texture; + RenderTexture *texture; - vec4 texture_resolution; + TextureFormat format; - union { - mri_texture_flags map; - unsigned int integer; - } flags; + void load(Asset *asset); + void bind(s32 index); - mri_texture_format format; - mri_texture_version version; -} mri_texture; + private: + s32 texture_width; + s32 texture_height; -typedef struct { - mstr *name; + u64 hash; - float width, height; + union { + TextureFlags map; + unsigned int integer; + } flags; - gl_framebuffer buffer; + TextureVersion version; +}; - mri_texture texture; -} mri_framebuffer; +class Framebuffer +{ + public: + Framebuffer(f32 width, f32 height, f32 scale); + ~Framebuffer(); -mri_texture *mri_texture_load(mri_engine *engine, mri_asset *asset); -void mri_texture_unload(mri_texture *texture); -void mri_texture_bind(mri_texture *t, int index); + Texture *texture; + RenderFramebuffer *buffer; +}; -void mri_framebuffer_create(mri_framebuffer *buffer, mstr *name, float width, float height, float scale); -void mri_framebuffer_load(mri_framebuffer *buffer); -void mri_framebuffer_unload(mri_framebuffer *buffer); -void mri_framebuffer_bind(mri_framebuffer *buffer); +} // namespace Mauri #endif // _TEXTURE_H diff --git a/src/util.c b/src/util.c deleted file mode 100644 index f71761b..0000000 --- a/src/util.c +++ /dev/null @@ -1,17 +0,0 @@ -#include <string.h> -#include <stdlib.h> -#include <stddef.h> - -#include "util.h" - -// http://www.cse.yorku.ca/~oz/hash.html -unsigned long djb2_hash(unsigned char *str) { - return 0; - unsigned long hash = 5381; - int c; - - while ((c = *str++)) - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - - return hash; -} diff --git a/src/util.cc b/src/util.cc new file mode 100644 index 0000000..ff341ef --- /dev/null +++ b/src/util.cc @@ -0,0 +1,20 @@ +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +namespace Mauri +{ + +int next_power_of_two(int n) +{ + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + return ++n; +}; + +} // namespace Mauri @@ -1,6 +1,25 @@ #ifndef _UTIL_H #define _UTIL_H -unsigned long djb2_hash(unsigned char *str); +#include <stdint.h> + +typedef uint32_t u32; +typedef int32_t s32; + +typedef uint64_t u64; +typedef int64_t s64; + +typedef uint8_t u8; +typedef int8_t s8; + +typedef float f32; +typedef double f64; + +typedef unsigned char byte; + +namespace Mauri +{ +extern s32 next_power_of_two(s32 n); +} #endif // _UTIL_H diff --git a/subprojects/cglm b/subprojects/cglm deleted file mode 160000 -Subproject 66f6bbde6c3786924025637edfc67c50c13aa73 diff --git a/subprojects/fmt b/subprojects/fmt new file mode 160000 +Subproject 05a28312cf55f885cbd12f42a1e4d15b1794b42 diff --git a/subprojects/glad/include/KHR/khrplatform.h b/subprojects/glad/include/KHR/khrplatform.h index 5b55ea2..dd22d92 100644 --- a/subprojects/glad/include/KHR/khrplatform.h +++ b/subprojects/glad/include/KHR/khrplatform.h @@ -119,7 +119,7 @@ * This follows the return type of the function and precedes the function * name in the function prototype. */ -#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC) +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) /* Win32 but not WinCE */ # define KHRONOS_APIENTRY __stdcall #else diff --git a/subprojects/glad/include/glad/gl.h b/subprojects/glad/include/glad/gl.h index 1f083c8..a7fe964 100644 --- a/subprojects/glad/include/glad/gl.h +++ b/subprojects/glad/include/glad/gl.h @@ -1,5 +1,5 @@ /** - * Loader generated by glad 2.0.0-beta on Wed May 13 00:01:36 2020 + * Loader generated by glad 2.0.0-beta on Thu Oct 1 10:15:45 2020 * * Generator: C/C++ * Specification: gl @@ -11,17 +11,17 @@ * Options: * - MX_GLOBAL = False * - ON_DEMAND = False - * - LOADER = False + * - LOADER = True * - ALIAS = False * - HEADER_ONLY = False * - DEBUG = False * - MX = False * * Commandline: - * --api='gl:core=3.2' --extensions='' c + * --api='gl:core=3.2' --extensions='' c --loader * * Online: - * http://glad.sh/#api=gl%3Acore%3D3.2&extensions=&generator=c&options= + * http://glad.sh/#api=gl%3Acore%3D3.2&extensions=&generator=c&options=LOADER * */ @@ -53,6 +53,7 @@ #endif #define GLAD_GL +#define GLAD_OPTION_GL_LOADER #ifdef __cplusplus extern "C" { @@ -977,70 +978,113 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #include <KHR/khrplatform.h> + typedef unsigned int GLenum; + typedef unsigned char GLboolean; + typedef unsigned int GLbitfield; + typedef void GLvoid; + typedef khronos_int8_t GLbyte; + typedef khronos_uint8_t GLubyte; + typedef khronos_int16_t GLshort; + typedef khronos_uint16_t GLushort; + typedef int GLint; + typedef unsigned int GLuint; + typedef khronos_int32_t GLclampx; + typedef int GLsizei; + typedef khronos_float_t GLfloat; + typedef khronos_float_t GLclampf; + typedef double GLdouble; + typedef double GLclampd; + typedef void *GLeglClientBufferEXT; + typedef void *GLeglImageOES; + typedef char GLchar; + typedef char GLcharARB; + #ifdef __APPLE__ typedef void *GLhandleARB; #else typedef unsigned int GLhandleARB; #endif + typedef khronos_uint16_t GLhalf; + typedef khronos_uint16_t GLhalfARB; + typedef khronos_int32_t GLfixed; + #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_intptr_t GLintptr; #else typedef khronos_intptr_t GLintptr; #endif + #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_intptr_t GLintptrARB; #else typedef khronos_intptr_t GLintptrARB; #endif + #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_ssize_t GLsizeiptr; #else typedef khronos_ssize_t GLsizeiptr; #endif + #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_ssize_t GLsizeiptrARB; #else typedef khronos_ssize_t GLsizeiptrARB; #endif + typedef khronos_int64_t GLint64; + typedef khronos_int64_t GLint64EXT; + typedef khronos_uint64_t GLuint64; + typedef khronos_uint64_t GLuint64EXT; + typedef struct __GLsync *GLsync; + struct _cl_context; + struct _cl_event; + typedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); + typedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); + typedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); + typedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); + typedef unsigned short GLhalfNV; + typedef GLintptr GLvdpauSurfaceNV; + typedef void (GLAD_API_PTR *GLVULKANPROCNV)(void); + #define GL_VERSION_1_0 1 GLAD_API_CALL int GLAD_GL_VERSION_1_0; #define GL_VERSION_1_1 1 @@ -2023,6 +2067,12 @@ GLAD_API_CALL int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr); GLAD_API_CALL int gladLoadGL( GLADloadfunc load); +#ifdef GLAD_GL + +GLAD_API_CALL int gladLoaderLoadGL(void); +GLAD_API_CALL void gladLoaderUnloadGL(void); + +#endif #ifdef __cplusplus } diff --git a/subprojects/glad/src/gl.c b/subprojects/glad/src/gl.c index 1fae27b..eb3e66a 100644 --- a/subprojects/glad/src/gl.c +++ b/subprojects/glad/src/gl.c @@ -890,6 +890,172 @@ int gladLoadGL( GLADloadfunc load) { +#ifdef GLAD_GL + +#ifndef GLAD_LOADER_LIBRARY_C_ +#define GLAD_LOADER_LIBRARY_C_ + +#include <stddef.h> +#include <stdlib.h> + +#if GLAD_PLATFORM_WIN32 +#include <windows.h> +#else +#include <dlfcn.h> +#endif + + +static void* glad_get_dlopen_handle(const char *lib_names[], int length) { + void *handle = NULL; + int i; + + for (i = 0; i < length; ++i) { +#if GLAD_PLATFORM_WIN32 + #if GLAD_PLATFORM_UWP + size_t buffer_size = (strlen(lib_names[i]) + 1) * sizeof(WCHAR); + LPWSTR buffer = (LPWSTR) malloc(buffer_size); + if (buffer != NULL) { + int ret = MultiByteToWideChar(CP_ACP, 0, lib_names[i], -1, buffer, buffer_size); + if (ret != 0) { + handle = (void*) LoadPackagedLibrary(buffer, 0); + } + free((void*) buffer); + } + #else + handle = (void*) LoadLibraryA(lib_names[i]); + #endif +#else + handle = dlopen(lib_names[i], RTLD_LAZY | RTLD_LOCAL); +#endif + if (handle != NULL) { + return handle; + } + } + + return NULL; +} + +static void glad_close_dlopen_handle(void* handle) { + if (handle != NULL) { +#if GLAD_PLATFORM_WIN32 + FreeLibrary((HMODULE) handle); +#else + dlclose(handle); +#endif + } +} + +static GLADapiproc glad_dlsym_handle(void* handle, const char *name) { + if (handle == NULL) { + return NULL; + } + +#if GLAD_PLATFORM_WIN32 + return (GLADapiproc) GetProcAddress((HMODULE) handle, name); +#else + return GLAD_GNUC_EXTENSION (GLADapiproc) dlsym(handle, name); +#endif +} + +#endif /* GLAD_LOADER_LIBRARY_C_ */ + +typedef void* (GLAD_API_PTR *GLADglprocaddrfunc)(const char*); +struct _glad_gl_userptr { + void *handle; + GLADglprocaddrfunc gl_get_proc_address_ptr; +}; + +static GLADapiproc glad_gl_get_proc(void *vuserptr, const char *name) { + struct _glad_gl_userptr userptr = *(struct _glad_gl_userptr*) vuserptr; + GLADapiproc result = NULL; + + if(userptr.gl_get_proc_address_ptr != NULL) { + result = GLAD_GNUC_EXTENSION (GLADapiproc) userptr.gl_get_proc_address_ptr(name); + } + if(result == NULL) { + result = glad_dlsym_handle(userptr.handle, name); + } + + return result; +} + +static void* _gl_handle = NULL; + +static void* glad_gl_dlopen_handle(void) { +#if GLAD_PLATFORM_APPLE + static const char *NAMES[] = { + "../Frameworks/OpenGL.framework/OpenGL", + "/Library/Frameworks/OpenGL.framework/OpenGL", + "/System/Library/Frameworks/OpenGL.framework/OpenGL", + "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL" + }; +#elif GLAD_PLATFORM_WIN32 + static const char *NAMES[] = {"opengl32.dll"}; +#else + static const char *NAMES[] = { + #if defined(__CYGWIN__) + "libGL-1.so", + #endif + "libGL.so.1", + "libGL.so" + }; +#endif + + if (_gl_handle == NULL) { + _gl_handle = glad_get_dlopen_handle(NAMES, sizeof(NAMES) / sizeof(NAMES[0])); + } + + return _gl_handle; +} + +static struct _glad_gl_userptr glad_gl_build_userptr(void *handle) { + struct _glad_gl_userptr userptr; + + userptr.handle = handle; +#if GLAD_PLATFORM_APPLE || defined(__HAIKU__) + userptr.gl_get_proc_address_ptr = NULL; +#elif GLAD_PLATFORM_WIN32 + userptr.gl_get_proc_address_ptr = + (GLADglprocaddrfunc) glad_dlsym_handle(handle, "wglGetProcAddress"); +#else + userptr.gl_get_proc_address_ptr = + (GLADglprocaddrfunc) glad_dlsym_handle(handle, "glXGetProcAddressARB"); +#endif + + return userptr; +} + +int gladLoaderLoadGL(void) { + int version = 0; + void *handle; + int did_load = 0; + struct _glad_gl_userptr userptr; + + did_load = _gl_handle == NULL; + handle = glad_gl_dlopen_handle(); + if (handle) { + userptr = glad_gl_build_userptr(handle); + + version = gladLoadGLUserPtr(glad_gl_get_proc, &userptr); + + if (did_load) { + gladLoaderUnloadGL(); + } + } + + return version; +} + + + +void gladLoaderUnloadGL(void) { + if (_gl_handle != NULL) { + glad_close_dlopen_handle(_gl_handle); + _gl_handle = NULL; + } +} + +#endif /* GLAD_GL */ #ifdef __cplusplus } diff --git a/subprojects/jansson b/subprojects/jansson deleted file mode 160000 -Subproject a740f15c17ef7956b1493d8d1f800896c1e60ba diff --git a/subprojects/moro b/subprojects/moro deleted file mode 160000 -Subproject 8a2b0e9107356e75696cb9ea619b2436889cd63 diff --git a/subprojects/s3tc-dxt-decompression b/subprojects/s3tc-dxt-decompression new file mode 160000 +Subproject d3a8c52440bbc03f60a9c3921e41409583181c1 diff --git a/subprojects/stb_image/stb_image_write.h b/subprojects/stb_image/stb_image_write.h index 95943eb..1b31b03 100644 --- a/subprojects/stb_image/stb_image_write.h +++ b/subprojects/stb_image/stb_image_write.h @@ -55,11 +55,11 @@ USAGE: There are also five equivalent functions that use an arbitrary write function. You are expected to open/close your file-equivalent before and after calling these: - int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); - int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); - int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); - int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); - int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int +stride_in_bytes); int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void +*data); int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int +stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); int +stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); where the callback is: void stbi_write_func(void *context, void *data, int size); @@ -155,65 +155,67 @@ LICENSE // if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' #ifndef STBIWDEF #ifdef STB_IMAGE_WRITE_STATIC -#define STBIWDEF static +#define STBIWDEF static #else #ifdef __cplusplus -#define STBIWDEF extern "C" +#define STBIWDEF extern "C" #else -#define STBIWDEF extern +#define STBIWDEF extern #endif #endif #endif -#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations extern int stbi_write_tga_with_rle; extern int stbi_write_png_compression_level; extern int stbi_write_force_png_filter; #endif #ifndef STBI_WRITE_NO_STDIO -STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); -STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); -STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); -STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); #ifdef STBI_WINDOWS_UTF8 -STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t *input); #endif #endif typedef void stbi_write_func(void *context, void *data, int size); -STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); -STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); -STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, + int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); -STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, + int quality); STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); -#endif//INCLUDE_STB_IMAGE_WRITE_H +#endif // INCLUDE_STB_IMAGE_WRITE_H #ifdef STB_IMAGE_WRITE_IMPLEMENTATION #ifdef _WIN32 - #ifndef _CRT_SECURE_NO_WARNINGS - #define _CRT_SECURE_NO_WARNINGS - #endif - #ifndef _CRT_NONSTDC_NO_DEPRECATE - #define _CRT_NONSTDC_NO_DEPRECATE - #endif +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif #endif #ifndef STBI_WRITE_NO_STDIO #include <stdio.h> #endif // STBI_WRITE_NO_STDIO +#include <math.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> -#include <math.h> #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) // ok @@ -224,27 +226,25 @@ STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); #endif #ifndef STBIW_MALLOC -#define STBIW_MALLOC(sz) malloc(sz) -#define STBIW_REALLOC(p,newsz) realloc(p,newsz) -#define STBIW_FREE(p) free(p) +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p, newsz) realloc(p, newsz) +#define STBIW_FREE(p) free(p) #endif #ifndef STBIW_REALLOC_SIZED -#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#define STBIW_REALLOC_SIZED(p, oldsz, newsz) STBIW_REALLOC(p, newsz) #endif - #ifndef STBIW_MEMMOVE -#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#define STBIW_MEMMOVE(a, b, sz) memmove(a, b, sz) #endif - #ifndef STBIW_ASSERT #include <assert.h> #define STBIW_ASSERT(x) assert(x) #endif -#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) +#define STBIW_UCHAR(x) (unsigned char)((x)&0xff) #ifdef STB_IMAGE_WRITE_STATIC static int stbi_write_png_compression_level = 8; @@ -260,29 +260,29 @@ static int stbi__flip_vertically_on_write = 0; STBIWDEF void stbi_flip_vertically_on_write(int flag) { - stbi__flip_vertically_on_write = flag; + stbi__flip_vertically_on_write = flag; } typedef struct { - stbi_write_func *func; - void *context; - unsigned char buffer[64]; - int buf_used; + stbi_write_func *func; + void *context; + unsigned char buffer[64]; + int buf_used; } stbi__write_context; // initialize a callback-based context static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) { - s->func = c; - s->context = context; + s->func = c; + s->context = context; } #ifndef STBI_WRITE_NO_STDIO static void stbi__stdio_write(void *context, void *data, int size) { - fwrite(data,1,size,(FILE*) context); + fwrite(data, 1, size, (FILE *)context); } #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) @@ -291,328 +291,387 @@ static void stbi__stdio_write(void *context, void *data, int size) #else #define STBIW_EXTERN extern #endif -STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); -STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, + const char *str, int cbmb, wchar_t *widestr, + int cchwide); +STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, + const wchar_t *widestr, int cchwide, char *str, + int cbmb, const char *defchar, int *used_default); -STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t *input) { - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int)bufferlen, NULL, NULL); } #endif static FILE *stbiw__fopen(char const *filename, char const *mode) { - FILE *f; + FILE *f; #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) - return 0; + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) - return 0; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; #if _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; #else - f = _wfopen(wFilename, wMode); + f = _wfopen(wFilename, wMode); #endif #elif defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; + if (0 != fopen_s(&f, filename, mode)) + f = 0; #else - f = fopen(filename, mode); + f = fopen(filename, mode); #endif - return f; + return f; } static int stbi__start_write_file(stbi__write_context *s, const char *filename) { - FILE *f = stbiw__fopen(filename, "wb"); - stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); - return f != NULL; + FILE *f = stbiw__fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *)f); + return f != NULL; } static void stbi__end_write_file(stbi__write_context *s) { - fclose((FILE *)s->context); + fclose((FILE *)s->context); } #endif // !STBI_WRITE_NO_STDIO typedef unsigned int stbiw_uint32; -typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; +typedef int stb_image_write_test[sizeof(stbiw_uint32) == 4 ? 1 : -1]; static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { - while (*fmt) { - switch (*fmt++) { - case ' ': break; - case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); - s->func(s->context,&x,1); - break; } - case '2': { int x = va_arg(v,int); - unsigned char b[2]; - b[0] = STBIW_UCHAR(x); - b[1] = STBIW_UCHAR(x>>8); - s->func(s->context,b,2); - break; } - case '4': { stbiw_uint32 x = va_arg(v,int); - unsigned char b[4]; - b[0]=STBIW_UCHAR(x); - b[1]=STBIW_UCHAR(x>>8); - b[2]=STBIW_UCHAR(x>>16); - b[3]=STBIW_UCHAR(x>>24); - s->func(s->context,b,4); - break; } - default: + while (*fmt) + { + switch (*fmt++) + { + case ' ': + break; + case '1': { + unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context, &x, 1); + break; + } + case '2': { + int x = va_arg(v, int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x >> 8); + s->func(s->context, b, 2); + break; + } + case '4': { + stbiw_uint32 x = va_arg(v, int); + unsigned char b[4]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x >> 8); + b[2] = STBIW_UCHAR(x >> 16); + b[3] = STBIW_UCHAR(x >> 24); + s->func(s->context, b, 4); + break; + } + default: STBIW_ASSERT(0); return; - } - } + } + } } static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) { - va_list v; - va_start(v, fmt); - stbiw__writefv(s, fmt, v); - va_end(v); + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); } static void stbiw__write_flush(stbi__write_context *s) { - if (s->buf_used) { - s->func(s->context, &s->buffer, s->buf_used); - s->buf_used = 0; - } + if (s->buf_used) + { + s->func(s->context, &s->buffer, s->buf_used); + s->buf_used = 0; + } } static void stbiw__putc(stbi__write_context *s, unsigned char c) { - s->func(s->context, &c, 1); + s->func(s->context, &c, 1); } static void stbiw__write1(stbi__write_context *s, unsigned char a) { - if (s->buf_used + 1 > sizeof(s->buffer)) - stbiw__write_flush(s); - s->buffer[s->buf_used++] = a; + if (s->buf_used + 1 > sizeof(s->buffer)) + stbiw__write_flush(s); + s->buffer[s->buf_used++] = a; } static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { - int n; - if (s->buf_used + 3 > sizeof(s->buffer)) - stbiw__write_flush(s); - n = s->buf_used; - s->buf_used = n+3; - s->buffer[n+0] = a; - s->buffer[n+1] = b; - s->buffer[n+2] = c; + int n; + if (s->buf_used + 3 > sizeof(s->buffer)) + stbiw__write_flush(s); + n = s->buf_used; + s->buf_used = n + 3; + s->buffer[n + 0] = a; + s->buffer[n + 1] = b; + s->buffer[n + 2] = c; } -static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, + unsigned char *d) { - unsigned char bg[3] = { 255, 0, 255}, px[3]; - int k; + unsigned char bg[3] = {255, 0, 255}, px[3]; + int k; - if (write_alpha < 0) - stbiw__write1(s, d[comp - 1]); + if (write_alpha < 0) + stbiw__write1(s, d[comp - 1]); - switch (comp) { - case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case - case 1: - if (expand_mono) + switch (comp) + { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp - else - stbiw__write1(s, d[0]); // monochrome TGA - break; - case 4: - if (!write_alpha) { + else + stbiw__write1(s, d[0]); // monochrome TGA + break; + case 4: + if (!write_alpha) + { // composite against pink background for (k = 0; k < 3; ++k) - px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); break; - } - /* FALLTHROUGH */ - case 3: - stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); - break; - } - if (write_alpha > 0) - stbiw__write1(s, d[comp - 1]); + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + stbiw__write1(s, d[comp - 1]); } -static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, + int write_alpha, int scanline_pad, int expand_mono) { - stbiw_uint32 zero = 0; - int i,j, j_end; + stbiw_uint32 zero = 0; + int i, j, j_end; - if (y <= 0) - return; + if (y <= 0) + return; - if (stbi__flip_vertically_on_write) - vdir *= -1; + if (stbi__flip_vertically_on_write) + vdir *= -1; - if (vdir < 0) { - j_end = -1; j = y-1; - } else { - j_end = y; j = 0; - } + if (vdir < 0) + { + j_end = -1; + j = y - 1; + } + else + { + j_end = y; + j = 0; + } - for (; j != j_end; j += vdir) { - for (i=0; i < x; ++i) { - unsigned char *d = (unsigned char *) data + (j*x+i)*comp; - stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); - } - stbiw__write_flush(s); - s->func(s->context, &zero, scanline_pad); - } + for (; j != j_end; j += vdir) + { + for (i = 0; i < x; ++i) + { + unsigned char *d = (unsigned char *)data + (j * x + i) * comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + stbiw__write_flush(s); + s->func(s->context, &zero, scanline_pad); + } } -static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, + void *data, int alpha, int pad, const char *fmt, ...) { - if (y < 0 || x < 0) { - return 0; - } else { - va_list v; - va_start(v, fmt); - stbiw__writefv(s, fmt, v); - va_end(v); - stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); - return 1; - } + if (y < 0 || x < 0) + { + return 0; + } + else + { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s, rgb_dir, vdir, x, y, comp, data, alpha, pad, expand_mono); + return 1; + } } static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { - int pad = (-x*3) & 3; - return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, - "11 4 22 4" "4 44 22 444444", - 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header - 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + int pad = (-x * 3) & 3; + return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 0, pad, + "11 4 22 4" + "4 44 22 444444", + 'B', 'M', 14 + 40 + (x * 3 + pad) * y, 0, 0, 14 + 40, // file header + 40, x, y, 1, 24, 0, 0, 0, 0, 0, 0); // bitmap header } STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { - stbi__write_context s = { 0 }; - stbi__start_write_callbacks(&s, func, context); - return stbi_write_bmp_core(&s, x, y, comp, data); + stbi__write_context s = {0}; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) { - stbi__write_context s = { 0 }; - if (stbi__start_write_file(&s,filename)) { - int r = stbi_write_bmp_core(&s, x, y, comp, data); - stbi__end_write_file(&s); - return r; - } else - return 0; + stbi__write_context s = {0}; + if (stbi__start_write_file(&s, filename)) + { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } + else + return 0; } -#endif //!STBI_WRITE_NO_STDIO +#endif //! STBI_WRITE_NO_STDIO static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) { - int has_alpha = (comp == 2 || comp == 4); - int colorbytes = has_alpha ? comp-1 : comp; - int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp - 1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 - if (y < 0 || x < 0) - return 0; + if (y < 0 || x < 0) + return 0; - if (!stbi_write_tga_with_rle) { - return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, - "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); - } else { - int i,j,k; - int jend, jdir; + if (!stbi_write_tga_with_rle) + { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *)data, has_alpha, 0, "111 221 2222 11", 0, 0, format, 0, + 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } + else + { + int i, j, k; + int jend, jdir; - stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + stbiw__writef(s, "111 221 2222 11", 0, 0, format + 8, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, + has_alpha * 8); - if (stbi__flip_vertically_on_write) { - j = 0; - jend = y; - jdir = 1; - } else { - j = y-1; - jend = -1; - jdir = -1; - } - for (; j != jend; j += jdir) { - unsigned char *row = (unsigned char *) data + j * x * comp; - int len; + if (stbi__flip_vertically_on_write) + { + j = 0; + jend = y; + jdir = 1; + } + else + { + j = y - 1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) + { + unsigned char *row = (unsigned char *)data + j * x * comp; + int len; - for (i = 0; i < x; i += len) { - unsigned char *begin = row + i * comp; - int diff = 1; - len = 1; + for (i = 0; i < x; i += len) + { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; - if (i < x - 1) { - ++len; - diff = memcmp(begin, row + (i + 1) * comp, comp); - if (diff) { - const unsigned char *prev = begin; - for (k = i + 2; k < x && len < 128; ++k) { - if (memcmp(prev, row + k * comp, comp)) { - prev += comp; - ++len; - } else { - --len; - break; - } - } - } else { - for (k = i + 2; k < x && len < 128; ++k) { - if (!memcmp(begin, row + k * comp, comp)) { - ++len; - } else { - break; - } - } - } - } + if (i < x - 1) + { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) + { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) + { + if (memcmp(prev, row + k * comp, comp)) + { + prev += comp; + ++len; + } + else + { + --len; + break; + } + } + } + else + { + for (k = i + 2; k < x && len < 128; ++k) + { + if (!memcmp(begin, row + k * comp, comp)) + { + ++len; + } + else + { + break; + } + } + } + } - if (diff) { - unsigned char header = STBIW_UCHAR(len - 1); - stbiw__write1(s, header); - for (k = 0; k < len; ++k) { - stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); - } - } else { - unsigned char header = STBIW_UCHAR(len - 129); - stbiw__write1(s, header); - stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + if (diff) + { + unsigned char header = STBIW_UCHAR(len - 1); + stbiw__write1(s, header); + for (k = 0; k < len; ++k) + { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } + else + { + unsigned char header = STBIW_UCHAR(len - 129); + stbiw__write1(s, header); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } } - } - } - stbiw__write_flush(s); - } - return 1; + } + stbiw__write_flush(s); + } + return 1; } STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { - stbi__write_context s = { 0 }; - stbi__start_write_callbacks(&s, func, context); - return stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__write_context s = {0}; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *)data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) { - stbi__write_context s = { 0 }; - if (stbi__start_write_file(&s,filename)) { - int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); - stbi__end_write_file(&s); - return r; - } else - return 0; + stbi__write_context s = {0}; + if (stbi__start_write_file(&s, filename)) + { + int r = stbi_write_tga_core(&s, x, y, comp, (void *)data); + stbi__end_write_file(&s); + return r; + } + else + return 0; } #endif @@ -620,177 +679,201 @@ STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const // Radiance RGBE HDR writer // by Baldur Karlsson -#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { - int exponent; - float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); - if (maxcomp < 1e-32f) { - rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; - } else { - float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + if (maxcomp < 1e-32f) + { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } + else + { + float normalize = (float)frexp(maxcomp, &exponent) * 256.0f / maxcomp; - rgbe[0] = (unsigned char)(linear[0] * normalize); - rgbe[1] = (unsigned char)(linear[1] * normalize); - rgbe[2] = (unsigned char)(linear[2] * normalize); - rgbe[3] = (unsigned char)(exponent + 128); - } + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } } static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) { - unsigned char lengthbyte = STBIW_UCHAR(length+128); - STBIW_ASSERT(length+128 <= 255); - s->func(s->context, &lengthbyte, 1); - s->func(s->context, &databyte, 1); + unsigned char lengthbyte = STBIW_UCHAR(length + 128); + STBIW_ASSERT(length + 128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); } static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) { - unsigned char lengthbyte = STBIW_UCHAR(length); - STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code - s->func(s->context, &lengthbyte, 1); - s->func(s->context, data, length); + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); } -static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, + float *scanline) { - unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; - unsigned char rgbe[4]; - float linear[3]; - int x; + unsigned char scanlineheader[4] = {2, 2, 0, 0}; + unsigned char rgbe[4]; + float linear[3]; + int x; - scanlineheader[2] = (width&0xff00)>>8; - scanlineheader[3] = (width&0x00ff); + scanlineheader[2] = (width & 0xff00) >> 8; + scanlineheader[3] = (width & 0x00ff); - /* skip RLE for images too small or large */ - if (width < 8 || width >= 32768) { - for (x=0; x < width; x++) { - switch (ncomp) { + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) + { + for (x = 0; x < width; x++) + { + switch (ncomp) + { case 4: /* fallthrough */ - case 3: linear[2] = scanline[x*ncomp + 2]; - linear[1] = scanline[x*ncomp + 1]; - linear[0] = scanline[x*ncomp + 0]; - break; + case 3: + linear[2] = scanline[x * ncomp + 2]; + linear[1] = scanline[x * ncomp + 1]; + linear[0] = scanline[x * ncomp + 0]; + break; default: - linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; - break; - } - stbiw__linear_to_rgbe(rgbe, linear); - s->func(s->context, rgbe, 4); - } - } else { - int c,r; - /* encode into scratch buffer */ - for (x=0; x < width; x++) { - switch(ncomp) { + linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } + else + { + int c, r; + /* encode into scratch buffer */ + for (x = 0; x < width; x++) + { + switch (ncomp) + { case 4: /* fallthrough */ - case 3: linear[2] = scanline[x*ncomp + 2]; - linear[1] = scanline[x*ncomp + 1]; - linear[0] = scanline[x*ncomp + 0]; - break; + case 3: + linear[2] = scanline[x * ncomp + 2]; + linear[1] = scanline[x * ncomp + 1]; + linear[0] = scanline[x * ncomp + 0]; + break; default: - linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; - break; - } - stbiw__linear_to_rgbe(rgbe, linear); - scratch[x + width*0] = rgbe[0]; - scratch[x + width*1] = rgbe[1]; - scratch[x + width*2] = rgbe[2]; - scratch[x + width*3] = rgbe[3]; - } + linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width * 0] = rgbe[0]; + scratch[x + width * 1] = rgbe[1]; + scratch[x + width * 2] = rgbe[2]; + scratch[x + width * 3] = rgbe[3]; + } - s->func(s->context, scanlineheader, 4); + s->func(s->context, scanlineheader, 4); - /* RLE each component separately */ - for (c=0; c < 4; c++) { - unsigned char *comp = &scratch[width*c]; + /* RLE each component separately */ + for (c = 0; c < 4; c++) + { + unsigned char *comp = &scratch[width * c]; - x = 0; - while (x < width) { - // find first run - r = x; - while (r+2 < width) { - if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) - break; - ++r; - } - if (r+2 >= width) - r = width; - // dump up to first run - while (x < r) { - int len = r-x; - if (len > 128) len = 128; - stbiw__write_dump_data(s, len, &comp[x]); - x += len; - } - // if there's a run, output it - if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd - // find next byte after run - while (r < width && comp[r] == comp[x]) - ++r; - // output run up to r - while (x < r) { - int len = r-x; - if (len > 127) len = 127; - stbiw__write_run_data(s, len, comp[x]); - x += len; - } + x = 0; + while (x < width) + { + // find first run + r = x; + while (r + 2 < width) + { + if (comp[r] == comp[r + 1] && comp[r] == comp[r + 2]) + break; + ++r; + } + if (r + 2 >= width) + r = width; + // dump up to first run + while (x < r) + { + int len = r - x; + if (len > 128) + len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r + 2 < width) + { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) + { + int len = r - x; + if (len > 127) + len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } } - } - } - } + } + } } static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) { - if (y <= 0 || x <= 0 || data == NULL) - return 0; - else { - // Each component is stored separately. Allocate scratch space for full output scanline. - unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); - int i, len; - char buffer[128]; - char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; - s->func(s->context, header, sizeof(header)-1); + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else + { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *)STBIW_MALLOC(x * 4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header) - 1); #ifdef __STDC_WANT_SECURE_LIB__ - len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #else - len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #endif - s->func(s->context, buffer, len); + s->func(s->context, buffer, len); - for(i=0; i < y; i++) - stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); - STBIW_FREE(scratch); - return 1; - } + for (i = 0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, + data + comp * x * (stbi__flip_vertically_on_write ? y - 1 - i : i)); + STBIW_FREE(scratch); + return 1; + } } STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) { - stbi__write_context s = { 0 }; - stbi__start_write_callbacks(&s, func, context); - return stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__write_context s = {0}; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *)data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { - stbi__write_context s = { 0 }; - if (stbi__start_write_file(&s,filename)) { - int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); - stbi__end_write_file(&s); - return r; - } else - return 0; + stbi__write_context s = {0}; + if (stbi__start_write_file(&s, filename)) + { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *)data); + stbi__end_write_file(&s); + return r; + } + else + return 0; } #endif // STBI_WRITE_NO_STDIO - ////////////////////////////////////////////////////////////////////////////// // // PNG writer @@ -798,196 +881,235 @@ STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const #ifndef STBIW_ZLIB_COMPRESS // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() -#define stbiw__sbraw(a) ((int *) (void *) (a) - 2) -#define stbiw__sbm(a) stbiw__sbraw(a)[0] -#define stbiw__sbn(a) stbiw__sbraw(a)[1] +#define stbiw__sbraw(a) ((int *)(void *)(a)-2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] -#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) -#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) -#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) +#define stbiw__sbneedgrow(a, n) ((a) == 0 || stbiw__sbn(a) + n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a, n) (stbiw__sbneedgrow(a, (n)) ? stbiw__sbgrow(a, n) : 0) +#define stbiw__sbgrow(a, n) stbiw__sbgrowf((void **)&(a), (n), sizeof(*(a))) -#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) -#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) -#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a, 1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)), 0 : 0) static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) { - int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; - void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); - STBIW_ASSERT(p); - if (p) { - if (!*arr) ((int *) p)[1] = 0; - *arr = (void *) ((int *) p + 2); - stbiw__sbm(*arr) = m; - } - return *arr; + int m = *arr ? 2 * stbiw__sbm(*arr) + increment : increment + 1; + void *p = + STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr) * itemsize + sizeof(int) * 2) : 0, + itemsize * m + sizeof(int) * 2); + STBIW_ASSERT(p); + if (p) + { + if (!*arr) + ((int *)p)[1] = 0; + *arr = (void *)((int *)p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; } static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) { - while (*bitcount >= 8) { - stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); - *bitbuffer >>= 8; - *bitcount -= 8; - } - return data; + while (*bitcount >= 8) + { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; } static int stbiw__zlib_bitrev(int code, int codebits) { - int res=0; - while (codebits--) { - res = (res << 1) | (code & 1); - code >>= 1; - } - return res; + int res = 0; + while (codebits--) + { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; } static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) { - int i; - for (i=0; i < limit && i < 258; ++i) - if (a[i] != b[i]) break; - return i; + int i; + for (i = 0; i < limit && i < 258; ++i) + if (a[i] != b[i]) + break; + return i; } static unsigned int stbiw__zhash(unsigned char *data) { - stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - return hash; + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; } #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) -#define stbiw__zlib_add(code,codebits) \ - (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) -#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +#define stbiw__zlib_add(code, codebits) (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b, c) stbiw__zlib_add(stbiw__zlib_bitrev(b, c), c) // default huffman tables -#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) -#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) -#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) -#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) -#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256, 7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280, 8) +#define stbiw__zlib_huff(n) \ + ((n) <= 143 ? stbiw__zlib_huff1(n) \ + : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) -#define stbiw__ZHASH 16384 +#define stbiw__ZHASH 16384 #endif // STBIW_ZLIB_COMPRESS -STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +STBIWDEF unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { #ifdef STBIW_ZLIB_COMPRESS - // user provided a zlib compress implementation, use that - return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); -#else // use builtin - static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; - static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; - static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; - static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; - unsigned int bitbuf=0; - int i,j, bitcount=0; - unsigned char *out = NULL; - unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); - if (hash_table == NULL) - return NULL; - if (quality < 5) quality = 5; + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin + static unsigned short lengthc[] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, + 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 259}; + static unsigned char lengtheb[] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, + 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; + static unsigned short distc[] = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, + 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, + 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 32768}; + static unsigned char disteb[] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; + unsigned int bitbuf = 0; + int i, j, bitcount = 0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char ***)STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char **)); + if (hash_table == NULL) + return NULL; + if (quality < 5) + quality = 5; - stbiw__sbpush(out, 0x78); // DEFLATE 32K window - stbiw__sbpush(out, 0x5e); // FLEVEL = 1 - stbiw__zlib_add(1,1); // BFINAL = 1 - stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1, 1); // BFINAL = 1 + stbiw__zlib_add(1, 2); // BTYPE = 1 -- fixed huffman - for (i=0; i < stbiw__ZHASH; ++i) - hash_table[i] = NULL; + for (i = 0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; - i=0; - while (i < data_len-3) { - // hash next 3 bytes of data to be compressed - int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; - unsigned char *bestloc = 0; - unsigned char **hlist = hash_table[h]; - int n = stbiw__sbcount(hlist); - for (j=0; j < n; ++j) { - if (hlist[j]-data > i-32768) { // if entry lies within window - int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); - if (d >= best) { best=d; bestloc=hlist[j]; } - } - } - // when hash table entry is too long, delete half the entries - if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { - STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); - stbiw__sbn(hash_table[h]) = quality; - } - stbiw__sbpush(hash_table[h],data+i); + i = 0; + while (i < data_len - 3) + { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data + i) & (stbiw__ZHASH - 1), best = 3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j = 0; j < n; ++j) + { + if (hlist[j] - data > i - 32768) + { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data + i, data_len - i); + if (d >= best) + { + best = d; + bestloc = hlist[j]; + } + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2 * quality) + { + STBIW_MEMMOVE(hash_table[h], hash_table[h] + quality, sizeof(hash_table[h][0]) * quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h], data + i); - if (bestloc) { - // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal - h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); - hlist = hash_table[h]; - n = stbiw__sbcount(hlist); - for (j=0; j < n; ++j) { - if (hlist[j]-data > i-32767) { - int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); - if (e > best) { // if next match is better, bail on current match - bestloc = NULL; - break; - } + if (bestloc) + { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data + i + 1) & (stbiw__ZHASH - 1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j = 0; j < n; ++j) + { + if (hlist[j] - data > i - 32767) + { + int e = stbiw__zlib_countm(hlist[j], data + i + 1, data_len - i - 1); + if (e > best) + { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } } - } - } + } - if (bestloc) { - int d = (int) (data+i - bestloc); // distance back - STBIW_ASSERT(d <= 32767 && best <= 258); - for (j=0; best > lengthc[j+1]-1; ++j); - stbiw__zlib_huff(j+257); - if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); - for (j=0; d > distc[j+1]-1; ++j); - stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); - if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); - i += best; - } else { - stbiw__zlib_huffb(data[i]); - ++i; - } - } - // write out final bytes - for (;i < data_len; ++i) - stbiw__zlib_huffb(data[i]); - stbiw__zlib_huff(256); // end of block - // pad with 0 bits to byte boundary - while (bitcount) - stbiw__zlib_add(0,1); + if (bestloc) + { + int d = (int)(data + i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j = 0; best > lengthc[j + 1] - 1; ++j) + ; + stbiw__zlib_huff(j + 257); + if (lengtheb[j]) + stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j = 0; d > distc[j + 1] - 1; ++j) + ; + stbiw__zlib_add(stbiw__zlib_bitrev(j, 5), 5); + if (disteb[j]) + stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } + else + { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (; i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0, 1); - for (i=0; i < stbiw__ZHASH; ++i) - (void) stbiw__sbfree(hash_table[i]); - STBIW_FREE(hash_table); + for (i = 0; i < stbiw__ZHASH; ++i) + (void)stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); - { - // compute adler32 on input - unsigned int s1=1, s2=0; - int blocklen = (int) (data_len % 5552); - j=0; - while (j < data_len) { - for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } - s1 %= 65521; s2 %= 65521; - j += blocklen; - blocklen = 5552; - } - stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); - stbiw__sbpush(out, STBIW_UCHAR(s2)); - stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); - stbiw__sbpush(out, STBIW_UCHAR(s1)); - } - *out_len = stbiw__sbn(out); - // make returned pointer freeable - STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); - return (unsigned char *) stbiw__sbraw(out); + { + // compute adler32 on input + unsigned int s1 = 1, s2 = 0; + int blocklen = (int)(data_len % 5552); + j = 0; + while (j < data_len) + { + for (i = 0; i < blocklen; ++i) + { + s1 += data[j + i]; + s2 += s1; + } + s1 %= 65521; + s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *)stbiw__sbraw(out); #endif // STBIW_ZLIB_COMPRESS } @@ -996,219 +1118,278 @@ static unsigned int stbiw__crc32(unsigned char *buffer, int len) #ifdef STBIW_CRC32 return STBIW_CRC32(buffer, len); #else - static unsigned int crc_table[256] = - { - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, - 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, - 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, - 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, - 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, - 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, - 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, - 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, - 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, - 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, - 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, - 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, - 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, - 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, - 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, - 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, - 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, - 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D - }; + static unsigned int crc_table[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0eDB8832, + 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, + 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, + 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, + 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, + 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, + 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, + 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, + 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, + 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, + 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, + 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, + 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, + 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, + 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, + 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, + 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, + 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, + 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D}; - unsigned int crc = ~0u; - int i; - for (i=0; i < len; ++i) - crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; - return ~crc; + unsigned int crc = ~0u; + int i; + for (i = 0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; #endif } -#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) -#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); -#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) +#define stbiw__wpng4(o, a, b, c, d) \ + ((o)[0] = STBIW_UCHAR(a), (o)[1] = STBIW_UCHAR(b), (o)[2] = STBIW_UCHAR(c), (o)[3] = STBIW_UCHAR(d), (o) += 4) +#define stbiw__wp32(data, v) stbiw__wpng4(data, (v) >> 24, (v) >> 16, (v) >> 8, (v)); +#define stbiw__wptag(data, s) stbiw__wpng4(data, s[0], s[1], s[2], s[3]) static void stbiw__wpcrc(unsigned char **data, int len) { - unsigned int crc = stbiw__crc32(*data - len - 4, len+4); - stbiw__wp32(*data, crc); + unsigned int crc = stbiw__crc32(*data - len - 4, len + 4); + stbiw__wp32(*data, crc); } static unsigned char stbiw__paeth(int a, int b, int c) { - int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); - if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); - if (pb <= pc) return STBIW_UCHAR(b); - return STBIW_UCHAR(c); + int p = a + b - c, pa = abs(p - a), pb = abs(p - b), pc = abs(p - c); + if (pa <= pb && pa <= pc) + return STBIW_UCHAR(a); + if (pb <= pc) + return STBIW_UCHAR(b); + return STBIW_UCHAR(c); } // @OPTIMIZE: provide an option that always forces left-predict or paeth predict -static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, + int filter_type, signed char *line_buffer) { - static int mapping[] = { 0,1,2,3,4 }; - static int firstmap[] = { 0,1,0,5,6 }; - int *mymap = (y != 0) ? mapping : firstmap; - int i; - int type = mymap[filter_type]; - unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); - int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + static int mapping[] = {0, 1, 2, 3, 4}; + static int firstmap[] = {0, 1, 0, 5, 6}; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height - 1 - y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; - if (type==0) { - memcpy(line_buffer, z, width*n); - return; - } + if (type == 0) + { + memcpy(line_buffer, z, width * n); + return; + } - // first loop isn't optimized since it's just one pixel - for (i = 0; i < n; ++i) { - switch (type) { - case 1: line_buffer[i] = z[i]; break; - case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; - case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; - case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; - case 5: line_buffer[i] = z[i]; break; - case 6: line_buffer[i] = z[i]; break; - } - } - switch (type) { - case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; - case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; - case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; - case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; - case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; - case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; - } + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) + { + switch (type) + { + case 1: + line_buffer[i] = z[i]; + break; + case 2: + line_buffer[i] = z[i] - z[i - signed_stride]; + break; + case 3: + line_buffer[i] = z[i] - (z[i - signed_stride] >> 1); + break; + case 4: + line_buffer[i] = (signed char)(z[i] - stbiw__paeth(0, z[i - signed_stride], 0)); + break; + case 5: + line_buffer[i] = z[i]; + break; + case 6: + line_buffer[i] = z[i]; + break; + } + } + switch (type) + { + case 1: + for (i = n; i < width * n; ++i) + line_buffer[i] = z[i] - z[i - n]; + break; + case 2: + for (i = n; i < width * n; ++i) + line_buffer[i] = z[i] - z[i - signed_stride]; + break; + case 3: + for (i = n; i < width * n; ++i) + line_buffer[i] = z[i] - ((z[i - n] + z[i - signed_stride]) >> 1); + break; + case 4: + for (i = n; i < width * n; ++i) + line_buffer[i] = z[i] - stbiw__paeth(z[i - n], z[i - signed_stride], z[i - signed_stride - n]); + break; + case 5: + for (i = n; i < width * n; ++i) + line_buffer[i] = z[i] - (z[i - n] >> 1); + break; + case 6: + for (i = n; i < width * n; ++i) + line_buffer[i] = z[i] - stbiw__paeth(z[i - n], 0, 0); + break; + } } -STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, + int *out_len) { - int force_filter = stbi_write_force_png_filter; - int ctype[5] = { -1, 0, 4, 2, 6 }; - unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; - unsigned char *out,*o, *filt, *zlib; - signed char *line_buffer; - int j,zlen; + int force_filter = stbi_write_force_png_filter; + int ctype[5] = {-1, 0, 4, 2, 6}; + unsigned char sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + unsigned char *out, *o, *filt, *zlib; + signed char *line_buffer; + int j, zlen; - if (stride_bytes == 0) - stride_bytes = x * n; + if (stride_bytes == 0) + stride_bytes = x * n; - if (force_filter >= 5) { - force_filter = -1; - } + if (force_filter >= 5) + { + force_filter = -1; + } - filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; - line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } - for (j=0; j < y; ++j) { - int filter_type; - if (force_filter > -1) { - filter_type = force_filter; - stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); - } else { // Estimate the best filter by running through all of them: - int best_filter = 0, best_filter_val = 0x7fffffff, est, i; - for (filter_type = 0; filter_type < 5; filter_type++) { - stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + filt = (unsigned char *)STBIW_MALLOC((x * n + 1) * y); + if (!filt) + return 0; + line_buffer = (signed char *)STBIW_MALLOC(x * n); + if (!line_buffer) + { + STBIW_FREE(filt); + return 0; + } + for (j = 0; j < y; ++j) + { + int filter_type; + if (force_filter > -1) + { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char *)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } + else + { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) + { + stbiw__encode_png_line((unsigned char *)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); - // Estimate the entropy of the line using this filter; the less, the better. - est = 0; - for (i = 0; i < x*n; ++i) { - est += abs((signed char) line_buffer[i]); + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x * n; ++i) + { + est += abs((signed char)line_buffer[i]); + } + if (est < best_filter_val) + { + best_filter_val = est; + best_filter = filter_type; + } } - if (est < best_filter_val) { - best_filter_val = est; - best_filter = filter_type; + if (filter_type != best_filter) + { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char *)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; } - } - if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it - stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); - filter_type = best_filter; - } - } - // when we get here, filter_type contains the filter type, and line_buffer contains the data - filt[j*(x*n+1)] = (unsigned char) filter_type; - STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); - } - STBIW_FREE(line_buffer); - zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); - STBIW_FREE(filt); - if (!zlib) return 0; + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j * (x * n + 1)] = (unsigned char)filter_type; + STBIW_MEMMOVE(filt + j * (x * n + 1) + 1, line_buffer, x * n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y * (x * n + 1), &zlen, stbi_write_png_compression_level); + STBIW_FREE(filt); + if (!zlib) + return 0; - // each tag requires 12 bytes of overhead - out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); - if (!out) return 0; - *out_len = 8 + 12+13 + 12+zlen + 12; + // each tag requires 12 bytes of overhead + out = (unsigned char *)STBIW_MALLOC(8 + 12 + 13 + 12 + zlen + 12); + if (!out) + return 0; + *out_len = 8 + 12 + 13 + 12 + zlen + 12; - o=out; - STBIW_MEMMOVE(o,sig,8); o+= 8; - stbiw__wp32(o, 13); // header length - stbiw__wptag(o, "IHDR"); - stbiw__wp32(o, x); - stbiw__wp32(o, y); - *o++ = 8; - *o++ = STBIW_UCHAR(ctype[n]); - *o++ = 0; - *o++ = 0; - *o++ = 0; - stbiw__wpcrc(&o,13); + o = out; + STBIW_MEMMOVE(o, sig, 8); + o += 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o, 13); - stbiw__wp32(o, zlen); - stbiw__wptag(o, "IDAT"); - STBIW_MEMMOVE(o, zlib, zlen); - o += zlen; - STBIW_FREE(zlib); - stbiw__wpcrc(&o, zlen); + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); - stbiw__wp32(o,0); - stbiw__wptag(o, "IEND"); - stbiw__wpcrc(&o,0); + stbiw__wp32(o, 0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o, 0); - STBIW_ASSERT(o == out + *out_len); + STBIW_ASSERT(o == out + *out_len); - return out; + return out; } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) { - FILE *f; - int len; - unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); - if (png == NULL) return 0; + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *)data, stride_bytes, x, y, comp, &len); + if (png == NULL) + return 0; - f = stbiw__fopen(filename, "wb"); - if (!f) { STBIW_FREE(png); return 0; } - fwrite(png, 1, len, f); - fclose(f); - STBIW_FREE(png); - return 1; + f = stbiw__fopen(filename, "wb"); + if (!f) + { + STBIW_FREE(png); + return 0; + } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; } #endif -STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, + int stride_bytes) { - int len; - unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); - if (png == NULL) return 0; - func(context, png, len); - STBIW_FREE(png); - return 1; + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *)data, stride_bytes, x, y, comp, &len); + if (png == NULL) + return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; } - /* *************************************************************************** * * JPEG writer @@ -1217,381 +1398,430 @@ STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html */ -static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, - 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; +static const unsigned char stbiw__jpg_ZigZag[] = {0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63}; -static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { - int bitBuf = *bitBufP, bitCnt = *bitCntP; - bitCnt += bs[1]; - bitBuf |= bs[0] << (24 - bitCnt); - while(bitCnt >= 8) { - unsigned char c = (bitBuf >> 16) & 255; - stbiw__putc(s, c); - if(c == 255) { - stbiw__putc(s, 0); - } - bitBuf <<= 8; - bitCnt -= 8; - } - *bitBufP = bitBuf; - *bitCntP = bitCnt; +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) +{ + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while (bitCnt >= 8) + { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if (c == 255) + { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; } -static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { - float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; - float z1, z2, z3, z4, z5, z11, z13; +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, + float *d7p) +{ + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; - float tmp0 = d0 + d7; - float tmp7 = d0 - d7; - float tmp1 = d1 + d6; - float tmp6 = d1 - d6; - float tmp2 = d2 + d5; - float tmp5 = d2 - d5; - float tmp3 = d3 + d4; - float tmp4 = d3 - d4; + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; - // Even part - float tmp10 = tmp0 + tmp3; // phase 2 - float tmp13 = tmp0 - tmp3; - float tmp11 = tmp1 + tmp2; - float tmp12 = tmp1 - tmp2; + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; - d0 = tmp10 + tmp11; // phase 3 - d4 = tmp10 - tmp11; + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; - z1 = (tmp12 + tmp13) * 0.707106781f; // c4 - d2 = tmp13 + z1; // phase 5 - d6 = tmp13 - z1; + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; - // Odd part - tmp10 = tmp4 + tmp5; // phase 2 - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; - // The rotator is modified from fig 4-8 to avoid extra negations. - z5 = (tmp10 - tmp12) * 0.382683433f; // c6 - z2 = tmp10 * 0.541196100f + z5; // c2-c6 - z4 = tmp12 * 1.306562965f + z5; // c2+c6 - z3 = tmp11 * 0.707106781f; // c4 + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 - z11 = tmp7 + z3; // phase 5 - z13 = tmp7 - z3; + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; - *d5p = z13 + z2; // phase 6 - *d3p = z13 - z2; - *d1p = z11 + z4; - *d7p = z11 - z4; + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; - *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; + *d0p = d0; + *d2p = d2; + *d4p = d4; + *d6p = d6; } -static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { - int tmp1 = val < 0 ? -val : val; - val = val < 0 ? val-1 : val; - bits[1] = 1; - while(tmp1 >>= 1) { - ++bits[1]; - } - bits[0] = val & ((1<<bits[1])-1); +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) +{ + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val - 1 : val; + bits[1] = 1; + while (tmp1 >>= 1) + { + ++bits[1]; + } + bits[0] = val & ((1 << bits[1]) - 1); } -static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, float *CDU, int du_stride, float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) { - const unsigned short EOB[2] = { HTAC[0x00][0], HTAC[0x00][1] }; - const unsigned short M16zeroes[2] = { HTAC[0xF0][0], HTAC[0xF0][1] }; - int dataOff, i, j, n, diff, end0pos, x, y; - int DU[64]; +static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, float *CDU, int du_stride, + float *fdtbl, int DC, const unsigned short HTDC[256][2], + const unsigned short HTAC[256][2]) +{ + const unsigned short EOB[2] = {HTAC[0x00][0], HTAC[0x00][1]}; + const unsigned short M16zeroes[2] = {HTAC[0xF0][0], HTAC[0xF0][1]}; + int dataOff, i, j, n, diff, end0pos, x, y; + int DU[64]; - // DCT rows - for(dataOff=0, n=du_stride*8; dataOff<n; dataOff+=du_stride) { - stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]); - } - // DCT columns - for(dataOff=0; dataOff<8; ++dataOff) { - stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+du_stride], &CDU[dataOff+du_stride*2], &CDU[dataOff+du_stride*3], &CDU[dataOff+du_stride*4], - &CDU[dataOff+du_stride*5], &CDU[dataOff+du_stride*6], &CDU[dataOff+du_stride*7]); - } - // Quantize/descale/zigzag the coefficients - for(y = 0, j=0; y < 8; ++y) { - for(x = 0; x < 8; ++x,++j) { - float v; - i = y*du_stride+x; - v = CDU[i]*fdtbl[j]; - // DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f)); - // ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway? - DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? v - 0.5f : v + 0.5f); - } - } + // DCT rows + for (dataOff = 0, n = du_stride * 8; dataOff < n; dataOff += du_stride) + { + stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff + 1], &CDU[dataOff + 2], &CDU[dataOff + 3], &CDU[dataOff + 4], + &CDU[dataOff + 5], &CDU[dataOff + 6], &CDU[dataOff + 7]); + } + // DCT columns + for (dataOff = 0; dataOff < 8; ++dataOff) + { + stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff + du_stride], &CDU[dataOff + du_stride * 2], + &CDU[dataOff + du_stride * 3], &CDU[dataOff + du_stride * 4], &CDU[dataOff + du_stride * 5], + &CDU[dataOff + du_stride * 6], &CDU[dataOff + du_stride * 7]); + } + // Quantize/descale/zigzag the coefficients + for (y = 0, j = 0; y < 8; ++y) + { + for (x = 0; x < 8; ++x, ++j) + { + float v; + i = y * du_stride + x; + v = CDU[i] * fdtbl[j]; + // DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f)); + // ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway? + DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? v - 0.5f : v + 0.5f); + } + } - // Encode DC - diff = DU[0] - DC; - if (diff == 0) { - stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]); - } else { - unsigned short bits[2]; - stbiw__jpg_calcBits(diff, bits); - stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]); - stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); - } - // Encode ACs - end0pos = 63; - for(; (end0pos>0)&&(DU[end0pos]==0); --end0pos) { - } - // end0pos = first element in reverse order !=0 - if(end0pos == 0) { - stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); - return DU[0]; - } - for(i = 1; i <= end0pos; ++i) { - int startpos = i; - int nrzeroes; - unsigned short bits[2]; - for (; DU[i]==0 && i<=end0pos; ++i) { - } - nrzeroes = i-startpos; - if ( nrzeroes >= 16 ) { - int lng = nrzeroes>>4; - int nrmarker; - for (nrmarker=1; nrmarker <= lng; ++nrmarker) - stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); - nrzeroes &= 15; - } - stbiw__jpg_calcBits(DU[i], bits); - stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); - stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); - } - if(end0pos != 63) { - stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); - } - return DU[0]; + // Encode DC + diff = DU[0] - DC; + if (diff == 0) + { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]); + } + else + { + unsigned short bits[2]; + stbiw__jpg_calcBits(diff, bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + // Encode ACs + end0pos = 63; + for (; (end0pos > 0) && (DU[end0pos] == 0); --end0pos) + { + } + // end0pos = first element in reverse order !=0 + if (end0pos == 0) + { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for (i = 1; i <= end0pos; ++i) + { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i] == 0 && i <= end0pos; ++i) + { + } + nrzeroes = i - startpos; + if (nrzeroes >= 16) + { + int lng = nrzeroes >> 4; + int nrmarker; + for (nrmarker = 1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes << 4) + bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if (end0pos != 63) + { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; } -static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { - // Constants that don't pollute global namespace - static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; - static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; - static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; - static const unsigned char std_ac_luminance_values[] = { - 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, - 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, - 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, - 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, - 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, - 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, - 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa - }; - static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; - static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; - static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; - static const unsigned char std_ac_chrominance_values[] = { - 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, - 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, - 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, - 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, - 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, - 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, - 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa - }; - // Huffman tables - static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; - static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; - static const unsigned short YAC_HT[256][2] = { - {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, - {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} - }; - static const unsigned short UVAC_HT[256][2] = { - {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, - {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} - }; - static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, - 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; - static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, - 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; - static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, - 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void *data, int quality) +{ + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}; + static const unsigned char std_dc_luminance_values[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, + 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, + 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa}; + static const unsigned char std_dc_chrominance_nrcodes[] = {0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; + static const unsigned char std_dc_chrominance_values[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, + 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, + 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, + 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa}; + // Huffman tables + static const unsigned short YDC_HT[256][2] = {{0, 2}, {2, 3}, {3, 3}, {4, 3}, {5, 3}, {6, 3}, {14, 4}, {30, 5}, {62, 6}, {126, 7}, {254, 8}, {510, 9}}; + static const unsigned short UVDC_HT[256][2] = {{0, 2}, {1, 2}, {2, 2}, {6, 3}, {14, 4}, {30, 5}, {62, 6}, {126, 7}, {254, 8}, {510, 9}, {1022, 10}, {2046, 11}}; + static const unsigned short YAC_HT[256][2] = { + {10, 4}, {0, 2}, {1, 2}, {4, 3}, {11, 4}, {26, 5}, {120, 7}, {248, 8}, {1014, 10}, {65410, 16}, {65411, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {12, 4}, {27, 5}, {121, 7}, {502, 9}, {2038, 11}, {65412, 16}, {65413, 16}, {65414, 16}, {65415, 16}, {65416, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {28, 5}, {249, 8}, {1015, 10}, {4084, 12}, {65417, 16}, {65418, 16}, {65419, 16}, {65420, 16}, {65421, 16}, {65422, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {58, 6}, {503, 9}, {4085, 12}, {65423, 16}, {65424, 16}, {65425, 16}, {65426, 16}, {65427, 16}, {65428, 16}, {65429, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {59, 6}, {1016, 10}, {65430, 16}, {65431, 16}, {65432, 16}, {65433, 16}, {65434, 16}, {65435, 16}, {65436, 16}, {65437, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {122, 7}, {2039, 11}, {65438, 16}, {65439, 16}, {65440, 16}, {65441, 16}, {65442, 16}, {65443, 16}, {65444, 16}, {65445, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {123, 7}, {4086, 12}, {65446, 16}, {65447, 16}, {65448, 16}, {65449, 16}, {65450, 16}, {65451, 16}, {65452, 16}, {65453, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {250, 8}, {4087, 12}, {65454, 16}, {65455, 16}, {65456, 16}, {65457, 16}, {65458, 16}, {65459, 16}, {65460, 16}, {65461, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {504, 9}, {32704, 15}, {65462, 16}, {65463, 16}, {65464, 16}, {65465, 16}, {65466, 16}, {65467, 16}, {65468, 16}, {65469, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {505, 9}, {65470, 16}, {65471, 16}, {65472, 16}, {65473, 16}, {65474, 16}, {65475, 16}, {65476, 16}, {65477, 16}, {65478, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {506, 9}, {65479, 16}, {65480, 16}, {65481, 16}, {65482, 16}, {65483, 16}, {65484, 16}, {65485, 16}, {65486, 16}, {65487, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {1017, 10}, {65488, 16}, {65489, 16}, {65490, 16}, {65491, 16}, {65492, 16}, {65493, 16}, {65494, 16}, {65495, 16}, {65496, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {1018, 10}, {65497, 16}, {65498, 16}, {65499, 16}, {65500, 16}, {65501, 16}, {65502, 16}, {65503, 16}, {65504, 16}, {65505, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {2040, 11}, {65506, 16}, {65507, 16}, {65508, 16}, {65509, 16}, {65510, 16}, {65511, 16}, {65512, 16}, {65513, 16}, {65514, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {65515, 16}, {65516, 16}, {65517, 16}, {65518, 16}, {65519, 16}, {65520, 16}, {65521, 16}, {65522, 16}, {65523, 16}, {65524, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {2041, 11}, {65525, 16}, {65526, 16}, {65527, 16}, {65528, 16}, {65529, 16}, {65530, 16}, {65531, 16}, {65532, 16}, {65533, 16}, {65534, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}; + static const unsigned short UVAC_HT[256][2] = { + {0, 2}, {1, 2}, {4, 3}, {10, 4}, {24, 5}, {25, 5}, {56, 6}, {120, 7}, {500, 9}, {1014, 10}, {4084, 12}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {11, 4}, {57, 6}, {246, 8}, {501, 9}, {2038, 11}, {4085, 12}, {65416, 16}, {65417, 16}, {65418, 16}, {65419, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {26, 5}, {247, 8}, {1015, 10}, {4086, 12}, {32706, 15}, {65420, 16}, {65421, 16}, {65422, 16}, {65423, 16}, {65424, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {27, 5}, {248, 8}, {1016, 10}, {4087, 12}, {65425, 16}, {65426, 16}, {65427, 16}, {65428, 16}, {65429, 16}, {65430, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {58, 6}, {502, 9}, {65431, 16}, {65432, 16}, {65433, 16}, {65434, 16}, {65435, 16}, {65436, 16}, {65437, 16}, {65438, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {59, 6}, {1017, 10}, {65439, 16}, {65440, 16}, {65441, 16}, {65442, 16}, {65443, 16}, {65444, 16}, {65445, 16}, {65446, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {121, 7}, {2039, 11}, {65447, 16}, {65448, 16}, {65449, 16}, {65450, 16}, {65451, 16}, {65452, 16}, {65453, 16}, {65454, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {122, 7}, {2040, 11}, {65455, 16}, {65456, 16}, {65457, 16}, {65458, 16}, {65459, 16}, {65460, 16}, {65461, 16}, {65462, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {249, 8}, {65463, 16}, {65464, 16}, {65465, 16}, {65466, 16}, {65467, 16}, {65468, 16}, {65469, 16}, {65470, 16}, {65471, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {503, 9}, {65472, 16}, {65473, 16}, {65474, 16}, {65475, 16}, {65476, 16}, {65477, 16}, {65478, 16}, {65479, 16}, {65480, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {504, 9}, {65481, 16}, {65482, 16}, {65483, 16}, {65484, 16}, {65485, 16}, {65486, 16}, {65487, 16}, {65488, 16}, {65489, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {505, 9}, {65490, 16}, {65491, 16}, {65492, 16}, {65493, 16}, {65494, 16}, {65495, 16}, {65496, 16}, {65497, 16}, {65498, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {506, 9}, {65499, 16}, {65500, 16}, {65501, 16}, {65502, 16}, {65503, 16}, {65504, 16}, {65505, 16}, {65506, 16}, {65507, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {2041, 11}, {65508, 16}, {65509, 16}, {65510, 16}, {65511, 16}, {65512, 16}, {65513, 16}, {65514, 16}, {65515, 16}, {65516, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {16352, 14}, {65517, 16}, {65518, 16}, {65519, 16}, {65520, 16}, {65521, 16}, {65522, 16}, {65523, 16}, {65524, 16}, {65525, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {1018, 10}, {32707, 15}, {65526, 16}, {65527, 16}, {65528, 16}, {65529, 16}, {65530, 16}, {65531, 16}, {65532, 16}, {65533, 16}, {65534, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}; + static const int YQT[] = {16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99}; + static const int UVQT[] = {17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, + 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}; + static const float aasf[] = {1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, + 1.175875602f * 2.828427125f, 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, + 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f}; - int row, col, i, k, subsample; - float fdtbl_Y[64], fdtbl_UV[64]; - unsigned char YTable[64], UVTable[64]; + int row, col, i, k, subsample; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; - if(!data || !width || !height || comp > 4 || comp < 1) { - return 0; - } + if (!data || !width || !height || comp > 4 || comp < 1) + { + return 0; + } - quality = quality ? quality : 90; - subsample = quality <= 90 ? 1 : 0; - quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; - quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + quality = quality ? quality : 90; + subsample = quality <= 90 ? 1 : 0; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; - for(i = 0; i < 64; ++i) { - int uvti, yti = (YQT[i]*quality+50)/100; - YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); - uvti = (UVQT[i]*quality+50)/100; - UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); - } + for (i = 0; i < 64; ++i) + { + int uvti, yti = (YQT[i] * quality + 50) / 100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char)(yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i] * quality + 50) / 100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char)(uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } - for(row = 0, k = 0; row < 8; ++row) { - for(col = 0; col < 8; ++col, ++k) { - fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); - fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); - } - } + for (row = 0, k = 0; row < 8; ++row) + { + for (col = 0; col < 8; ++col, ++k) + { + fdtbl_Y[k] = 1 / (YTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } - // Write Headers - { - static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; - static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; - const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), - 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; - s->func(s->context, (void*)head0, sizeof(head0)); - s->func(s->context, (void*)YTable, sizeof(YTable)); - stbiw__putc(s, 1); - s->func(s->context, UVTable, sizeof(UVTable)); - s->func(s->context, (void*)head1, sizeof(head1)); - s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); - s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); - stbiw__putc(s, 0x10); // HTYACinfo - s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); - s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); - stbiw__putc(s, 1); // HTUDCinfo - s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); - s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); - stbiw__putc(s, 0x11); // HTUACinfo - s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); - s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); - s->func(s->context, (void*)head2, sizeof(head2)); - } + // Write Headers + { + static const unsigned char head0[] = {0xFF, 0xD8, 0xFF, 0xE0, 0, 0x10, 'J', 'F', 'I', 'F', 0, 1, 1, + 0, 0, 1, 0, 1, 0, 0, 0xFF, 0xDB, 0, 0x84, 0}; + static const unsigned char head2[] = {0xFF, 0xDA, 0, 0xC, 3, 1, 0, 2, 0x11, 3, 0x11, 0, 0x3F, 0}; + const unsigned char head1[] = {0xFF, + 0xC0, + 0, + 0x11, + 8, + (unsigned char)(height >> 8), + STBIW_UCHAR(height), + (unsigned char)(width >> 8), + STBIW_UCHAR(width), + 3, + 1, + (unsigned char)(subsample ? 0x22 : 0x11), + 0, + 2, + 0x11, + 1, + 3, + 0x11, + 1, + 0xFF, + 0xC4, + 0x01, + 0xA2, + 0}; + s->func(s->context, (void *)head0, sizeof(head0)); + s->func(s->context, (void *)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void *)head1, sizeof(head1)); + s->func(s->context, (void *)(std_dc_luminance_nrcodes + 1), sizeof(std_dc_luminance_nrcodes) - 1); + s->func(s->context, (void *)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void *)(std_ac_luminance_nrcodes + 1), sizeof(std_ac_luminance_nrcodes) - 1); + s->func(s->context, (void *)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void *)(std_dc_chrominance_nrcodes + 1), sizeof(std_dc_chrominance_nrcodes) - 1); + s->func(s->context, (void *)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void *)(std_ac_chrominance_nrcodes + 1), sizeof(std_ac_chrominance_nrcodes) - 1); + s->func(s->context, (void *)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void *)head2, sizeof(head2)); + } - // Encode 8x8 macroblocks - { - static const unsigned short fillBits[] = {0x7F, 7}; - int DCY=0, DCU=0, DCV=0; - int bitBuf=0, bitCnt=0; - // comp == 2 is grey+alpha (alpha is ignored) - int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; - const unsigned char *dataR = (const unsigned char *)data; - const unsigned char *dataG = dataR + ofsG; - const unsigned char *dataB = dataR + ofsB; - int x, y, pos; - if(subsample) { - for(y = 0; y < height; y += 16) { - for(x = 0; x < width; x += 16) { - float Y[256], U[256], V[256]; - for(row = y, pos = 0; row < y+16; ++row) { - // row >= height => use last input row - int clamped_row = (row < height) ? row : height - 1; - int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; - for(col = x; col < x+16; ++col, ++pos) { - // if col >= width => use pixel from last input column - int p = base_p + ((col < width) ? col : (width-1))*comp; - float r = dataR[p], g = dataG[p], b = dataB[p]; - Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; - U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; - V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; - } - } - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + int DCY = 0, DCU = 0, DCV = 0; + int bitBuf = 0, bitCnt = 0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + const unsigned char *dataR = (const unsigned char *)data; + const unsigned char *dataG = dataR + ofsG; + const unsigned char *dataB = dataR + ofsB; + int x, y, pos; + if (subsample) + { + for (y = 0; y < height; y += 16) + { + for (x = 0; x < width; x += 16) + { + float Y[256], U[256], V[256]; + for (row = y, pos = 0; row < y + 16; ++row) + { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = + (stbi__flip_vertically_on_write ? (height - 1 - clamped_row) : clamped_row) * width * comp; + for (col = x; col < x + 16; ++col, ++pos) + { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width - 1)) * comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128; + U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b; + V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b; + } + } + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); - // subsample U,V - { - float subU[64], subV[64]; - int yy, xx; - for(yy = 0, pos = 0; yy < 8; ++yy) { - for(xx = 0; xx < 8; ++xx, ++pos) { - int j = yy*32+xx*2; - subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; - subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; - } - } - DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); - DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); - } + // subsample U,V + { + float subU[64], subV[64]; + int yy, xx; + for (yy = 0, pos = 0; yy < 8; ++yy) + { + for (xx = 0; xx < 8; ++xx, ++pos) + { + int j = yy * 32 + xx * 2; + subU[pos] = (U[j + 0] + U[j + 1] + U[j + 16] + U[j + 17]) * 0.25f; + subV[pos] = (V[j + 0] + V[j + 1] + V[j + 16] + V[j + 17]) * 0.25f; + } + } + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } } - } - } else { - for(y = 0; y < height; y += 8) { - for(x = 0; x < width; x += 8) { - float Y[64], U[64], V[64]; - for(row = y, pos = 0; row < y+8; ++row) { - // row >= height => use last input row - int clamped_row = (row < height) ? row : height - 1; - int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; - for(col = x; col < x+8; ++col, ++pos) { - // if col >= width => use pixel from last input column - int p = base_p + ((col < width) ? col : (width-1))*comp; - float r = dataR[p], g = dataG[p], b = dataB[p]; - Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; - U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; - V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; - } - } + } + else + { + for (y = 0; y < height; y += 8) + { + for (x = 0; x < width; x += 8) + { + float Y[64], U[64], V[64]; + for (row = y, pos = 0; row < y + 8; ++row) + { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = + (stbi__flip_vertically_on_write ? (height - 1 - clamped_row) : clamped_row) * width * comp; + for (col = x; col < x + 8; ++col, ++pos) + { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width - 1)) * comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128; + U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b; + V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b; + } + } - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); - DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); - DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } } - } - } + } - // Do the bit alignment of the EOI marker - stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); - } + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } - // EOI - stbiw__putc(s, 0xFF); - stbiw__putc(s, 0xD9); + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); - return 1; + return 1; } -STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, + int quality) { - stbi__write_context s = { 0 }; - stbi__start_write_callbacks(&s, func, context); - return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); + stbi__write_context s = {0}; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *)data, quality); } - #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) { - stbi__write_context s = { 0 }; - if (stbi__start_write_file(&s,filename)) { - int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); - stbi__end_write_file(&s); - return r; - } else - return 0; + stbi__write_context s = {0}; + if (stbi__start_write_file(&s, filename)) + { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } + else + return 0; } #endif @@ -1635,7 +1865,7 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const add HDR output fix monochrome BMP 0.95 (2014-08-17) - add monochrome TGA output + add monochrome TGA output 0.94 (2014-05-31) rename private functions to avoid conflicts with stb_image.h 0.93 (2014-05-27) |