Add MLX42 V2

This commit is contained in:
Etienne Rey-bethbeder
2023-04-26 13:39:03 +02:00
parent 8ff4363d2c
commit bc5fc2fc59
62 changed files with 26910 additions and 1 deletions

3543
MLX42/src/font/font.h Normal file

File diff suppressed because it is too large Load Diff

80
MLX42/src/font/mlx_font.c Normal file
View File

@ -0,0 +1,80 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_font.c :+: :+: */
/* +:+ */
/* By: W2Wizard <w2.wizzard@gmail.com> +#+ */
/* +#+ */
/* Created: 2022/02/22 12:01:37 by W2Wizard #+# #+# */
/* Updated: 2022/06/27 19:53:36 by lde-la-h ######## odam.nl */
/* */
/* ************************************************************************** */
#include "font.h"
#include "MLX42/MLX42_Int.h"
//= Private =//
/**
* Does the actual copying of pixels form the atlas buffer to the
* image buffer.
*
* Skips any non-printable characters.
*
* @param image The image to draw on.
* @param texture The font_atlas.
* @param texoffset The character texture X offset.
* @param imgoffset The image X offset.
*/
static void mlx_draw_char(mlx_image_t* image, int32_t texoffset, int32_t imgoffset)
{
if (texoffset < 0)
return;
char* pixelx;
uint8_t* pixeli;
for (uint32_t y = 0; y < FONT_HEIGHT; y++)
{
pixelx = &font_atlas.pixels[(y * font_atlas.width + texoffset) * BPP];
pixeli = image->pixels + ((y * image->width + imgoffset) * BPP);
memcpy(pixeli, pixelx, FONT_WIDTH * BPP);
}
}
//= Public =//
const mlx_texture_t* mlx_get_font(void)
{
return ((const mlx_texture_t*)&font_atlas);
}
int32_t mlx_get_texoffset(char c)
{
const bool _isprint = isprint(c);
// NOTE: Cheesy branchless operation :D
// +2 To skip line separator in texture
return (-1 * !_isprint + ((FONT_WIDTH + 2) * (c - 32)) * _isprint);
}
mlx_image_t* mlx_put_string(mlx_t* mlx, const char* str, int32_t x, int32_t y)
{
MLX_NONNULL(mlx);
MLX_NONNULL(str);
mlx_image_t* strimage;
const size_t len = strlen(str);
if (len > MLX_MAX_STRING)
return ((void*)mlx_error(MLX_STRTOOBIG));
if (!(strimage = mlx_new_image(mlx, len * FONT_WIDTH, FONT_HEIGHT)))
return (NULL);
// Draw the text itself
int32_t imgoffset = 0;
for (size_t i = 0; i < len; i++, imgoffset += FONT_WIDTH)
mlx_draw_char(strimage, mlx_get_texoffset(str[i]), imgoffset);
if (mlx_image_to_window(mlx, strimage, x, y) == -1)
return (mlx_delete_image(mlx, strimage), NULL);
return (strimage);
}

63
MLX42/src/mlx_cursor.c Normal file
View File

@ -0,0 +1,63 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_cursor.c :+: :+: */
/* +:+ */
/* By: W2Wizard <main@w2wizard.dev> +#+ */
/* +#+ */
/* Created: 2022/01/18 20:10:54 by W2Wizard #+# #+# */
/* Updated: 2023/03/09 11:11:45 by W2Wizard ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Public =//
mlx_win_cursor_t* mlx_create_std_cursor(cursor_t type)
{
MLX_ASSERT(type >= MLX_CURSOR_ARROW && type < MLX_CURSOR_VRESIZE, "Invalid standard cursor type");
GLFWcursor* cursor;
if ((cursor = glfwCreateStandardCursor(type)))
return (cursor);
return ((void *)mlx_error(MLX_MEMFAIL));
}
mlx_win_cursor_t* mlx_create_cursor(mlx_texture_t* texture)
{
MLX_NONNULL(texture);
GLFWcursor* cursor;
GLFWimage image = (GLFWimage) {
.width = texture->width,
.height = texture->height,
.pixels = texture->pixels
};
if ((cursor = glfwCreateCursor(&image, 0, 0)))
return (cursor);
return ((void *)mlx_error(MLX_MEMFAIL));
}
void mlx_destroy_cursor(mlx_win_cursor_t* cursor)
{
MLX_NONNULL(cursor);
glfwDestroyCursor(cursor);
}
void mlx_set_cursor(mlx_t* mlx, mlx_win_cursor_t* cursor)
{
MLX_NONNULL(mlx);
MLX_NONNULL(cursor);
glfwSetCursor(mlx->window, cursor);
}
void mlx_set_cursor_mode(mlx_t* mlx, mouse_mode_t mode)
{
MLX_NONNULL(mlx);
glfwSetInputMode(mlx->window, GLFW_CURSOR, mode);
}

47
MLX42/src/mlx_exit.c Normal file
View File

@ -0,0 +1,47 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_exit.c :+: :+: */
/* +:+ */
/* By: W2Wizard <w2.wizzard@gmail.com> +#+ */
/* +#+ */
/* Created: 2021/12/28 02:43:22 by W2Wizard #+# #+# */
/* Updated: 2022/11/26 14:23:55 by jvan-hal ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Private =//
static void mlx_free_image(void* content)
{
mlx_image_t* img = content;
mlx_freen(4, img->context, img->pixels, img->instances, img);
}
//= Public =//
void mlx_close_window(mlx_t* mlx)
{
MLX_NONNULL(mlx);
glfwSetWindowShouldClose(mlx->window, true);
}
/**
* All of glfw & glads resources are cleaned up by the terminate function.
* Now it's time to clean up our own mess.
*/
void mlx_terminate(mlx_t* mlx)
{
MLX_NONNULL(mlx);
mlx_ctx_t *const mlxctx = mlx->context;
glfwTerminate();
mlx_lstclear((mlx_list_t**)(&mlxctx->hooks), &free);
mlx_lstclear((mlx_list_t**)(&mlxctx->render_queue), &free);
mlx_lstclear((mlx_list_t**)(&mlxctx->images), &mlx_free_image);
mlx_freen(2, mlxctx, mlx);
}

251
MLX42/src/mlx_images.c Normal file
View File

