summaryrefslogtreecommitdiff
path: root/src/konsofren.h
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-07 04:11:02 +0800
committerKonAki <konaki@anokonaki.org>2026-10-07 04:11:02 +0800
commitf4a0c7936a57bc63381556188fa3de2903b2dc99 (patch)
tree5e0d072504e0cb675ff76ebfcbe30b2f19c72a06 /src/konsofren.h
initial commit
Diffstat (limited to 'src/konsofren.h')
-rw-r--r--src/konsofren.h402
1 files changed, 402 insertions, 0 deletions
diff --git a/src/konsofren.h b/src/konsofren.h
new file mode 100644
index 0000000..a209a88
--- /dev/null
+++ b/src/konsofren.h
@@ -0,0 +1,402 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * konsofren.h
+ *
+ * Copyright (c) 2026 KonAki
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef KONSOFREN_H
+#define KONSOFREN_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define KON_BACKGROUND_COLOR 0xFF05050A
+
+typedef struct kon_framebuffer {
+ int width, height;
+ uint32_t *data;
+} kon_framebuffer_t;
+
+typedef kon_framebuffer_t kon_image;
+
+typedef enum kon_imageFormat {
+ konFormatRGBA8 = 0,
+ konFormatABGR8,
+ konFormatARGB8,
+ konFormatBGRA8
+} kon_imageFormat_t;
+
+/*** framebuffer declarations ***/
+
+kon_framebuffer_t *kon_createFramebuffer(int width, int height);
+void kon_freeFramebuffer(kon_framebuffer_t *fb);
+void kon_putPixel(kon_framebuffer_t *fb, int x, int y, uint32_t color);
+void kon_clearFramebuffer(kon_framebuffer_t *fb, uint32_t color);
+void kon_resizeFramebuffer(kon_framebuffer_t *fb, int width, int height);
+void kon_exportPixels(kon_framebuffer_t *fb, kon_imageFormat_t format, uint8_t *out);
+
+/*** image declarations ***/
+
+kon_image *kon_loadImage(const uint8_t *pixels, int width, int height, kon_imageFormat_t format);
+void kon_freeImage(kon_image *image);
+
+void kon_drawImage(kon_framebuffer_t *fb, int x, int y, int width, int height, kon_image *image);
+
+/*** draw function declarations ***/
+
+void kon_drawRectangle(kon_framebuffer_t *fb, int x, int y, int width, int height, uint32_t color);
+void kon_fillRectangle(kon_framebuffer_t *fb, int x, int y, int width, int height, uint32_t color);
+void kon_drawLine(kon_framebuffer_t *fb, int x0, int y0, int x1, int y1, uint32_t color);
+void kon_drawCircle(kon_framebuffer_t *fb, int center_x, int center_y, int radius, uint32_t color);
+void kon_fillCircle(kon_framebuffer_t *fb, int center_x, int center_y, int radius, uint32_t color);
+
+/*** implementation ***/
+
+#ifdef KONSOFREN_IMPLEMENTATION
+
+/*** private helper ***/
+
+static inline uint32_t kon_blendColor(uint32_t dst, uint32_t src) {
+
+ uint8_t src_a = (src >> 24) & 0xFF;
+ uint8_t src_r = (src >> 16) & 0xFF;
+ uint8_t src_g = (src >> 8) & 0xFF;
+ uint8_t src_b = (src >> 0) & 0xFF;
+
+ if (src_a == 0xFF) {
+ return src;
+ }
+
+ uint8_t dst_a = (dst >> 24) & 0xFF;
+ uint8_t dst_r = (dst >> 16) & 0xFF;
+ uint8_t dst_g = (dst >> 8) & 0xFF;
+ uint8_t dst_b = (dst >> 0) & 0xFF;
+
+ uint8_t inv_a = 255 - src_a;
+
+ uint8_t out_r = (src_r * src_a + dst_r * inv_a) / 255;
+ uint8_t out_g = (src_g * src_a + dst_g * inv_a) / 255;
+ uint8_t out_b = (src_b * src_a + dst_b * inv_a) / 255;
+
+ return ((uint32_t)dst_a << 24) | ((uint32_t)out_r << 16) | ((uint32_t)out_g << 8) | (uint32_t)out_b;
+}
+
+/*** framebuffer implementation ***/
+
+kon_framebuffer_t *kon_createFramebuffer(int width, int height) {
+ kon_framebuffer_t *fb = malloc(sizeof(kon_framebuffer_t));
+ if (!fb) return NULL;
+
+ fb->data = calloc((size_t)width * height, sizeof(uint32_t));
+ if (!fb->data) {
+ free(fb);
+ return NULL;
+ }
+
+ fb->width = width;
+ fb->height = height;
+ return fb;
+}
+
+void kon_freeFramebuffer(kon_framebuffer_t *fb) {
+ if (!fb) return;
+
+ free(fb->data);
+ free(fb);
+}
+
+void kon_putPixel(kon_framebuffer_t *fb, int x, int y, uint32_t color) {
+ if (!fb) return;
+
+ if (x < 0 || x >= fb->width || y < 0 || y >= fb->height) return;
+
+ uint32_t dst = fb->data[y * fb->width + x];
+ fb->data[y * fb->width + x] = kon_blendColor(dst, color);
+}
+
+void kon_clearFramebuffer(kon_framebuffer_t *fb, uint32_t color) {
+ if (!fb) return;
+
+ for (int i = 0; i < fb->width * fb->height; i++) {
+ /* did it directly to not check overhead because of the if in bounds check */
+ fb->data[i] = color;
+ }
+}
+
+void kon_resizeFramebuffer(kon_framebuffer_t *fb, int width, int height) {
+ if (!fb) return;
+
+ uint32_t *tmp = realloc(fb->data, (size_t)width * height * sizeof(uint32_t));
+ if (!tmp) return;
+
+ fb->data = tmp;
+ fb->width = width;
+ fb->height = height;
+
+ kon_clearFramebuffer(fb, KON_BACKGROUND_COLOR);
+}
+
+void kon_exportPixels(kon_framebuffer_t *fb, kon_imageFormat_t format, uint8_t *out) {
+ if (!fb || !out) return;
+
+ int fbSize = fb->width * fb->height;
+
+ for (int i = 0; i < fbSize; i++) {
+ uint32_t color = fb->data[i];
+
+ uint8_t a = (color >> 24) & 0xFF;
+ uint8_t r = (color >> 16) & 0xFF;
+ uint8_t g = (color >> 8) & 0xFF;
+ uint8_t b = (color >> 0) & 0xFF;
+
+ switch (format) {
+ case konFormatRGBA8:
+ out[i * 4 + 0] = r;
+ out[i * 4 + 1] = g;
+ out[i * 4 + 2] = b;
+ out[i * 4 + 3] = a;
+ break;
+ case konFormatABGR8:
+ out[i * 4 + 0] = a;
+ out[i * 4 + 1] = b;
+ out[i * 4 + 2] = g;
+ out[i * 4 + 3] = r;
+ break;
+ case konFormatARGB8:
+ out[i * 4 + 0] = a;
+ out[i * 4 + 1] = r;
+ out[i * 4 + 2] = g;
+ out[i * 4 + 3] = b;
+ break;
+ case konFormatBGRA8:
+ out[i * 4 + 0] = b;
+ out[i * 4 + 1] = g;
+ out[i * 4 + 2] = r;
+ out[i * 4 + 3] = a;
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+/*** draw functions implementation ***/
+
+void kon_drawRectangle(kon_framebuffer_t *fb, int x, int y, int width, int height, uint32_t color) {
+ if (!fb) return;
+
+ kon_drawLine(fb, x, y, x + width, y, color);
+ kon_drawLine(fb, x, y + height, x + width, y + height, color);
+ kon_drawLine(fb, x, y, x, y + height, color);
+ kon_drawLine(fb, x + width, y, x + width, y + height, color);
+}
+
+void kon_fillRectangle(kon_framebuffer_t *fb, int x, int y, int width, int height, uint32_t color) {
+ if (!fb) return;
+
+ if (width < 0 || height < 0) return;
+
+ if (x < 0) {
+ width += x;
+ x = 0;
+ }
+ if (y < 0) {
+ height += y;
+ y = 0;
+ }
+
+ if (x + width > fb->width ) width = fb->width - x;
+ if (y + height > fb->height) height = fb->height - y;
+
+ for (int offset_y = 0; offset_y < height; offset_y++) {
+ for (int offset_x = 0; offset_x < width; offset_x++) {
+ uint32_t dst = fb->data[(offset_y + y) * fb->width + (offset_x + x)];
+ fb->data[(y + offset_y) * fb->width + (x + offset_x)] = kon_blendColor(dst, color);
+
+ }
+ }
+}
+
+void kon_drawLine(kon_framebuffer_t *fb, int x0, int y0, int x1, int y1, uint32_t color) {
+ if (!fb) return;
+
+ int dx = abs(x1 - x0);
+ int dy = abs(y1 - y0);
+
+ int sx = (x0 < x1) ? 1 : -1;
+ int sy = (y0 < y1) ? 1 : -1;
+
+ int err = dx - dy;
+
+ for (;;) {
+ kon_putPixel(fb, x0, y0, color);
+
+ if (x0 == x1 && y0 == y1) break;
+
+ int e2 = 2 * err;
+
+ if (e2 > -dy) {
+ err -= dy;
+ x0 += sx;
+ }
+
+ if (e2 < dx) {
+ err += dx;
+ y0 += sy;
+ }
+ }
+}
+
+void kon_drawCircle(kon_framebuffer_t *fb, int center_x, int center_y, int radius, uint32_t color) {
+ if (!fb) return;
+ if (radius <= 0) return;
+
+ int x = radius;
+ int y = 0;
+ int err = 1 - radius;
+
+ while (x >= y) {
+ kon_putPixel(fb, center_x + x, center_y + y, color);
+ kon_putPixel(fb, center_x + y, center_y + x, color);
+ kon_putPixel(fb, center_x - y, center_y + x, color);
+ kon_putPixel(fb, center_x - x, center_y + y, color);
+ kon_putPixel(fb, center_x - x, center_y - y, color);
+ kon_putPixel(fb, center_x - y, center_y - x, color);
+ kon_putPixel(fb, center_x + y, center_y - x, color);
+ kon_putPixel(fb, center_x + x, center_y - y, color);
+
+ y += 1;
+
+ if (err < 0) {
+ err += 2 * y + 1;
+ } else {
+ x -= 1;
+ err += 2 * (y - x) + 1;
+ }
+ }
+}
+
+void kon_fillCircle(kon_framebuffer_t *fb, int center_x, int center_y, int radius, uint32_t color) {
+ if (!fb) return;
+ if (radius <= 0) return;
+
+ int x = radius;
+ int y = 0;
+ int err = 1 - radius;
+
+ while (x >= y) {
+ kon_drawLine(fb, center_x - x, center_y + y, center_x + x, center_y + y, color);
+ kon_drawLine(fb, center_x - x, center_y - y, center_x + x, center_y - y, color);
+ kon_drawLine(fb, center_x - y, center_y + x, center_x + y, center_y + x, color);
+ kon_drawLine(fb, center_x - y, center_y - x, center_x + y, center_y - x, color);
+
+ y += 1;
+
+ if (err < 0) {
+ err += 2 * y + 1;
+ } else {
+ x -= 1;
+ err += 2 * (y - x) + 1;
+ }
+ }
+}
+
+/*** image implementation ***/
+
+kon_image *kon_loadImage(const uint8_t *pixels, int width, int height, kon_imageFormat_t format) {
+ kon_image *image = malloc(sizeof(kon_image));
+ if (!image) return NULL;
+
+ image->data = malloc((size_t)width * height * sizeof(uint32_t));
+ if (!image->data) {
+ free(image);
+ return NULL;
+ }
+
+ image->width = width;
+ image->height = height;
+
+ for (int i = 0; i < width * height; i++) {
+ uint8_t r, g, b, a;
+
+ switch(format) {
+ case konFormatRGBA8:
+ r = pixels[i * 4 + 0];
+ g = pixels[i * 4 + 1];
+ b = pixels[i * 4 + 2];
+ a = pixels[i * 4 + 3];
+ break;
+ case konFormatABGR8:
+ a = pixels[i * 4 + 0];
+ b = pixels[i * 4 + 1];
+ g = pixels[i * 4 + 2];
+ r = pixels[i * 4 + 3];
+ break;
+ case konFormatARGB8:
+ a = pixels[i * 4 + 0];
+ r = pixels[i * 4 + 1];
+ g = pixels[i * 4 + 2];
+ b = pixels[i * 4 + 3];
+ break;
+ case konFormatBGRA8:
+ b = pixels[i * 4 + 0];
+ g = pixels[i * 4 + 1];
+ r = pixels[i * 4 + 2];
+ a = pixels[i * 4 + 3];
+ break;
+ default:
+ free(image->data);
+ free(image);
+ return NULL;
+ }
+
+ image->data[i] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b;
+ }
+
+ return image;
+}
+void kon_freeImage(kon_image *image) {
+ kon_freeFramebuffer(image);
+}
+
+void kon_drawImage(kon_framebuffer_t *fb, int x, int y, int width, int height, kon_image *image) {
+ if (!fb || !image) return;
+
+ /* just trying to not draw when it's unnecessary */
+ if (image->width <= 0 || image->height <= 0) return;
+ if (width <= 0 || height <= 0) return;
+ if (x >= fb->width || y >= fb->height) return;
+
+ /* using this loop to make sure we only the parts of the image that are on screen */
+ for (int iy = 0; iy < height; iy++) {
+ for (int ix = 0; ix < width; ix++) {
+ int src_x = ix * image->width / width;
+ int src_y = iy * image->height / height;
+
+ uint32_t color = image->data[src_y * image->width + src_x];
+ kon_putPixel(fb, x+ix, y+iy, color);
+ }
+ }
+}
+
+#endif
+#endif