// color names and functions for dealing with colors #include "ted-internal.h" typedef struct { ColorSetting setting; const char *name; } ColorName; static ColorName color_names[] = { {COLOR_UNKNOWN, "unknown"}, {COLOR_TEXT, "text"}, {COLOR_TEXT_SECONDARY, "text-secondary"}, {COLOR_BG, "bg"}, {COLOR_CURSOR, "cursor"}, {COLOR_CURSOR_ERROR, "cursor-error"}, {COLOR_CURSOR_LINE_BG, "cursor-line-bg"}, {COLOR_VIEW_ONLY_CURSOR, "view-only-cursor"}, {COLOR_VIEW_ONLY_SELECTION_BG, "view-only-selection-bg"}, {COLOR_MATCHING_BRACKET_HL, "matching-bracket-hl"}, {COLOR_BORDER, "border"}, {COLOR_TEXT_FOLDER, "text-folder"}, {COLOR_TEXT_OTHER, "text-other"}, {COLOR_SELECTION_BG, "selection-bg"}, {COLOR_MENU_BACKDROP, "menu-backdrop"}, {COLOR_MENU_BG, "menu-bg"}, {COLOR_MENU_HL, "menu-hl"}, {COLOR_ERROR_BG, "error-bg"}, {COLOR_ERROR_BORDER, "error-border"}, {COLOR_INFO_BG, "info-bg"}, {COLOR_INFO_BORDER, "info-border"}, {COLOR_WARNING_BG, "warning-bg"}, {COLOR_WARNING_BORDER, "warning-border"}, {COLOR_ACTIVE_TAB_HL, "active-tab-hl"}, {COLOR_SELECTED_TAB_HL, "selected-tab-hl"}, {COLOR_FIND_HL, "find-hl"}, {COLOR_KEYWORD, "keyword"}, {COLOR_BUILTIN, "builtin"}, {COLOR_COMMENT, "comment"}, {COLOR_PREPROCESSOR, "preprocessor"}, {COLOR_STRING, "string"}, {COLOR_CHARACTER, "character"}, {COLOR_CONSTANT, "constant"}, {COLOR_TODO, "todo"}, {COLOR_AUTOCOMPLETE_BG, "autocomplete-bg"}, {COLOR_AUTOCOMPLETE_HL, "autocomplete-hl"}, {COLOR_AUTOCOMPLETE_BORDER, "autocomplete-border"}, {COLOR_AUTOCOMPLETE_VARIABLE, "autocomplete-variable"}, {COLOR_AUTOCOMPLETE_FUNCTION, "autocomplete-function"}, {COLOR_AUTOCOMPLETE_TYPE, "autocomplete-type"}, {COLOR_HOVER_BORDER, "hover-border"}, {COLOR_HOVER_BG, "hover-bg"}, {COLOR_HOVER_TEXT, "hover-text"}, {COLOR_HOVER_HL, "hover-hl"}, {COLOR_HL_WRITE, "hl-write"}, {COLOR_YES, "yes"}, {COLOR_NO, "no"}, {COLOR_CANCEL, "cancel"}, {COLOR_LINE_NUMBERS, "line-numbers"}, {COLOR_CURSOR_LINE_NUMBER, "cursor-line-number"}, {COLOR_LINE_NUMBERS_SEPARATOR, "line-numbers-separator"}, }; static_assert_if_possible(arr_count(color_names) == COLOR_COUNT) int color_name_cmp(const void *av, const void *bv) { const ColorName *a = av, *b = bv; return strcmp(a->name, b->name); } void color_init(void) { qsort(color_names, arr_count(color_names), sizeof *color_names, color_name_cmp); } ColorSetting color_setting_from_str(const char *str) { int lo = 0; int hi = COLOR_COUNT; while (lo < hi) { int mid = (lo + hi) / 2; int cmp = strcmp(color_names[mid].name, str); if (cmp < 0) { lo = mid + 1; } else if (cmp > 0) { hi = mid; } else { return color_names[mid].setting; } } return COLOR_UNKNOWN; } const char *color_setting_to_str(ColorSetting s) { for (int i = 0; i < COLOR_COUNT; ++i) { ColorName const *n = &color_names[i]; if (n->setting == s) return n->name; } return "???"; } // converts #rrggbb/#rrggbbaa to a color. returns false if it's not in the right format. Status color_from_str(const char *str, u32 *color) { u32 r = 0, g = 0, b = 0, a = 0xff; bool success = false; switch (strlen(str)) { case 4: success = sscanf(str, "#%01x%01x%01x", &r, &g, &b) == 3; // extend single hex digit to double hex digit r |= r << 4; g |= g << 4; b |= b << 4; break; case 5: success = sscanf(str, "#%01x%01x%01x%01x", &r, &g, &b, &a) == 4; r |= r << 4; g |= g << 4; b |= b << 4; a |= a << 4; break; case 7: success = sscanf(str, "#%02x%02x%02x", &r, &g, &b) == 3; break; case 9: success = sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a) == 4; break; } if (!success || r > 0xff || g > 0xff || b > 0xff || a > 0xff) return false; if (color) *color = (u32)r << 24 | (u32)g << 16 | (u32)b << 8 | (u32)a; return true; } ColorSetting color_for_symbol_kind(SymbolKind kind) { switch (kind) { case SYMBOL_CONSTANT: return COLOR_CONSTANT; case SYMBOL_TYPE: return COLOR_AUTOCOMPLETE_TYPE; case SYMBOL_FIELD: case SYMBOL_VARIABLE: return COLOR_AUTOCOMPLETE_VARIABLE; case SYMBOL_FUNCTION: return COLOR_AUTOCOMPLETE_FUNCTION; case SYMBOL_OTHER: return COLOR_TEXT; case SYMBOL_KEYWORD: return COLOR_KEYWORD; } return COLOR_TEXT; } u32 color_blend(u32 bg, u32 fg) { u32 r1 = bg >> 24; u32 g1 = (bg >> 16) & 0xff; u32 b1 = (bg >> 8) & 0xff; u32 r2 = fg >> 24; u32 g2 = (fg >> 16) & 0xff; u32 b2 = (fg >> 8) & 0xff; u32 a2 = fg & 0xff; u32 r = (r1 * (255 - a2) + r2 * a2 + 127) / 255; u32 g = (g1 * (255 - a2) + g2 * a2 + 127) / 255; u32 b = (b1 * (255 - a2) + b2 * a2 + 127) / 255; return r << 24 | g << 16 | b << 8 | 0xff; } u32 color_apply_opacity(u32 color, float opacity) { opacity = clampf(opacity, 0.0f, 1.0f); return (color & 0xffffff00) | (u32)((color & 0xff) * opacity); } static float color_relative_luminance(const float rgb[3]) { // see https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef float c[3]; for (int i = 0; i < 3; ++i) { const float x = rgb[i]; c[i] = x <= 0.03928f ? x * (1.0f / 12.92f) : powf((x + 0.055f) * (1.0f / 1.055f), 2.4f); } return 0.2126f * c[0] + 0.7152f * c[1] + 0.0722f * c[2]; } float color_contrast_ratio(const float rgb1[3], const float rgb2[3]) { // see https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef float l1 = color_relative_luminance(rgb1); float l2 = color_relative_luminance(rgb2); if (l1 < l2) { float temp = l1; l1 = l2; l2 = temp; } return (l1 + 0.05f) / (l2 + 0.05f); } float color_contrast_ratio_u32(u32 color1, u32 color2) { float rgb1[4], rgb2[4]; rgba_u32_to_floats(color1, rgb1); rgba_u32_to_floats(color2, rgb2); return color_contrast_ratio(rgb1, rgb2); }