@ -0,0 +1,251 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_images.c :+: :+: */
/* +:+ */
/* By: W2Wizard <main@w2wizard.dev> +#+ */
/* +#+ */
/* Created: 2021/12/28 02:29:06 by W2Wizard #+# #+# */
/* Updated: 2023/03/30 16:36:39 by ntamayo- ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Private =//
void mlx_flush_batch(mlx_ctx_t* mlx)
{
if (mlx->batch_size <= 0)
return;
glBindBuffer(GL_ARRAY_BUFFER, mlx->vbo);
glBufferData(GL_ARRAY_BUFFER, mlx->batch_size * sizeof(vertex_t), mlx->batch_vertices, GL_STATIC_DRAW);
glDrawArrays(GL_TRIANGLES, 0, mlx->batch_size);
mlx->batch_size = 0;
memset(mlx->bound_textures, 0, sizeof(mlx->bound_textures));
}
static int8_t mlx_bind_texture(mlx_ctx_t* mlx, mlx_image_t* img)
{
const GLint handle = (GLint)((mlx_image_ctx_t*)img->context)->texture;
// Attempt to bind the texture, or obtain the index if it is already bound.
for (int8_t i = 0; i < 16; i++)
{
if (mlx->bound_textures[i] == handle)
return (i);
if (mlx->bound_textures[i] == 0)
{
mlx->bound_textures[i] = handle;
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, handle);
return (i);
}
}
// If no free slot was found, flush the batch and assign the texture to the first available slot
mlx_flush_batch(mlx);
mlx->bound_textures[0] = handle;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, handle);
return (0);
}
/**
* Internal function to draw a single instance of an image
* to the screen.
*/
void mlx_draw_instance(mlx_ctx_t* mlx, mlx_image_t* img, mlx_instance_t* instance)
{
float w = (float) img->width;
float h = (float) img->height;
float x = (float) instance->x;
float y = (float) instance->y;
float z = (float) instance->z;
int8_t tex = mlx_bind_texture(mlx, img);
vertex_t vertices[6] = {
(vertex_t){x, y, z, 0.f, 0.f, tex},
(vertex_t){x + w, y + h, z, 1.f, 1.f, tex},
(vertex_t){x + w, y, z, 1.f, 0.f, tex},
(vertex_t){x, y, z, 0.f, 0.f, tex},
(vertex_t){x, y + h, z, 0.f, 1.f, tex},
(vertex_t){x + w, y + h, z, 1.f, 1.f, tex},
};
memmove(mlx->batch_vertices + mlx->batch_size, vertices, sizeof(vertices));
mlx->batch_size += 6;
if (mlx->batch_size >= MLX_BATCH_SIZE)
mlx_flush_batch(mlx);
}
mlx_instance_t* mlx_grow_instances(mlx_image_t* img, bool* did_realloc)
{
mlx_image_ctx_t* const ctx = img->context;
if (img->count >= ctx->instances_capacity)
{
if (ctx->instances_capacity == 0)
ctx->instances_capacity = img->count;
else
ctx->instances_capacity *= 2;
*did_realloc = true;
return realloc(img->instances, ctx->instances_capacity * sizeof(mlx_instance_t));
}
*did_realloc = false;
return img->instances;
}
//= Public =//
void mlx_set_instance_depth(mlx_instance_t* instance, int32_t zdepth)
{
MLX_NONNULL(instance);
if (instance->z == zdepth)
return;
instance->z = zdepth;
/**
* NOTE: The reason why we don't sort directly is that
* the user might call this function multiple times in a row and we don't
* want to sort for every change. Pre-loop wise that is.
*/
sort_queue = true;
}
int32_t mlx_image_to_window(mlx_t* mlx, mlx_image_t* img, int32_t x, int32_t y)
{
MLX_NONNULL(mlx);
MLX_NONNULL(img);
// Allocate buffers...
img->count++;
bool did_realloc;
mlx_instance_t* instances = mlx_grow_instances(img, &did_realloc);
draw_queue_t* queue = calloc(1, sizeof(draw_queue_t));
if (!instances || !queue)
{
if (did_realloc)
free(instances);
return (free(queue), mlx_error(MLX_MEMFAIL), -1);
}
// Set data...
queue->image = img;
int32_t index = queue->instanceid = img->count - 1;
img->instances = instances;
img->instances[index].x = x;
img->instances[index].y = y;
// NOTE: We keep updating the Z for the convenience of the user.
// Always update Z depth to prevent overlapping images by default.
img->instances[index].z = ((mlx_ctx_t*)mlx->context)->zdepth++;
img->instances[index].enabled = true;
// Add draw call...
sort_queue = true;
mlx_list_t* templst;
if ((templst = mlx_lstnew(queue)))
{
mlx_lstadd_front(&((mlx_ctx_t*)mlx->context)->render_queue, templst);
return (index);
}
return (mlx_freen(2, instances, queue), mlx_error(MLX_MEMFAIL), -1);
}
mlx_image_t* mlx_new_image(mlx_t* mlx, uint32_t width, uint32_t height)
{
MLX_NONNULL(mlx);
if (!width || !height || width > INT16_MAX || height > INT16_MAX)
return ((void*)mlx_error(MLX_INVDIM));
const mlx_ctx_t* mlxctx = mlx->context;
mlx_image_t* newimg = calloc(1, sizeof(mlx_image_t));
mlx_image_ctx_t* newctx = calloc(1, sizeof(mlx_image_ctx_t));
if (!newimg || !newctx)
{
mlx_freen(2, newimg, newctx);
return ((void *)mlx_error(MLX_MEMFAIL));
}
newimg->enabled = true;
newimg->context = newctx;
(*(uint32_t*)&newimg->width) = width;
(*(uint32_t*)&newimg->height) = height;
if (!(newimg->pixels = calloc(width * height, sizeof(int32_t))))
{
mlx_freen(2, newimg, newctx);
return ((void *)mlx_error(MLX_MEMFAIL));
}
mlx_list_t* newentry;
if (!(newentry = mlx_lstnew(newimg)))
{
mlx_freen(3, newimg->pixels, newimg->context, newimg);
return ((void *)mlx_error(MLX_MEMFAIL));
}
// Generate OpenGL texture
glGenTextures(1, &newctx->texture);
glBindTexture(GL_TEXTURE_2D, newctx->texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
mlx_lstadd_front((mlx_list_t**)(&mlxctx->images), newentry);
return (newimg);
}
void mlx_delete_image(mlx_t* mlx, mlx_image_t* image)
{
MLX_NONNULL(mlx);
MLX_NONNULL(image);
mlx_ctx_t* mlxctx = mlx->context;
// Delete all instances in the render queue
mlx_list_t* quelst;
while ((quelst = mlx_lstremove(&mlxctx->render_queue, image, &mlx_equal_inst)))
mlx_freen(2, quelst->content, quelst);
mlx_list_t* imglst;
if ((imglst = mlx_lstremove(&mlxctx->images, image, &mlx_equal_image)))
{
glDeleteTextures(1, &((mlx_image_ctx_t*)image->context)->texture);
mlx_freen(5, image->pixels, image->instances, image->context, imglst, image);
}
}
bool mlx_resize_image(mlx_image_t* img, uint32_t nwidth, uint32_t nheight)
{
MLX_NONNULL(img);
if (!nwidth || !nheight || nwidth > INT16_MAX || nheight > INT16_MAX)
return (mlx_error(MLX_INVDIM));
if (nwidth != img->width || nheight != img->height)
{
uint32_t* origin = (uint32_t*)img->pixels;
float wstep = (float)img->width / nwidth;
float hstep = (float)img->height / nheight;
uint8_t* tempbuff = calloc(nwidth * nheight, BPP);
if (!tempbuff)
return (mlx_error(MLX_MEMFAIL));
img->pixels = tempbuff;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nwidth, nheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->pixels);
uint32_t* destin = (uint32_t*)img->pixels;
for (uint32_t j = 0; j < nheight; j++)
for (uint32_t i = 0; i < nwidth; i++)
destin[j * nwidth + i] = origin[(uint32_t)(j * hstep) * img->width + (uint32_t)(i * wstep)];
(*(uint32_t*)&img->width) = nwidth;
(*(uint32_t*)&img->height) = nheight;
free(origin);
}
return (true);
}

