remove the asc_col4i datatype in favor of a unified asc_color type default tip

Wed, 06 Aug 2025 00:37:01 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 06 Aug 2025 00:37:01 +0200
changeset 256
60014484121c
parent 255
0e0a0bf4f7e4

remove the asc_col4i datatype in favor of a unified asc_color type

and also adds some min and max functions

src/2d.c file | annotate | diff | comparison | revisions
src/ascension/2d.h file | annotate | diff | comparison | revisions
src/ascension/camera.h file | annotate | diff | comparison | revisions
src/ascension/datatypes.h file | annotate | diff | comparison | revisions
src/ascension/mesh.h file | annotate | diff | comparison | revisions
src/ascension/shader.h file | annotate | diff | comparison | revisions
src/ascension/text.h file | annotate | diff | comparison | revisions
src/camera.c file | annotate | diff | comparison | revisions
src/scene.c file | annotate | diff | comparison | revisions
src/shader.c file | annotate | diff | comparison | revisions
src/text.c file | annotate | diff | comparison | revisions
test/snake/snake.c file | annotate | diff | comparison | revisions
--- a/src/2d.c	Tue Aug 05 20:38:11 2025 +0200
+++ b/src/2d.c	Wed Aug 06 00:37:01 2025 +0200
@@ -109,13 +109,13 @@
     asc_shader_upload_model_matrix(shader, node);
 
     if (filled) {
-        asc_shader_upload_col4f(rect_shader->color, rectangle->color);
+        asc_shader_upload_color(rect_shader->color, rectangle->color);
     }
     asc_shader_upload_vec2f(rect_shader->size, rectangle->size);
 
     if (border) {
         asc_shader_upload_float(rect_shader->thickness, rectangle->thickness);
-        asc_shader_upload_col4f(rect_shader->border_color, rectangle->border_color);
+        asc_shader_upload_color(rect_shader->border_color, rectangle->border_color);
     }
     if (round) {
         asc_shader_upload_float(rect_shader->radius, rectangle->radius);
@@ -148,15 +148,15 @@
         // use the main color
         args.border_color = args.color;
     }
-    rectangle->color = asc_col_itof(args.color);
-    rectangle->border_color = asc_col_itof(args.border_color);
+    rectangle->color = args.color;
+    rectangle->border_color = args.border_color;
 
     asc_ptr_cast(AscSceneNode, node, rectangle);
     asc_scene_node_init(node,
         ASC_SCENE_NODE_FUNCS(asc_rectangle),
         .pos2d = ASC_VEC2I(pos_x, pos_y),
         .origin2d = ASC_VEC2I(args.origin_x, args.origin_y),
-        .render_group = (args.filled && args.color.alpha < 255) || args.border_color.alpha < 255
+        .render_group = (args.filled && args.color.alpha < 1) || args.border_color.alpha < 1
                              ? ASC_RENDER_GROUP_2D_BLEND
                              : ASC_RENDER_GROUP_2D_OPAQUE
     );
@@ -254,13 +254,13 @@
     asc_shader_upload_model_matrix(shader, node);
 
     if (filled) {
-        asc_shader_upload_col4f(ellipsis_shader->color, ellipsis->color);
+        asc_shader_upload_color(ellipsis_shader->color, ellipsis->color);
     }
     asc_shader_upload_vec2f(ellipsis_shader->radii, ellipsis->radii);
 
     if (border) {
         asc_shader_upload_float(ellipsis_shader->thickness, ellipsis->thickness);
-        asc_shader_upload_col4f(ellipsis_shader->border_color, ellipsis->border_color);
+        asc_shader_upload_color(ellipsis_shader->border_color, ellipsis->border_color);
     }
 
     // Draw mesh
@@ -295,21 +295,21 @@
         ellipsis->thickness = args.thickness;
     }
 
