#include "muscles.h" #include static inline u32 rgba_to_u32(RGBA color) { return ((u32)((u8)(color.r * 255.0)) << 24) | ((u32)((u8)(color.g * 255.0)) << 16) | ((u32)((u8)(color.b * 255.0)) << 8) | (u8)(color.a * 255.0); } static inline int abs_i(int x) { return x < 0 ? -x : x; } static inline double abs_d(double x) { return x < 0 ? -x : x; } static inline float clamp_f(float f, float min, float max) { if (f < min) return min; if (f > max) return max; return f; } static inline double clamp_d(double f, double min, double max) { if (f < min) return min; if (f > max) return max; return f; } Texture make_circle(RGBA& color, int diameter) { auto pixels = std::make_unique(diameter * diameter); float radius = (diameter / 2) - 2; RGBA shade = color; // circle outline maybe? float y = -diameter / 2; for (int i = 0; i < diameter; i++) { float x = -diameter / 2; for (int j = 0; j < diameter; j++) { float dist = sqrt((double)(x * x + y * y)); float lum = 0.5 + clamp_f(radius - dist, -1, 1) / 2; shade.a = color.a * lum; pixels[i * diameter + j] = rgba_to_u32(shade); x += 1; } y += 1; } return sdl_create_texture(pixels.get(), diameter, diameter); } Texture make_triangle(RGBA& color, int width, int height, bool up) { auto pixels = std::make_unique(width * height); RGBA shade = color; const double y_begin = -0.15; const double y_inter = 0.25; const double x_dil = 1.2; const double intensity = 20.0; double w = (double)width; double h = (double)height; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { double x = -0.5 + ((double)j + 0.5) / w; double y = -0.5 + ((double)i + 0.5) / h; float lum = 0.0; if (y >= y_begin) { double l = y_inter - (abs_d(x * x_dil) + y); lum = clamp_d(l * intensity, 0.0, 1.0); } shade.a = color.a * lum; int row = up ? height - i - 1 : i; pixels[row * width + j] = rgba_to_u32(shade); } } return sdl_create_texture(pixels.get(), width, height); } Texture make_rectangle(RGBA& color, int width, int height, float left, float top, float thickness) { auto pixels = std::make_unique(width * height); u32 shade = rgba_to_u32(color); if (left < 0.0 || left >= 1.0 || top < 0.0 || top >= 1.0 || thickness <= 0.0) { memset(pixels.get(), 0, width * height * sizeof(u32)); return sdl_create_texture(pixels.get(), width, height); } if (left >= 0.5) left = 1.0 - left; if (top >= 0.5) top = 1.0 - top; int left_gap = left * (float)width + 0.5; int top_gap = top * (float)height + 0.5; int thicc = thickness * (float)(width > height ? width : height) + 0.5; if (thicc < 1) thicc = 1; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { pixels[i*width+j] = ( i >= top_gap && j >= left_gap && i < height - top_gap && j < width - left_gap ) && ( i < top_gap + thicc || j < left_gap + thicc || i >= height - top_gap - thicc || j >= width - left_gap - thicc ) ? shade : 0; } } return sdl_create_texture(pixels.get(), width, height); } Texture make_cross_icon(RGBA& color, int length, float inner, float outer) { auto pixels = std::make_unique(length * length); float l = (float)length + 0.5; int strong = (int)(l * inner); int weak = (int)(l * outer); RGBA shade = color; for (int i = 0; i < length; i++) { for (int j = 0; j < length; j++) { int d1 = abs_i(i - j); int d2 = abs_i(i - ((length - 1) - j)); float lum = 0.0; if (d1 == weak || d2 == weak) lum = 0.5; if (d1 <= strong || d2 <= strong) lum = 1.0; shade.a = color.a * lum; pixels[i * length + j] = rgba_to_u32(shade); } } return sdl_create_texture(pixels.get(), length, length); } Texture make_folder_icon(RGBA& dark, RGBA& light, int w, int h) { auto pixels = std::make_unique(w * h); float width = (float)w + 0.5; float height = (float)h + 0.5; int pad_x = width * 0.07; int pad_y = height * 0.14; int tab_w = width * 0.4; int tab_h = height * 0.2; int lid_y = height * 0.4; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { RGBA *color = i < lid_y ? &light : &dark; u32 pixel = 0; if (i >= pad_y && i < h - pad_y && j >= pad_x && j < w - pad_x && (i > tab_h || j <= tab_w)) pixel = rgba_to_u32(*color); pixels[i * w + j] = pixel; } } return sdl_create_texture(pixels.get(), w, h); } Texture make_file_icon(RGBA& back, RGBA& fold_color, RGBA& line_color, int w, int h) { auto pixels = std::make_unique(w * h); const float fold_length = 0.17; const float line_x = 0.27; const float line_start = 0.28; const float line_height_frac = 0.4; const float line_gap = 0.16; const float lines_end = 0.8; const float pad_x = 0.2; const float pad_y = 0.1; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { float x = ((float)j + 0.5) / (float)w; float y = ((float)i + 0.5) / (float)h; int col = 0; if (x >= pad_x && x < 1.0 - pad_x && y >= pad_y && y < 1.0 - pad_y) col = 1; float fold_x = (1.0 - pad_x) - x; float fold_y = y - pad_y; if (fold_x + fold_y < fold_length) col = 0; if (col > 0 && fold_x < fold_length && fold_y < fold_length) col = 2; float start = 0.1 + line_start; if (x >= line_x && x < 1.0 - line_x && y >= start && y < lines_end ) { double whole = 0.0; float frac = modf((y - start) / line_gap, &whole); if (frac <= line_height_frac) col = 3; } RGBA *color = nullptr; if (col == 1) color = &back; else if (col == 2) color = &fold_color; else if (col == 3) color = &line_color; pixels[i*w+j] = color ? rgba_to_u32(*color) : 0; } } return sdl_create_texture(pixels.get(), w, h); } Texture make_process_icon(RGBA& back, RGBA& outline, int length) { auto pixels = std::make_unique(length * length); const double outer_rim = 0.175; const double circle_outer = 0.12; const double circle_outer_rim = 0.1; const double circle_inner = 0.042; const double circle_inner_rim = 0.03; const double bar_inner = 0.06; const double bar_outer = 0.09; double root_2 = sqrt(2); const double diag_inner = bar_inner * root_2; const double diag_outer = bar_outer * root_2; for (int i = 0; i < length; i++) { for (int j = 0; j < length; j++) { int col = 0; double x = ((double)j + 0.5) / (double)length; double y = ((double)i + 0.5) / (double)length; double cx = x - 0.5; double cy = y - 0.5; double dist = cx * cx + cy * cy; if (dist < 0.2) { double d1 = x - y; double d2 = (1.0 - x) - y; if ( (d1 >= -diag_inner && d1 < diag_inner) || (d2 >= -diag_inner && d2 < diag_inner) || (cx >= -bar_inner && cx < bar_inner) || (cy >= -bar_inner && cy < bar_inner) ) { col = 1; } else if ( (d1 >= -diag_outer && d1 < diag_outer) || (d2 >= -diag_outer && d2 < diag_outer) || (cx >= -bar_outer && cx < bar_outer) || (cy >= -bar_outer && cy < bar_outer) ) { col = 2; } if (col > 0 && dist >= outer_rim) col = 2; } if (dist < circle_outer_rim) col = 1; else if (col != 1 && dist < circle_outer) col = 2; if (dist < circle_inner_rim) col = 0; else if (dist < circle_inner) col = 2; RGBA *color = nullptr; if (col == 1) color = &back; else if (col == 2) color = &outline; pixels[i*length+j] = color ? rgba_to_u32(*color) : 0; } } return sdl_create_texture(pixels.get(), length, length); } Texture make_divider_icon(RGBA& color, int width, int height, double gap, double thicc, double sharpness, bool up_down) { RGBA shade = color; auto pixels = std::make_unique(width * height); double inner = 0.5 - thicc; double end = inner - gap; double frac_h = (double)height / (double)width; double frac_req_h = 1.0 - thicc - gap; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { double x = ((double)j + 0.5) / (double)width - 0.5; double y = ((double)i + 0.5) / (double)height - 0.5; if (up_down) { auto temp = x; x = y; y = temp; } y *= frac_req_h / frac_h; double max_y = frac_req_h / 2.0; if (x >= 0.5 || y >= max_y || y < -max_y) { pixels[i*width+j] = 0; continue; } x = abs_d(x); y = abs_d(y); double g1 = x + y; double g2 = y - x; double lum = 0.0; if (g1 >= inner && g1 < 0.5 && g2 < end) { double mid = (inner + 0.5) / 2.0; double slope = 0.5 - mid; lum = 1.0 - (abs_d(g1 - mid) / slope); lum = clamp_d(lum * sharpness, 0.0, 1.0); double edge = end - g2; edge = clamp_d(edge * sharpness / slope, 0.0, 1.0); lum *= edge; } shade.a = color.a * lum; pixels[i*width+j] = rgba_to_u32(shade); } } return sdl_create_texture(pixels.get(), width, height); } Texture make_goto_icon(RGBA& color, int length) { auto pixels = std::make_unique(length * length); RGBA shade = color; const double fade_distance = 0.02; const double inner_edge = 0.3; const double outer_edge = 0.45; const double arc_dilation = 1.1; const double arc_x_offset = 0.1; const double arc_y_offset = -0.1; const double arrow_size = 0.3; const double arrow_squish = 1.15; const double arrow_fade = 0.08; double len = (double)length; for (int i = 0; i < length; i++) { for (int j = 0; j < length; j++) { double x = ((double)j + 0.5) / len - 0.5; double y = ((double)i + 0.5) / len - 0.5; double lum = 0.0; double cx = x * arc_dilation + arc_x_offset; double cy = y + arc_y_offset; double ix = x + outer_edge - inner_edge; double arrow_x = (((outer_edge - arc_x_offset) / arc_dilation) + (inner_edge - (ix - x))) / 2.0; if (cy < 0.0 && cy-cx < 0.0) { double outer = sqrt(cx * cx + cy * cy); double inner = sqrt(ix * ix + cy * cy); if (inner >= inner_edge && outer < outer_edge) { inner -= inner_edge; outer = outer_edge - outer; if (inner < fade_distance) lum = inner / fade_distance; else if (outer < fade_distance) lum = outer / fade_distance; else lum = 1.0; } } double head = 1.0 - (y + abs_d(x - arrow_x) * arrow_squish) / arrow_size; if (cy >= 0.0 && head > 0.0) lum = clamp_d(head / arrow_fade, 0.0, 1.0); shade.a = color.a * lum; pixels[i*length+j] = lum == 0 ? 0 : rgba_to_u32(shade); } } return sdl_create_texture(pixels.get(), length, length); } double smooth_edges(double pos, double end, double fade) { double outer = end - pos; double lum = 1.0; if (pos < fade) lum = pos / fade; else if (outer < fade) lum = outer / fade; return lum; } Texture make_glass_icon(RGBA& color, int length) { auto pixels = std::make_unique(length * length); RGBA shade = color; const double circle_fade = 0.025; const double handle_fade = 0.02; const double circle_x = 0.4; const double circle_y = 0.4; const double inner_edge = 0.18; const double outer_edge = 0.3; const double handle_height = 0.15; const double handle_start = 0.1; const double handle_width = 0.7; double len = (double)length; for (int i = 0; i < length; i++) { for (int j = 0; j < length; j++) { double x = ((double)j + 0.5) / len - 0.5; double y = ((double)i + 0.5) / len - 0.5; double lum = 0.0; double cx = x + 0.5 - circle_x; double cy = y + 0.5 - circle_y; double dist = sqrt(cx*cx + cy*cy); if (dist >= inner_edge && dist < outer_edge) lum = smooth_edges(dist - inner_edge, outer_edge - inner_edge, circle_fade); double w = outer_edge - inner_edge; double handle_x = x + y - handle_start; double handle_y = x - y + handle_height / 2.0; if (handle_x >= 0.0 && handle_x < handle_width && handle_y >= 0.0 && handle_y < handle_height ) { lum += smooth_edges(handle_x, handle_width, handle_fade) * smooth_edges(handle_y, handle_height, handle_fade); } shade.a = color.a * clamp_d(lum, 0.0, 1.0); pixels[i*length+j] = lum > 0.0 ? rgba_to_u32(shade) : 0; } } return sdl_create_texture(pixels.get(), length, length); } Texture make_plus_minus_icon(RGBA& color, int length, bool plus) { auto pixels = std::make_unique(length * length); RGBA shade = color; const double indent_lum = 0.2; const double sharpness = 25.0; const double squareness = 6.1; const double radius = 0.4; const double max_threshold = 0.6; const double thicc = 0.09; const double end = 0.3; double len = (double)length; for (int i = 0; i < length; i++) { for (int j = 0; j < length; j++) { double x = ((double)j + 0.5) / len - 0.5; double y = ((double)i + 0.5) / len - 0.5; double dist = sqrt(x * x + y * y); double curve = abs_d(x*y); curve *= curve; double thres = radius + squareness * curve; double lum = 0.0; if (dist < thres && thres < max_threshold) lum = clamp_d(sharpness * (thres - dist) / thres, 0.0, 1.0); if (x >= -end && x < end && y >= -end && y < end) { if ((plus && x >= -thicc && x < thicc) || (y >= -thicc && y < thicc)) lum = indent_lum; } shade.a = color.a * lum; pixels[i*length+j] = rgba_to_u32(shade); } } return sdl_create_texture(pixels.get(), length, length); }