195
MLX42/src/mlx_init.c Normal file
View File

@ -0,0 +1,195 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_init.c :+: :+: */
/* +:+ */
/* By: W2Wizard <main@w2wizard.dev> +#+ */
/* +#+ */
/* Created: 2021/12/28 00:24:30 by W2Wizard #+# #+# */
/* Updated: 2023/02/13 11:36:27 by W2Wizard ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Private =//
static void framebuffer_callback(GLFWwindow *window, int width, int height)
{
(void)window;
glViewport(0, 0, width, height);
}
static bool mlx_create_buffers(mlx_t* mlx)
{
mlx_ctx_t* mlxctx = mlx->context;
mlxctx->zdepth = 0;
glActiveTexture(GL_TEXTURE0);
glGenVertexArrays(1, &(mlxctx->vao));
glGenBuffers(1, &(mlxctx->vbo));
glBindVertexArray(mlxctx->vao);
glBindBuffer(GL_ARRAY_BUFFER, mlxctx->vbo);
// Vertex XYZ coordinates
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), NULL);
glEnableVertexAttribArray(0);
// UV Coordinates
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void *)(sizeof(float) * 3));
glEnableVertexAttribArray(1);
// Texture index
glVertexAttribIPointer(2, 1, GL_BYTE, sizeof(vertex_t), (void *)(sizeof(float) * 5));
glEnableVertexAttribArray(2);
glEnable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture0"), 0);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture1"), 1);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture2"), 2);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture3"), 3);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture4"), 4);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture5"), 5);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture6"), 6);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture7"), 7);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture8"), 8);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture9"), 9);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture10"), 10);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture11"), 11);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture12"), 12);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture13"), 13);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture14"), 14);
glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture15"), 15);
return (true);
}
/**
* Compiles the given shader source code of a given shader type.
* Returns shader object via param.
*
* @param code The shader source code.
* @param Type GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, ...
* @return Non-zero on success, else 0.
*/
static uint32_t mlx_compile_shader(const char* code, int32_t type)
{
GLuint shader;
int32_t success;
char infolog[512] = {0};
if (!code || (shader = glCreateShader(type)) == 0)
return (0);
GLint len = strlen(code);
glShaderSource(shader, 1, &code, &len);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, sizeof(infolog), NULL, infolog);
fprintf(stderr, "%s", infolog);
return (0);
}
return (shader);
}
static bool mlx_init_render(mlx_t* mlx)
{
uint32_t vshader = 0;
uint32_t fshader = 0;
char infolog[512] = {0};
mlx_ctx_t* mlxctx = mlx->context;
glfwMakeContextCurrent(mlx->window);
glfwSetFramebufferSizeCallback(mlx->window, framebuffer_callback);
glfwSetWindowUserPointer(mlx->window, mlx);
glfwSwapInterval(MLX_SWAP_INTERVAL);
// Load all OpenGL function pointers
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
return (mlx_error(MLX_GLADFAIL));
if (!(vshader = mlx_compile_shader(vert_shader, GL_VERTEX_SHADER)))
return (mlx_error(MLX_VERTFAIL));
if (!(fshader = mlx_compile_shader(frag_shader, GL_FRAGMENT_SHADER)))
return (mlx_error(MLX_FRAGFAIL));
if (!(mlxctx->shaderprogram = glCreateProgram()))
return (mlx_error(MLX_SHDRFAIL));
glAttachShader(mlxctx->shaderprogram, vshader);
glAttachShader(mlxctx->shaderprogram, fshader);
glLinkProgram(mlxctx->shaderprogram);
int32_t success;
glGetProgramiv(mlxctx->shaderprogram, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(mlxctx->shaderprogram, sizeof(infolog), NULL, infolog);
fprintf(stderr, "%s", infolog);
return (mlx_error(MLX_SHDRFAIL));
}
glDeleteShader(vshader);
glDeleteShader(fshader);
glUseProgram(mlxctx->shaderprogram);
for (size_t i = 0; i < 16; i++)
mlxctx->bound_textures[i] = 0;
return (true);
}
//= Public =//
// NOTE: https://www.glfw.org/docs/3.3/group__window.html
// Default settings
int32_t mlx_settings[MLX_SETTINGS_MAX] = {false, false, false, true, false};
mlx_errno_t mlx_errno = MLX_SUCCESS;
bool sort_queue = false;
mlx_t* mlx_init(int32_t width, int32_t height, const char* title, bool resize)
{
MLX_NONNULL(title);
MLX_ASSERT(width > 0, "Window width must be positive");
MLX_ASSERT(height > 0, "Window height must be positive");
bool init;
mlx_t* mlx;
if (!(init = glfwInit()))
return ((void*)mlx_error(MLX_GLFWFAIL));
if (!(mlx = calloc(1, sizeof(mlx_t))))
return ((void*)mlx_error(MLX_MEMFAIL));
if (!(mlx->context = calloc(1, sizeof(mlx_ctx_t))))
return (free(mlx), (void*)mlx_error(MLX_MEMFAIL));
mlx_ctx_t* const mlxctx = mlx->context;
mlx->width = width;
mlx->height = height;
mlxctx->initialWidth = width;
mlxctx->initialHeight = height;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_MAXIMIZED, mlx_settings[MLX_MAXIMIZED]);
glfwWindowHint(GLFW_DECORATED, mlx_settings[MLX_DECORATED]);
glfwWindowHint(GLFW_VISIBLE, !mlx_settings[MLX_HEADLESS]);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
glfwWindowHint(GLFW_RESIZABLE, resize);
if (!(mlx->window = glfwCreateWindow(width, height, title, mlx_settings[MLX_FULLSCREEN] ? glfwGetPrimaryMonitor() : NULL, NULL)))
return (mlx_terminate(mlx), (void*)mlx_error(MLX_WINFAIL));
if (!mlx_init_render(mlx) || !mlx_create_buffers(mlx))
return (mlx_terminate(mlx), NULL);
return (mlx);
}
void mlx_set_setting(mlx_settings_t setting, int32_t value)
{
MLX_ASSERT(setting >= 0 && setting < MLX_SETTINGS_MAX, "Invalid settings value");
mlx_settings[setting] = value;
}

49
MLX42/src/mlx_keys.c Normal file
View File