-    if (!args.filled && asc_memcmpz(&args.border_color, sizeof(args.border_color))) {
+    if (!args.filled && asc_memcmpz(&args.border_color, sizeof(asc_color))) {
         // convenience fallback:
         // when we are drawing an outline but have no explicit border color,
         // use the main color
         args.border_color = args.color;
     }
-    ellipsis->color = asc_col_itof(args.color);
-    ellipsis->border_color = asc_col_itof(args.border_color);
+    ellipsis->color = args.color;
+    ellipsis->border_color = args.border_color;
 
     asc_ptr_cast(AscSceneNode, node, ellipsis);
     asc_scene_node_init(node,
         ASC_SCENE_NODE_FUNCS(asc_ellipsis),
         .pos2d = ASC_VEC2I(pos_x, pos_y),
         .origin3d = ASC_VEC3F(ellipsis->radii.x, ellipsis->radii.y, 0), // use float to avoid cast
-        .render_group = (args.filled && args.color.alpha < 255) || args.border_color.alpha < 255
+        .render_group = (args.filled && args.color.alpha < 1) || args.border_color.alpha < 1
                              ? ASC_RENDER_GROUP_2D_BLEND
                              : ASC_RENDER_GROUP_2D_OPAQUE
     );
--- a/src/ascension/2d.h	Tue Aug 05 20:38:11 2025 +0200
+++ b/src/ascension/2d.h	Wed Aug 06 00:37:01 2025 +0200
@@ -34,8 +34,8 @@
 typedef struct asc_rectangle_s {
     AscSceneNode node;
     AscMesh mesh;
-    asc_col4f color;
-    asc_col4f border_color;
+    asc_color color;
+    asc_color border_color;
     asc_vec2f size;
     float radius;
     float thickness;
@@ -86,11 +86,11 @@
      * If filled is true, this is the fill color.
      * If filled is false, and no border_color is specified, this is used as border color.
      */
-    asc_col4i color;
+    asc_color color;
     /**
      * Border color to be used when thickness is larger than zero and filled is true.
      */
-    asc_col4i border_color;
+    asc_color border_color;
     /**
      * If true, the rectangle will be filled with the specified color.
      * If the thickness is larger than zero, an outline border with that thickness is drawn
@@ -117,8 +117,8 @@
 typedef struct asc_ellipsis_s {
     AscSceneNode node;
     AscMesh mesh;
-    asc_col4f color;
-    asc_col4f border_color;
+    asc_color color;
+    asc_color border_color;
     asc_vec2f radii;
     float thickness;
     bool filled;
@@ -166,11 +166,11 @@
      * If filled is true, this is the fill color.
      * If filled is false, and no border_color is specified, this is used as border color.
      */
-    asc_col4i color;
+    asc_color color;
     /**
      * Border color to be used when thickness is larger than zero and filled is true.
      */
-    asc_col4i border_color;
+    asc_color border_color;
     /**
      * If true, the ellipsis will be filled with the specified color.
      * If the thickness is larger than zero, an outline border with that thickness is drawn
--- a/src/ascension/camera.h	Tue Aug 05 20:38:11 2025 +0200
+++ b/src/ascension/camera.h	Wed Aug 06 00:37:01 2025 +0200
@@ -46,7 +46,7 @@
      */
     asc_camera_viewport_update_func viewport_update_func;
     asc_camera_projection_update_func projection_update_func;
-    asc_col4f clear_color;
+    asc_color clear_color;
     bool viewport_clear;
 };
 
@@ -72,7 +72,7 @@
      * Indicates whether the viewport for this camera shall be cleared before rendering.
      * The active drawing color will be used as the clear color (can be changed in the camera struct, afterward).
      */
-    asc_col4i clear_color;
+    asc_color clear_color;
     bool viewport_clear;
 };
 
--- a/src/ascension/datatypes.h	Tue Aug 05 20:38:11 2025 +0200
+++ b/src/ascension/datatypes.h	Wed Aug 06 00:37:01 2025 +0200
@@ -173,19 +173,14 @@
 #define ASC_VEC4F_0 (asc_vec4f){{0.0f, 0.0f, 0.0f, 0.0f}}
 #define ASC_VEC4F_1 (asc_vec4f){{1.0f, 1.0f, 1.0f, 1.0f}}
 
-typedef struct asc_col4i {
-    asc_ubyte red, green, blue, alpha;
-} asc_col4i;
-
-typedef struct asc_col4f {
+typedef struct asc_color {
     float red, green, blue, alpha;
-} asc_col4f;
+} asc_color;
 
-// TODO: think about removing integer colors
-#define ASC_RGB(r,g,b) (asc_col4i){(asc_ubyte)(r), (asc_ubyte)(g), (asc_ubyte)(b), 255u}
-#define ASC_RGBA(r,g,b,a) (asc_col4i){(asc_ubyte)(r), (asc_ubyte)(g), (asc_ubyte)(b), (asc_ubyte)(a)}
-#define ASC_RGB_F(r,g,b) (asc_col4f){r,g,b,1.f}
-#define ASC_RGBA_F(r,g,b,a) (asc_col4f){r,g,b,a}
+#define ASC_RGBi(r,g,b) asc_color_int(r,g,b,255)
+#define ASC_RGBAi(r,g,b,a) asc_color_int(r,g,b,a)
+#define ASC_RGB(r,g,b) (asc_color){r,g,b,1.f}
+#define ASC_RGBA(r,g,b,a) (asc_color){r,g,b,a}
 
 typedef float asc_mat4f[16];
 