@ -0,0 +1,49 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_keys.c :+: :+: */
/* +:+ */
/* By: W2Wizard <main@w2wizard.dev> +#+ */
/* +#+ */
/* Created: 2022/01/01 21:06:45 by W2Wizard #+# #+# */
/* Updated: 2023/02/13 12:24:40 by W2Wizard ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Private =//
static void mlx_key_callback(GLFWwindow* window, int32_t key, int32_t scancode, int32_t action, int32_t mods)
{
const mlx_t* mlx = glfwGetWindowUserPointer(window);
const mlx_key_t key_hook = ((mlx_ctx_t*)mlx->context)->key_hook;
const mlx_key_data_t callback_data = {
key,
action,
scancode,
mods,
};
key_hook.func(callback_data, key_hook.param);
}
//= Public =//
void mlx_key_hook(mlx_t* mlx, mlx_keyfunc func, void* param)
{
MLX_NONNULL(mlx);
MLX_NONNULL(func);
mlx_ctx_t* mlxctx = mlx->context;
mlxctx->key_hook.func = func;
mlxctx->key_hook.param = param;
glfwSetKeyCallback(mlx->window, mlx_key_callback);
}
bool mlx_is_key_down(mlx_t* mlx, keys_t key)
{
MLX_NONNULL(mlx);
return (glfwGetKey(mlx->window, key) == GLFW_PRESS);
}

118
MLX42/src/mlx_loop.c Normal file
View File

@ -0,0 +1,118 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_loop.c :+: :+: */
/* +:+ */
/* By: W2Wizard <main@w2wizard.dev> +#+ */
/* +#+ */
/* Created: 2021/12/28 01:24:36 by W2Wizard #+# #+# */
/* Updated: 2023/03/28 16:34:17 by W2Wizard ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Private =//
static void mlx_exec_loop_hooks(mlx_t* mlx)
{
const mlx_ctx_t* mlxctx = mlx->context;
mlx_list_t* lstcpy = mlxctx->hooks;
while (lstcpy && !glfwWindowShouldClose(mlx->window))
{
mlx_hook_t* hook = ((mlx_hook_t*)lstcpy->content);
hook->func(hook->param);
lstcpy = lstcpy->next;
}
}
static void mlx_render_images(mlx_t* mlx)
{
mlx_ctx_t* mlxctx = mlx->context;
mlx_list_t* imglst = mlxctx->images;
if (sort_queue)
{
sort_queue = false;
mlx_sort_renderqueue(&mlxctx->render_queue);
}
// Upload image textures to GPU
while (imglst)
{
mlx_image_t* image;
if (!(image = imglst->content)) {
mlx_error(MLX_INVIMG);
return;
}
glBindTexture(GL_TEXTURE_2D, ((mlx_image_ctx_t*)image->context)->texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels);
imglst = imglst->next;
}
// Execute draw calls
mlx_list_t* render_queue = mlxctx->render_queue;
while (render_queue)
{
draw_queue_t* drawcall = render_queue->content;
mlx_instance_t* instance = &drawcall->image->instances[drawcall->instanceid];
if (drawcall && drawcall->image->enabled && instance->enabled)
mlx_draw_instance(mlx->context, drawcall->image, instance);
render_queue = render_queue->next;
}
}
//= Public =//
bool mlx_loop_hook(mlx_t* mlx, void (*f)(void*), void* param)
{
MLX_NONNULL(mlx);
MLX_NONNULL(f);
mlx_hook_t* hook;
if (!(hook = malloc(sizeof(mlx_hook_t))))
return (mlx_error(MLX_MEMFAIL));
mlx_list_t* lst;
if (!(lst = mlx_lstnew(hook)))
{
free(hook);
return (mlx_error(MLX_MEMFAIL));
}
hook->func = f;
hook->param = param;
const mlx_ctx_t *mlxctx = mlx->context;
mlx_lstadd_back((mlx_list_t**)(&mlxctx->hooks), lst);
return (true);
}
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
void mlx_loop(mlx_t* mlx)
{
MLX_NONNULL(mlx);
double start, oldstart = 0;
while (!glfwWindowShouldClose(mlx->window))
{
start = glfwGetTime();
mlx->delta_time = start - oldstart;
oldstart = start;
glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glfwGetWindowSize(mlx->window, &(mlx->width), &(mlx->height));
if ((mlx->width > 1 || mlx->height > 1))
mlx_update_matrix(mlx, mlx->width, mlx->height);
mlx_exec_loop_hooks(mlx);
mlx_render_images(mlx);
mlx_flush_batch(mlx->context);
glfwSwapBuffers(mlx->window);
glfwPollEvents();
}
}

37
MLX42/src/mlx_monitor.c Normal file
View File

@ -0,0 +1,37 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_monitor.c :+: :+: */
/* +:+ */
/* By: W2Wizard <w2.wizzard@gmail.com> +#+ */
/* +#+ */
/* Created: 2022/01/19 17:18:59 by W2Wizard #+# #+# */
/* Updated: 2022/06/27 20:02:38 by lde-la-h ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Public =//
void mlx_get_monitor_size(int32_t index, int32_t* width, int32_t* height)
{
MLX_ASSERT(index >= 0, "Index out of bounds");
MLX_NONNULL(width);
MLX_NONNULL(height);
*width = 0;
*height = 0;
int32_t monitor_count;
GLFWmonitor** monitors = glfwGetMonitors(&monitor_count);
if (index > monitor_count || !monitors)
return;
const GLFWvidmode* vidmode;
if ((vidmode = glfwGetVideoMode(monitors[index])))
{
*width = vidmode->width;
*height = vidmode->height;
}
}

100
MLX42/src/mlx_mouse.c Normal file
View File

@ -0,0 +1,100 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_mouse.c :+: :+: */
/* +:+ */
/* By: W2Wizard <w2.wizzard@gmail.com> +#+ */
/* +#+ */
/* Created: 2022/01/01 23:20:13 by W2Wizard #+# #+# */
/* Updated: 2022/06/29 15:34:25 by lde-la-h ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Private =//
static void mlx_scroll_cb(GLFWwindow* window, double xoffset, double yoffset)
{
const mlx_t* mlx = glfwGetWindowUserPointer(window);
const mlx_scroll_t scroll_hook = ((mlx_ctx_t*)mlx->context)->scroll_hook;
scroll_hook.func(xoffset, yoffset, scroll_hook.param);
}
static void mlx_mouse_cb(GLFWwindow* window, int32_t button, int32_t action, int32_t mods)
{
const mlx_t* mlx = glfwGetWindowUserPointer(window);
const mlx_mouse_t mouse_hook = ((mlx_ctx_t*)mlx->context)->mouse_hook;
mouse_hook.func(button, action, mods, mouse_hook.param);
}
static void mlx_cursor_cb(GLFWwindow* window, double xpos, double ypos)
{
const mlx_t* mlx = glfwGetWindowUserPointer(window);
const mlx_cursor_t cursor_hook = ((mlx_ctx_t*)mlx->context)->cursor_hook;
cursor_hook.func(xpos, ypos, cursor_hook.param);
}
//= Public =//
void mlx_scroll_hook(mlx_t* mlx, mlx_scrollfunc func, void* param)
{
MLX_NONNULL(mlx);
MLX_NONNULL(func);
mlx_ctx_t* const mlxctx = mlx->context;
mlxctx->scroll_hook.func = func;
mlxctx->scroll_hook.param = param;
glfwSetScrollCallback(mlx->window, mlx_scroll_cb);
}
void mlx_mouse_hook(mlx_t* mlx, mlx_mousefunc func, void* param)
{
MLX_NONNULL(mlx);
MLX_NONNULL(func);
mlx_ctx_t* const mlxctx = mlx->context;
mlxctx->mouse_hook.func = func;
mlxctx->mouse_hook.param = param;
glfwSetMouseButtonCallback(mlx->window, mlx_mouse_cb);
}
void mlx_cursor_hook(mlx_t* mlx, mlx_cursorfunc func, void* param)
{
MLX_NONNULL(mlx);
MLX_NONNULL(func);
mlx_ctx_t* const mlxctx = mlx->context;
mlxctx->cursor_hook.func = func;
mlxctx->cursor_hook.param = param;
glfwSetCursorPosCallback(mlx->window, mlx_cursor_cb);
}
bool mlx_is_mouse_down(mlx_t* mlx, mouse_key_t key)
{
MLX_NONNULL(mlx);
return (glfwGetMouseButton(mlx->window, key) == GLFW_PRESS);
}
void mlx_set_mouse_pos(mlx_t* mlx, int32_t x, int32_t y)
{
MLX_NONNULL(mlx);
glfwSetCursorPos(mlx->window, (double)x, (double)y);
}
void mlx_get_mouse_pos(mlx_t* mlx, int32_t* x, int32_t* y)
{
MLX_NONNULL(mlx);
MLX_NONNULL(x);
MLX_NONNULL(y);
double xd, yd;
glfwGetCursorPos(mlx->window, &xd, &yd);
*x = (int32_t)xd;
*y = (int32_t)yd;
}

34
MLX42/src/mlx_put_pixel.c Normal file
View File

@ -0,0 +1,34 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_put_pixel.c :+: :+: */
/* +:+ */
/* By: W2Wizard <w2.wizzard@gmail.com> +#+ */
/* +#+ */
/* Created: 2021/12/28 03:30:13 by W2Wizard #+# #+# */
/* Updated: 2022/06/29 16:00:30 by lde-la-h ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
// BUG: Linux may experience a red hue instead due to endiannes
void mlx_draw_pixel(uint8_t* pixel, uint32_t color)
{
*(pixel++) = (uint8_t)(color >> 24);
*(pixel++) = (uint8_t)(color >> 16);
*(pixel++) = (uint8_t)(color >> 8);
*(pixel++) = (uint8_t)(color & 0xFF);
}
//= Public =//
void mlx_put_pixel(mlx_image_t* image, uint32_t x, uint32_t y, uint32_t color)
{
MLX_NONNULL(image);
MLX_ASSERT(x < image->width, "Pixel is out of bounds");
MLX_ASSERT(y < image->height, "Pixel is out of bounds");
uint8_t* pixelstart = &image->pixels[(y * image->width + x) * BPP];
mlx_draw_pixel(pixelstart, color);
}

137
MLX42/src/mlx_window.c Normal file
View File

@ -0,0 +1,137 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_window.c :+: :+: */
/* +:+ */
/* By: W2wizard <w2wizzard@gmail.com> +#+ */
/* +#+ */
/* Created: 2022/02/08 01:14:59 by W2wizard #+# #+# */
/* Updated: 2022/11/22 09:06:54 by jvan-hal ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Private =//
/**
* Recalculate the view projection matrix, used by images for screen pos
* Reference: https://bit.ly/3KuHOu1 (Matrix View Projection)
*/
void mlx_update_matrix(const mlx_t* mlx, int32_t width, int32_t height)
{
const mlx_ctx_t* mlxctx = mlx->context;
const float depth = mlxctx->zdepth;
/**
* In case the setting to stretch the image is set, we maintain the width and height but not
* the depth.
*/
width = mlx_settings[MLX_STRETCH_IMAGE] ? mlxctx->initialWidth : mlx->width;
height = mlx_settings[MLX_STRETCH_IMAGE] ? mlxctx->initialHeight : mlx->height;
const float matrix[16] = {
2.f / width, 0, 0, 0,
0, 2.f / -(height), 0, 0,
0, 0, -2.f / (depth - -depth), 0,
-1, -(height / -height),
-((depth + -depth) / (depth - -depth)), 1
};
glUniformMatrix4fv(glGetUniformLocation(mlxctx->shaderprogram, "ProjMatrix"), 1, GL_FALSE, matrix);
}
static void mlx_resize_callback(GLFWwindow* window, int32_t width, int32_t height)
{
const mlx_t* mlx = glfwGetWindowUserPointer(window);
const mlx_ctx_t* mlxctx = mlx->context;
if (mlxctx->resize_hook.func)
mlxctx->resize_hook.func(width, height, mlxctx->resize_hook.param);
}
static void mlx_close_callback(GLFWwindow* window)
{
const mlx_t* mlx = glfwGetWindowUserPointer(window);
const mlx_close_t close_hook = ((mlx_ctx_t*)mlx->context)->close_hook;
close_hook.func(close_hook.param);
}
//= Public =//
void mlx_close_hook(mlx_t* mlx, mlx_closefunc func, void* param)
{
MLX_NONNULL(mlx);
MLX_NONNULL(func);
mlx_ctx_t* mlxctx = mlx->context;
mlxctx->close_hook.func = func;
mlxctx->close_hook.param = param;
glfwSetWindowCloseCallback(mlx->window, mlx_close_callback);
}
void mlx_resize_hook(mlx_t* mlx, mlx_resizefunc func, void* param)
{
MLX_NONNULL(mlx);
MLX_NONNULL(func);
mlx_ctx_t* mlxctx = mlx->context;
mlxctx->resize_hook.func = func;
mlxctx->resize_hook.param = param;
glfwSetWindowSizeCallback(mlx->window, mlx_resize_callback);
}
void mlx_set_icon(mlx_t* mlx, mlx_texture_t* image)
{
MLX_NONNULL(mlx);
MLX_NONNULL(image);
const GLFWimage icon = {
.width = image->width,
.height = image->height,
.pixels = image->pixels
};
glfwSetWindowIcon(mlx->window, 1, &icon);
}
void mlx_set_window_pos(mlx_t* mlx, int32_t xpos, int32_t ypos)
{
MLX_NONNULL(mlx);
glfwSetWindowPos(mlx->window, xpos, ypos);
}
void mlx_get_window_pos(mlx_t* mlx, int32_t* xpos, int32_t* ypos)
{
MLX_NONNULL(mlx);
MLX_NONNULL(xpos);
MLX_NONNULL(ypos);
glfwGetWindowPos(mlx->window, xpos, ypos);
}
void mlx_set_window_size(mlx_t* mlx, int32_t new_width, int32_t new_height)
{
MLX_NONNULL(mlx);
mlx->width = new_width;
mlx->height = new_height;
glfwSetWindowSize(mlx->window, new_width, new_height);
}
void mlx_set_window_limit(mlx_t* mlx, int32_t min_w, int32_t min_h, int32_t max_w, int32_t max_h)
{
MLX_NONNULL(mlx);
glfwSetWindowSizeLimits(mlx->window, min_w, min_h, max_w, max_h);
}
void mlx_set_window_title(mlx_t* mlx, const char* title)
{
MLX_NONNULL(mlx);
MLX_NONNULL(title);
glfwSetWindowTitle(mlx->window, title);
}

View File

@ -0,0 +1,35 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_png.c :+: :+: */
/* +:+ */
/* By: W2Wizard <w2.wizzard@gmail.com> +#+ */
/* +#+ */
/* Created: 2022/02/16 23:11:29 by W2Wizard #+# #+# */
/* Updated: 2022/06/27 19:55:06 by lde-la-h ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Public =//
mlx_texture_t* mlx_load_png(const char* path)
{
MLX_NONNULL(path);
mlx_texture_t* image;
if (!(image = malloc(sizeof(mlx_texture_t))))
return ((void*)mlx_error(MLX_MEMFAIL));
uint32_t error;
image->bytes_per_pixel = BPP;
if ((error = lodepng_decode32_file(&image->pixels, &image->width, &image->height, path)))
{
free(image);
// Explicitly print error on purpose
fprintf(stderr, "MLX42: LodePNG: %s\n", lodepng_error_text(error));
return ((void*)mlx_error(MLX_INVPNG));
}
return (image);
}

View File

@ -0,0 +1,42 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_texture.c :+: :+: */
/* +:+ */
/* By: W2Wizard <main@w2wizard.dev> +#+ */
/* +#+ */
/* Created: 2022/02/17 01:02:24 by W2Wizard #+# #+# */
/* Updated: 2023/03/09 11:03:47 by W2Wizard ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Public =//
mlx_image_t* mlx_texture_to_image(mlx_t* mlx, mlx_texture_t* texture)
{
MLX_NONNULL(mlx);
MLX_NONNULL(texture);
mlx_image_t* image = mlx_new_image(mlx, texture->width, texture->height);
if (image == NULL)
return (NULL);
uint8_t* pixelx;
uint8_t* pixeli;
for (uint32_t i = 0; i < texture->height; i++)
{
pixelx = &texture->pixels[(i * texture->width) * texture->bytes_per_pixel];
pixeli = &image->pixels[(i * image->width) * texture->bytes_per_pixel];
memmove(pixeli, pixelx, texture->width * texture->bytes_per_pixel);
}
return (image);
}
void mlx_delete_texture(mlx_texture_t* texture)
{
MLX_NONNULL(texture);
mlx_freen(2, texture->pixels, texture);
}

View File

@ -0,0 +1,208 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_xpm42.c :+: :+: */
/* +:+ */
/* By: W2Wizard <w2.wizzard@gmail.com> +#+ */
/* +#+ */
/* Created: 2021/12/28 03:42:29 by W2Wizard #+# #+# */
/* Updated: 2022/06/27 19:58:33 by lde-la-h ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
/**
* XPM is an obscure image format which can't seem to make up its mind
* whether it wants to be written in C code or not.
*
* https://en.wikipedia.org/wiki/X_PixMap
*
* This might anger some but instead I decided to write my own
* image format, very similar to XPM2, which seems to be the better
* option between the 3 versions. The only difference is in the
* header which carries the file type, width, height, color count
* and finally color type aka 'c' for RGBA8 or 'm' for monochrome
* output.
*
* The changes, in my opinion, very much simplify the XPM format
* into something literally anybody can use without much guessing
* as to what does what.
*
* Additionally with the C style format, the idea is that you simply include
* it directly into the compilation of the program (since it's just C).
*
* As convenient as this is, I just find it hideous especially the XPM3 variant.
* By sticking to the XPM style format, conversion should be very easy and
* straightforward to this format however.
*/
//= Private =//
/**
* Parses HEX color channel e.g: "0F"
*
* @param channel The 2 character string to parse.
* @return Int value of the channel.
*/
static uint8_t mlx_parse_hex_channel(char* channel)
{
char temp_chan[] = {channel[0], channel[1], '\0'};
return (strtol(temp_chan, NULL, 16));
}
/**
* Parses the XPM color value entry e.g: ".X #00FF00FF"
* into the color table while also verifying the format.
*
* @param xpm The XPM.
* @param line The line to parse.
* @param ctable The color hash table.
* @param s Size of the hash table
* @return True or false depending on if it sucessfully parsed the line.
*/
static bool mlx_insert_xpm_entry(xpm_t* xpm, char* line, uint32_t* ctable, size_t s)
{
// NOTE: uintptr because windows likes to complain...
// Verify the length of the Pixel string by checking backwards for the first
// occurence of a space and then check the distance by comparing with cpp.
if (((uintptr_t)strrchr(line, ' ') - (uintptr_t)line) != (uint64_t)xpm->cpp)
return (false);
if (!isspace(line[xpm->cpp]) || line[xpm->cpp + 1] != '#' || !isalnum(line[xpm->cpp + 2]))
return (false);
uint32_t color = 0;
size_t start_offset = xpm->cpp + 2;
color |= mlx_parse_hex_channel(line + start_offset) << 24;
color |= mlx_parse_hex_channel(line + start_offset + 2) << 16;
color |= mlx_parse_hex_channel(line + start_offset + 4) << 8;
color |= mlx_parse_hex_channel(line + start_offset + 6);
int32_t index = mlx_fnv_hash(line, xpm->cpp) % s;
ctable[index] = xpm->mode == 'm' ? mlx_rgba_to_mono(color) : color;
return (true);
}
/**
* Retrieves the pixel data line by line and then processes each pixel
* by hashing the characters and looking it up from the color table.
*
* @param xpm The XPM.
* @param file The filepath to the XPM42 file.
* @param ctable The color hash table.
* @param s Size of the hash table.
* @return True or false depending on if it sucessfully parsed the line.
*/
static bool mlx_read_data(xpm_t* xpm, FILE* file, uint32_t* ctable, size_t s)
{
size_t line_len;
char* line = NULL;
for (int64_t y_xpm = 0; y_xpm < xpm->texture.height; y_xpm++)
{
if (!mlx_getline(&line, &line_len, file))
return (free(line), false);
if (line[line_len - 1] == '\n')
line_len--;
if (line_len != xpm->texture.width * xpm->cpp)
return (free(line), false);
// NOTE: Copy pixel by pixel as we need to retrieve the hash table.
for (int64_t x_xpm = 0, x_line = 0; x_xpm < xpm->texture.width; x_xpm++, x_line += xpm->cpp)
{
uint8_t* pixelstart = &xpm->texture.pixels[(y_xpm * xpm->texture.width + x_xpm) * BPP];
mlx_draw_pixel(pixelstart, ctable[mlx_fnv_hash(&line[x_line], xpm->cpp) % s]);
}
}
free(line);
return (true);
}
/**
* For quick lookups we basically create a stack allocated lookup
* table with every ascii character in it. This should help avoid a O(n)
* case and give us a O(1) for very fast look ups.
*
* Downside is we still need to iterate over each pixel to solve its color.
* So I hope this makes it at least a bit faster.
*
* TODO: This buffer might be way to big! Do actual collision checks,
* for now just straight up raw dog this.
*/
static bool mlx_read_table(xpm_t* xpm, FILE* file)
{
char* line = NULL;
size_t line_len;
uint32_t ctable[UINT16_MAX] = {0};
for (int32_t i = 0; i < xpm->color_count; i++)
{
if (!mlx_getline(&line, &line_len, file))
return (free(line), false);
if (!mlx_insert_xpm_entry(xpm, line, ctable, (sizeof(ctable) / BPP)))
return (free(line), false);
}
free(line);
return (mlx_read_data(xpm, file, ctable, (sizeof(ctable) / BPP)));
}
/**
* Reads the XPM42 file header which usually consists of a
* file type declaration of "!XPM42" followed by the next line
* containing image information such as width, height, unique color
* count and finally the color mode. Which is either c for Color or
* m for Monochrome.
*/
static bool mlx_read_xpm_header(xpm_t* xpm, FILE *file)
{
int32_t flagc;
char buffer[64] = {0};
// Check file type dec...
if (!fgets(buffer, sizeof(buffer), file))
return (false);
if (strncmp(buffer, "!XPM42\n", sizeof(buffer)) != 0)
return (false);
// Get header info ...
if (!fgets(buffer, sizeof(buffer), file))
return (false);
flagc = sscanf(buffer, "%i %i %i %i %c\n", &xpm->texture.width, &xpm->texture.height, &xpm->color_count, &xpm->cpp, &xpm->mode);
if (flagc < 4 || xpm->texture.width > INT16_MAX || xpm->texture.height > INT16_MAX || \
!(xpm->mode == 'c' || xpm->mode == 'm') || xpm->cpp > 10)
return (false);
xpm->texture.bytes_per_pixel = BPP;
xpm->texture.pixels = calloc(xpm->texture.width * xpm->texture.height, sizeof(int32_t));
return (xpm->texture.pixels != NULL ? mlx_read_table(xpm, file) : false);
}
//= Public =//
xpm_t* mlx_load_xpm42(const char* path)
{
FILE* file;
xpm_t* xpm = NULL;
MLX_NONNULL(path);
if (!strstr(path, ".xpm42"))
return ((void*)mlx_error(MLX_INVEXT));
if (!(file = fopen(path, "r")))
return ((void*)mlx_error(MLX_INVFILE));
if (!(xpm = calloc(1, sizeof(xpm_t))))
return ((void*)mlx_error(MLX_MEMFAIL));
if (!mlx_read_xpm_header(xpm, file))
{
mlx_freen(2, xpm->texture.pixels, xpm);
mlx_error(MLX_INVXPM);
xpm = NULL;
}
fclose(file);
return (xpm);
}
void mlx_delete_xpm42(xpm_t* xpm)
{
MLX_NONNULL(xpm);
free(xpm->texture.pixels);
free(xpm);
}