@@ -261,6 +256,84 @@
     double: asc_clampd \
     )(v, min, max)
 
+static inline int asc_mini(int x, int y) {
+    if (x < y) return x;
+    return y;
+}
+
+static inline long long int asc_minll(long long int x, long long int y) {
+    if (x < y) return x;
+    return y;
+}
+
+static inline unsigned asc_minu(unsigned x, unsigned y) {
+    if (x < y) return x;
+    return y;
+}
+
+static inline unsigned long long int asc_minull(unsigned long long int x, unsigned long long int y) {
+    if (x < y) return x;
+    return y;
+}
+
+static inline float asc_minf(float x, float y) {
+    if (x < y) return x;
+    return y;
+}
+
+static inline double asc_mind(double x, double y) {
+    if (x < y) return x;
+    return y;
+}
+
+#define asc_min(x, y) _Generic((x), \
+    int: asc_mini, \
+    long long int: asc_minll, \
+    unsigned: asc_minu, \
+    unsigned long long int: asc_minull, \
+    float: asc_minf, \
+    double: asc_mind \
+    )(x, y)
+
+static inline int asc_maxi(int x, int y) {
+    if (x > y) return x;
+    return y;
+}
+
+static inline long long int asc_maxll(long long int x, long long int y) {
+    if (x > y) return x;
+    return y;
+}
+
+static inline unsigned asc_maxu(unsigned x, unsigned y) {
+    if (x > y) return x;
+    return y;
+}
+
+static inline unsigned long long int asc_maxull(unsigned long long int x, unsigned long long int y) {
+    if (x > y) return x;
+    return y;
+}
+
+static inline float asc_maxf(float x, float y) {
+    if (x > y) return x;
+    return y;
+}
+
+static inline double asc_maxd(double x, double y) {
+    if (x > y) return x;
+    return y;
+}
+
+#define asc_max(x, y) _Generic((x), \
+    int: asc_maxi, \
+    long long int: asc_maxll, \
+    unsigned: asc_maxu, \
+    unsigned long long int: asc_maxull, \
+    float: asc_maxf, \
+    double: asc_maxd \
+    )(x, y)
+
 static inline asc_vec2i asc_rect_center(asc_rect rect) {
     return ASC_VEC2I(rect.pos.x + rect.size.width/2, rect.pos.y + rect.size.height/2);
 }
@@ -318,41 +391,24 @@
     return asc_fround_zero(sinf(x));
 }
 