View File

@ -0,0 +1,31 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_comparison.c :+: :+: */
/* +:+ */
/* By: jvan-hal <jvan-hal@student.codam.nl> +#+ */
/* +#+ */
/* Created: 2023/01/31 17:20:19 by jvan-hal #+# #+# */
/* Updated: 2023/01/31 17:23:49 by jvan-hal ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Private =//
bool mlx_equal_image(void* lstcontent, void* value)
{
const mlx_image_t* lcontent = lstcontent;
const mlx_image_t* lvalue = value;
return (lcontent == lvalue);
}
bool mlx_equal_inst(void* lstcontent, void* value)
{
const draw_queue_t* lcontent = lstcontent;
const mlx_image_t* lvalue = value;
return (lcontent->image == lvalue);
}

View File

@ -0,0 +1,60 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_error.c :+: :+: */
/* +:+ */
/* By: W2Wizard <w2.wizzard@gmail.com> +#+ */
/* +#+ */
/* Created: 2021/12/28 02:51:54 by W2Wizard #+# #+# */
/* Updated: 2022/11/22 08:50:15 by jvan-hal ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Private =//
// English description of the error codes.
static const char* mlx_errors[MLX_ERRMAX] = {
"No Errors",
"File has invalid extension",
"Failed to open the file",
"PNG file is invalid or corrupted",
"XPM42 file is invalid or corrupted",
"The specified X or Y positions are out of bounds",
"The specified Width or Height dimensions are out of bounds",
"The provided image is invalid, might indicate mismanagement of images",
"Failed to compile the vertex shader.",
"Failed to compile the fragment shader.",
"Failed to compile the shaders.",
"Failed to allocate memory",
"Failed to initialize GLAD",
"Failed to initialize GLFW",
"Failed to create window",
"String is too big to be drawn",
};
/**
* Functions to set the error number, simply for convenience.
*
* @param val The error value.
* @return Always false
*/
bool mlx_error(mlx_errno_t val)
{
mlx_errno = val;
#ifndef NDEBUG
fprintf(stderr, "MLX42: %s", mlx_strerror(mlx_errno));
#endif
return (false);
}
//= Public =//
const char* mlx_strerror(mlx_errno_t val)
{
MLX_ASSERT(val >= 0, "Index must be positive");
MLX_ASSERT(val < MLX_ERRMAX, "Index out of bounds");
return (mlx_errors[val]);
}