-/**
- * Converts a float color (0.0f to 1.0f) to an int color (0 to 255).
- *
- * This operation is quite expensive. When you need to use it often, it's
- * quite obvious that you should change the data type.
- *
- * @param c the color using floats
- * @return the same color using ints
- */
-static inline asc_col4i asc_col_ftoi(asc_col4f c) {
-    unsigned red = (unsigned)(255*c.red);
-    unsigned green = (unsigned)(255*c.green);
-    unsigned blue = (unsigned)(255*c.blue);
-    unsigned alpha = (unsigned)(255*c.alpha);
-    asc_col4i r;
-    r.red = asc_clamp(red, 0, 255);
-    r.green = asc_clamp(green, 0, 255);
-    r.blue = asc_clamp(blue, 0, 255);
-    r.alpha = asc_clamp(alpha, 0, 255);
-    return r;
-}
-
-static inline asc_col4f asc_col_itof(asc_col4i c) {
+static inline asc_color asc_color_int(int r, int g, int b, int a) {
     // dividing by 256 is much more performant
     // because it compiles to multiplication instead of division
     const float f = 256.f / 255.f;
-    const float red = c.red * f / 256.f;
-    const float green = c.green * f / 256.f;
-    const float blue = c.blue * f / 256.f;
-    const float alpha = c.alpha * f / 256.f;
-    return (asc_col4f) {red, green, blue, alpha};
+    const float red = r * f / 256.f;
+    const float green = g * f / 256.f;
+    const float blue = b * f / 256.f;
+    const float alpha = a * f / 256.f;
+    return (asc_color) {red, green, blue, alpha};
 }
 
-static inline SDL_Color asc_col_sdl(asc_col4i col) {
-    return (SDL_Color) {.r = col.red, .g = col.green, .b = col.blue, .a = col.alpha};
+static inline SDL_Color asc_color_sdl(asc_color color) {
+    return (SDL_Color){
+        .r = asc_min((int)(color.red*255), 255),
+        .g = asc_min((int)(color.green*255), 255),
+        .b = asc_min((int)(color.blue*255), 255),
+        .a = asc_min((int)(color.alpha*255), 255),
+    };
 }
 
 // --------------------------------------------------------------------------
--- a/src/ascension/mesh.h	Tue Aug 05 20:38:11 2025 +0200
+++ b/src/ascension/mesh.h	Wed Aug 06 00:37:01 2025 +0200
@@ -55,7 +55,7 @@
 typedef struct asc_vertex2d_col {
     asc_vec2f pos;
     asc_vec2f uv;
-    asc_col4f color;
+    asc_color color;
 } asc_vertex2d_col;
 
 typedef struct asc_vertex3d {
@@ -66,7 +66,7 @@
 typedef struct asc_vertex3d_col {
     asc_vec3f pos;
     asc_vec2f uv;
-    asc_col4f color;
+    asc_color color;
 } asc_vertex3d_col;
 
 
--- a/src/ascension/shader.h	Tue Aug 05 20:38:11 2025 +0200
+++ b/src/ascension/shader.h	Wed Aug 06 00:37:01 2025 +0200
@@ -178,11 +178,7 @@
 
 void asc_shader_upload_model_matrix(const AscShaderProgram *shader, const AscSceneNode *node);
 
-void asc_shader_upload_col4f(int uniform_id, asc_col4f color);
-
-static inline void asc_shader_upload_col4i(int uniform_id, asc_col4i color) {
-    asc_shader_upload_col4f(uniform_id, asc_col_itof(color));
-}
+void asc_shader_upload_color(int uniform_id, asc_color color);
 
 void asc_shader_upload_float(int uniform_id, float value);
 
--- a/src/ascension/text.h	Tue Aug 05 20:38:11 2025 +0200
+++ b/src/ascension/text.h	Wed Aug 06 00:37:01 2025 +0200
@@ -41,7 +41,7 @@
     AscTexture *texture;
     cxmutstr text;
     AscFont font;
-    asc_col4i color;
+    asc_color color;
     unsigned short max_width;
     /**
      * The automatically calculated offset in case the text is centered.
@@ -67,7 +67,7 @@
     int y;
     const char *name;
     const char *text;
-    asc_col4i color;
+    asc_color color;
     enum asc_text_alignment alignment;
     unsigned short max_width;
     bool centered;
--- a/src/camera.c	Tue Aug 05 20:38:11 2025 +0200
+++ b/src/camera.c	Wed Aug 06 00:37:01 2025 +0200
@@ -47,7 +47,7 @@
     camera->viewport_update_func = args.viewport_update_func;
     camera->projection_update_func = args.projection_update_func;
     camera->viewport_clear = args.viewport_clear;
-    camera->clear_color = asc_col_itof(args.clear_color);
+    camera->clear_color = args.clear_color;
 }
 
 void asc_camera_ortho(AscCamera *camera, asc_rect rect) {
--- a/src/scene.c	Tue Aug 05 20:38:11 2025 +0200
+++ b/src/scene.c	Wed Aug 06 00:37:01 2025 +0200
@@ -210,7 +210,7 @@
             scene->camera.viewport.size.height
         );
         glEnable(GL_SCISSOR_TEST);
-        const asc_col4f col = scene->camera.clear_color;
+        const asc_color col = scene->camera.clear_color;
         glClearColor(col.red, col.green, col.blue, col.alpha);
         glClear(GL_COLOR_BUFFER_BIT);
         glDisable(GL_SCISSOR_TEST);
--- a/src/shader.c	Tue Aug 05 20:38:11 2025 +0200
+++ b/src/shader.c	Wed Aug 06 00:37:01 2025 +0200
@@ -277,7 +277,7 @@
     glUniformMatrix4fv(shader->model, 1,GL_FALSE, node->world_transform);
 }
 
-void asc_shader_upload_col4f(int uniform_id, asc_col4f color) {
+void asc_shader_upload_color(int uniform_id, asc_color color) {
     glUniform4f(uniform_id, color.red, color.green, color.blue, color.alpha);
 }
 
--- a/src/text.c	Tue Aug 05 20:38:11 2025 +0200
+++ b/src/text.c	Wed Aug 06 00:37:01 2025 +0200
@@ -74,7 +74,7 @@
     TTF_SetFontWrapAlignment(
             font, alignments[text->base.flags & ASC_TEXT_ALIGNMENT_MASK]);
     SDL_Surface *surface = TTF_RenderText_Blended_Wrapped(
-            font, text->text.ptr, text->text.length, asc_col_sdl(text->color), text->max_width
+            font, text->text.ptr, text->text.length, asc_color_sdl(text->color), text->max_width
     );
     if (surface == NULL) {
         asc_error("Rendering TTF surface failed: %s", SDL_GetError());
@@ -150,8 +150,8 @@
     if (text->font.size == 0) {
         text->font.size = 12;
     }
-    if (asc_memcmpz(&args.color, sizeof(asc_col4i))) {
-        text->color = ASC_RGB(255, 255, 255);
+    if (asc_memcmpz(&args.color, sizeof(asc_color))) {
+        text->color = ASC_RGB(1, 1, 1);
     } else {
         text->color = args.color;
     }
--- a/test/snake/snake.c	Tue Aug 05 20:38:11 2025 +0200
+++ b/test/snake/snake.c	Wed Aug 06 00:37:01 2025 +0200
@@ -58,7 +58,7 @@
 static asc_vec2i directions[4];
 
 typedef struct {
-    asc_col4f color;
+    asc_color color;
     enum MoveDirection direction;
     enum MoveDirection target_direction;
     /**
@@ -143,7 +143,7 @@
 
     // also add a frame for the main scene
     // add this to the UI layer so that the border size does not scale
-    AscSceneNode *frame = asc_rectangle(.thickness = 2, .color = ASC_RGB(66, 142, 161));
+    AscSceneNode *frame = asc_rectangle(.thickness = 2, .color = ASC_RGBi(66, 142, 161));
     asc_behavior_add(frame, .func = main_scene_frame_scale);
     asc_ui_add_node(frame);
 }
@@ -170,7 +170,7 @@
 static void fps_counter_create(void) {
     AscSceneNode *node = asc_text(
         .name = "FPS Counter",
-        .color = ASC_RGB(255, 255, 255),
+        .color = ASC_RGB(1, 1, 1),
         .font = asc_font(ASC_FONT_REGULAR, 12),
     );
     asc_behavior_add(node, .func = fps_counter_update, .interval = asc_seconds(1));
@@ -187,7 +187,7 @@
     int old_owner = game_field->tile_data[x][y] & GAME_FIELD_TILE_OWNER_MASK;
     if (player == NULL) {
         asc_clear_flag(game_field->tile_data[x][y], GAME_FIELD_TILE_OWNER_MASK);
-        tile->color = asc_col_itof(ASC_RGB(16, 50, 160));
+        tile->color = ASC_RGBi(16, 50, 160);
     } else {
         asc_set_flag_masked(game_field->tile_data[x][y], GAME_FIELD_TILE_OWNER_MASK, player->number);
         tile->color = player->color;
@@ -205,8 +205,8 @@
             AscSceneNode *tile = asc_rectangle(
                 .x = x*GAME_FIELD_TILE_SIZE, .y = y*GAME_FIELD_TILE_SIZE, .filled = true, .thickness = 2,
                 .width = GAME_FIELD_TILE_SIZE, .height = GAME_FIELD_TILE_SIZE,
-                .color = ASC_RGB(16, 50, 160),
-                .border_color = ASC_RGB(20, 84, 128),
+                .color = ASC_RGBi(16, 50, 160),
+                .border_color = ASC_RGBi(20, 84, 128),
             );
 
             game_field->tile_data[x][y] = GAME_FIELD_TILE_EXISTS_FLAG;
@@ -259,7 +259,7 @@
     // Bind texture
     asc_texture_bind(TEXTURE_PLAYER, shader->map_albedo, 0);
     asc_texture_bind(TEXTURE_PLAYER_COLOR_MAP, shader->map_color, 1);
-    asc_shader_upload_col4f(shader->color, player->color);
+    asc_shader_upload_color(shader->color, player->color);
     asc_mesh_draw_triangle_strip(&sprite->mesh);
 }
 
@@ -370,7 +370,7 @@
     player_position(player, 12, 8);
     player->speed = 3.f; // start with 3 tiles/sec
     player->number = 1;
-    player->color = ASC_RGB_F(1, 0, 0);
+    player->color = ASC_RGB(1, 0, 0);
     player->trace = cxLinkedListCreateSimple(sizeof(asc_vec2i));
     node->draw_func = player_draw;
     node->user_data_free_func = (cx_destructor_func2)player_destroy;
@@ -452,7 +452,7 @@
             (GAME_FIELD_SIZE+2)*GAME_FIELD_TILE_SIZE
         ),
         .viewport_clear = true,
-        .clear_color = ASC_RGB(0, 32, 16),
+        .clear_color = ASC_RGBi(0, 32, 16),
         .viewport_update_func = main_scene_viewport_update
     );
 

mercurial