176
MLX42/src/utils/mlx_list.c Normal file
View File

@ -0,0 +1,176 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_list.c :+: :+: */
/* +:+ */
/* By: W2Wizard <main@w2wizard.dev> +#+ */
/* +#+ */
/* Created: 2021/12/28 01:53:51 by W2Wizard #+# #+# */
/* Updated: 2023/02/27 11:31:01 by W2Wizard ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Private =//
int32_t mlx_lstsize(mlx_list_t* lst)
{
int32_t i;
for (i = 0; lst != NULL; i++)
lst = lst->next;
return (i);
}
static void mlx_lstdelone(mlx_list_t* lst, void (*del)(void *))
{
if (del != NULL)
del(lst->content);
free(lst);
}
void mlx_lstclear(mlx_list_t** lst, void (*del)(void*))
{
mlx_list_t* next_lst;
while (*lst != NULL)
{
next_lst = (*lst)->next;
mlx_lstdelone(*lst, del);
*lst = next_lst;
}
}
mlx_list_t* mlx_lstnew(void* content)
{
mlx_list_t* out = NULL;
if ((out = malloc(sizeof(mlx_list_t))))
{
out->content = content;
out->next = NULL;
out->prev = NULL;
}
return (out);
}
mlx_list_t* mlx_lstlast(mlx_list_t* lst)
{
if (!lst)
return (NULL);
while (lst->next)
lst = lst->next;
return (lst);
}
void mlx_lstadd_back(mlx_list_t** lst, mlx_list_t* new)
{
if (!lst || !new)
return;
if (!*lst)
*lst = new;
else
{
mlx_list_t* temp = mlx_lstlast(*lst);
new->prev = temp;
temp->next = new;
}
}
void mlx_lstadd_front(mlx_list_t** lst, mlx_list_t* new)
{
if (!lst || !new)
return;
if ((*lst) != NULL)
(*lst)->prev = new;
new->next = *lst;
new->prev = NULL;
*lst = new;
}
/**
* Removes the specified content from the list, if found.
* Also fixes any relinking that might be needed.
*
* @param[in] lst The list
* @param[in] comp Function to check if the content and value are the same.
* @returns The removed element, clean up as you wish.
*/
mlx_list_t* mlx_lstremove(mlx_list_t** lst, void* value, bool (*comp)(void*, void*))
{
mlx_list_t* lstcpy = *lst;
while (lstcpy && !comp(lstcpy->content, value))
lstcpy = lstcpy->next;
if (lstcpy == NULL)
return (NULL);
if (lstcpy == *lst)
*lst = lstcpy->next;
if (lstcpy->next != NULL)
lstcpy->next->prev = lstcpy->prev;
if (lstcpy->prev != NULL)
lstcpy->prev->next = lstcpy->next;
return (lstcpy);
}
// Retrieve Z value from queue.
static int32_t mlx_getzdata(mlx_list_t* entry)
{
const draw_queue_t* queue = entry->content;
return (queue->image->instances[queue->instanceid].z);
}
// Insert the entry back into head sorted.
static void mlx_insertsort(mlx_list_t** head, mlx_list_t* new)
{
mlx_list_t* current;
if (*head == NULL)
*head = new;
else if (mlx_getzdata(*head) >= mlx_getzdata(new))
{
new->next = *head;
new->next->prev = new;
*head = new;
}
else
{
current = *head;
// Find insertion location.
while (current->next != NULL && mlx_getzdata(current->next) < mlx_getzdata(new))
current = current->next;
new->next = current->next;
// Insert at the end
if (current->next != NULL)
new->next->prev = new;
current->next = new;
new->prev = current;
}
}
/**
* Okay-ish sorting algorithm to sort the render queue / doubly linked list.
* We need to do this to fix transparency.
*
* @param lst The render queue.
*/
void mlx_sort_renderqueue(mlx_list_t** lst)
{
mlx_list_t* sorted = NULL;
mlx_list_t* lstcpy = *lst;
while (lstcpy != NULL)
{
mlx_list_t* next = lstcpy->next;
// Separate entry out of list and insert it back but sorted.
lstcpy->prev = lstcpy->next = NULL;
mlx_insertsort(&sorted, lstcpy);
lstcpy = next;
}
*lst = sorted;
}

131
MLX42/src/utils/mlx_utils.c Normal file
View File

@ -0,0 +1,131 @@
/* ************************************************************************** */
/* */
/* :::::::: */
/* mlx_utils.c :+: :+: */
/* +:+ */
/* By: W2Wizard <w2.wizzard@gmail.com> +#+ */
/* +#+ */
/* Created: 2022/01/03 20:13:17 by W2Wizard #+# #+# */
/* Updated: 2022/11/22 10:56:09 by jvan-hal ######## odam.nl */
/* */
/* ************************************************************************** */
#include "MLX42/MLX42_Int.h"
//= Private =//
/**
* Function to read a file stream line by line, reusing the same output pointer.
* Since the same output pointer is reused it should only be freed once, either on success or failure.
* This function is made to be somewhat similar to getline.
* Getline can't be used directly since it's not standard and therefore not available on all platforms.
*
* @param out Pointer to store output string.
* @param out_size Pointer to store output strings length.
* @param file File stream to read from.
* @return True if line was read, false if EOF was reached or an error occurred.
*/
bool mlx_getline(char** out, size_t* out_size, FILE* file)
{
MLX_NONNULL(out);
MLX_NONNULL(out_size);
MLX_NONNULL(file);
size_t size = 0;
char* temp = NULL;
static char BUFF[GETLINE_BUFF + 1]; // Add space for '\0'
if (*out) *out[0] = '\0';
while (fgets(BUFF, sizeof(BUFF), file))
{
size += strlen(BUFF);
if (!(temp = realloc(*out, sizeof(char) * size + 1)))
return (false);
if (*out == NULL)
memset(temp, '\0', size);
temp[size] = '\0';
*out = temp;
*out_size = size;
strncat(*out, BUFF, size);
if (strrchr(BUFF, '\n'))
return (true);
memset(BUFF, '\0', sizeof(BUFF));
}
return (size);
}
/**
* String hashing algorithm using FNV-1a.
* Source: https://bit.ly/3JcRGHa
*
* @param str The string to hash
* @param len The length of the string.
* @return The hashed output.
*/
uint64_t mlx_fnv_hash(char* str, size_t len)
{
const uint64_t fnv_prime = 0x100000001b3;
const uint64_t fnv_offset = 0xcbf29ce484222325;
uint64_t hash = fnv_offset;
for (size_t i = 0; i < len; i++)
{
hash ^= str[i];
hash *= fnv_prime;
}
return (hash);
}
/**
* Utility function that lets you free x amount of pointers.
*
* @param count The amount of args provided.
* @param ... Any form of pointer.
* @return False, this is simply for convenience when necessary.
*/
bool mlx_freen(int32_t count, ...)
{
va_list args;
va_start(args, count);
for (int32_t i = 0; i < count; i++)
free(va_arg(args, void*));
va_end(args);
return (false);
}
/**
* Converts an RGBA value to a monochrome/grayscale value.
* It does so using specific weights for each channel.
*
* @see https://goodcalculators.com/rgb-to-grayscale-conversion-calculator/
*
* @param color The input RGBA value.
* @return The rgba value converted to a grayscale color.
*/
uint32_t mlx_rgba_to_mono(uint32_t color)
{
const uint8_t r = 0.299f * ((color >> 24) & 0xFF);
const uint8_t g = 0.587f * ((color >> 16) & 0xFF);
const uint8_t b = 0.114f * ((color >> 8) & 0xFF);
const uint8_t y = r + g + b;
return (y << 24 | y << 16 | y << 8 | (color & 0xFF));
}
//= Public =//
double mlx_get_time(void)
{
return (glfwGetTime());
}
void mlx_focus(mlx_t* mlx)
{
MLX_NONNULL(mlx);
glfwFocusWindow(mlx->window);
}