203 lines
11 KiB
C++
203 lines
11 KiB
C++
// ui_icons.cpp - SVG icon rasterization via lunasvg
|
|
|
|
#include "ui/ui_icons.h"
|
|
#include <lunasvg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
UI_IconInfo g_icons[UI_ICON_COUNT] = {};
|
|
|
|
// Simple SVG icon sources (24x24 viewBox)
|
|
static const char *g_icon_svgs[UI_ICON_COUNT] = {
|
|
// UI_ICON_CLOSE - X mark
|
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<path d="M6 6 L18 18 M18 6 L6 18" stroke="white" stroke-width="2.5" stroke-linecap="round"/>
|
|
</svg>)",
|
|
|
|
// UI_ICON_CHECK - checkmark
|
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<path d="M5 12 L10 17 L19 7" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
|
</svg>)",
|
|
|
|
// UI_ICON_CHEVRON_DOWN - downward arrow
|
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<path d="M7 10 L12 15 L17 10" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
|
</svg>)",
|
|
|
|
// UI_ICON_KNOB - filled circle with indicator line pointing up (12 o'clock)
|
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<circle cx="12" cy="12" r="10" fill="white" opacity="0.25"/>
|
|
<line x1="12" y1="12" x2="12" y2="3" stroke="white" stroke-width="2.5" stroke-linecap="round"/>
|
|
</svg>)",
|
|
|
|
// UI_ICON_SLIDER_THUMB - solid body with grip ridges
|
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<rect x="2" y="1" width="20" height="22" rx="4" fill="white"/>
|
|
<line x1="6" y1="8" x2="18" y2="8" stroke="black" stroke-width="1.2" opacity="0.3"/>
|
|
<line x1="6" y1="11" x2="18" y2="11" stroke="black" stroke-width="1.2" opacity="0.3"/>
|
|
<line x1="6" y1="14" x2="18" y2="14" stroke="black" stroke-width="1.2" opacity="0.3"/>
|
|
<line x1="6" y1="17" x2="18" y2="17" stroke="black" stroke-width="1.2" opacity="0.3"/>
|
|
</svg>)",
|
|
|
|
// UI_ICON_FADER - exact asset fader cap from assets/fader.svg
|
|
R"SVG(<svg xmlns="http://www.w3.org/2000/svg" viewBox="847 488 62.2 129.3">
|
|
<defs>
|
|
<linearGradient id="linearGradient7718" y2="528.75" gradientUnits="userSpaceOnUse" x2="87.866" gradientTransform="matrix(1.0278,0,0,1,787.52,-27.904)" y1="516.83" x1="87.866">
|
|
<stop style="stop-color:#999999" offset="0"/>
|
|
<stop style="stop-color:#999999;stop-opacity:0" offset="1"/>
|
|
</linearGradient>
|
|
<linearGradient id="linearGradient7720" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1.0235,0,0,1,242.38,-1008.6)" y1="496.57" x1="618.49">
|
|
<stop style="stop-color:#cccccc" offset="0"/>
|
|
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
|
</linearGradient>
|
|
<linearGradient id="linearGradient7722" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1,0,0,0.50643,256.46,265.42)" y1="496.57" x1="618.49">
|
|
<stop style="stop-color:#cccccc" offset="0"/>
|
|
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
|
</linearGradient>
|
|
<linearGradient id="linearGradient7724" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1,0,0,0.42746,256.46,317.38)" y1="496.57" x1="618.49">
|
|
<stop style="stop-color:#cccccc" offset="0"/>
|
|
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
|
</linearGradient>
|
|
<linearGradient id="linearGradient7726" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1,0,0,0.26952,256.46,405.1)" y1="496.57" x1="618.49">
|
|
<stop style="stop-color:#cccccc" offset="0"/>
|
|
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
|
</linearGradient>
|
|
<linearGradient id="linearGradient7728" y2="521.42" gradientUnits="userSpaceOnUse" x2="87.866" gradientTransform="matrix(1.0364,0,0,0.96441,786.64,-1114.5)" y1="516.83" x1="87.866">
|
|
<stop style="stop-color:#999999" offset="0"/>
|
|
<stop style="stop-color:#333333" offset="1"/>
|
|
</linearGradient>
|
|
<linearGradient id="linearGradient7730" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1.0364,0,0,0.96441,234.39,-1076.7)" y1="496.57" x1="618.49">
|
|
<stop style="stop-color:#cccccc" offset="0"/>
|
|
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
|
</linearGradient>
|
|
<linearGradient id="linearGradient7732" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1.0274,0,0,0.48841,240.25,-833.5)" y1="496.57" x1="618.49">
|
|
<stop style="stop-color:#cccccc" offset="0"/>
|
|
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
|
</linearGradient>
|
|
<linearGradient id="linearGradient7734" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1.0213,0,0,0.41225,243.85,-783.64)" y1="496.57" x1="618.49">
|
|
<stop style="stop-color:#cccccc" offset="0"/>
|
|
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
|
</linearGradient>
|
|
<linearGradient id="linearGradient7736" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1.0122,0,0,0.25993,249.26,-698.66)" y1="496.57" x1="618.49">
|
|
<stop style="stop-color:#cccccc" offset="0"/>
|
|
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
|
</linearGradient>
|
|
</defs>
|
|
<rect style="fill:#4d4d4d" rx="3" ry="3" height="129.29" width="62.143" y="488.03" x="847.03"/>
|
|
<rect style="fill:#e6e6e6" height="3.5355" width="60.419" y="552.42" x="847.97"/>
|
|
<rect style="fill:url(#linearGradient7718)" rx="2.148" ry="2" height="21.071" width="60.613" y="488.74" x="847.72"/>
|
|
<rect style="fill:#333333" height="10.119" width="58.811" y="540.26" x="847.92"/>
|
|
<rect style="fill:#333333" height="8.793" width="61.133" y="530.89" x="847.03"/>
|
|
<rect style="fill:#1a1a1a" height="4.546" width="61.133" y="512.48" x="847.03"/>
|
|
<rect style="fill:#1a1a1a" height="11.364" width="61.133" y="518.25" x="847.03"/>
|
|
<rect style="fill:url(#linearGradient7720)" rx="1.024" ry="0.64" transform="scale(1,-1)" height="2.261" width="60.012" y="-511.76" x="847.72"/>
|
|
<rect style="fill:url(#linearGradient7722)" rx="1" ry="0.324" height="1.145" width="58.633" y="517.03" x="847.89"/>
|
|
<rect style="fill:url(#linearGradient7724)" rx="1" ry="0.273" height="0.967" width="58.633" y="529.76" x="847.89"/>
|
|
<rect style="fill:url(#linearGradient7726)" rx="1" ry="0.172" height="0.609" width="58.633" y="539.01" x="847.89"/>
|
|
<rect style="fill:url(#linearGradient7728)" transform="scale(1,-1)" rx="2.166" ry="1.929" height="20.321" width="61.118" y="-616.24" x="847.34"/>
|
|
<rect style="fill:#333333" transform="scale(1,-1)" height="9.759" width="58.811" y="-567.93" x="847.92"/>
|
|
<rect style="fill:#666666" transform="scale(1,-1)" height="8.48" width="61.133" y="-576.96" x="847.03"/>
|
|
<rect style="fill:#808080" transform="scale(1,-1)" height="4.384" width="61.133" y="-594.72" x="847.03"/>
|
|
<rect style="fill:#808080" transform="scale(1,-1)" height="10.96" width="61.133" y="-589.16" x="847.03"/>
|
|
<rect style="fill:url(#linearGradient7730)" transform="scale(1,-1)" rx="1.036" ry="0.617" height="2.181" width="60.767" y="-597.6" x="847.34"/>
|
|
<rect style="fill:url(#linearGradient7732)" transform="scale(1,-1)" rx="1.027" ry="0.312" height="1.104" width="60.24" y="-590.84" x="847.89"/>
|
|
<rect style="fill:url(#linearGradient7734)" transform="scale(1,-1)" rx="1.021" ry="0.264" height="0.932" width="59.883" y="-578.82" x="847.89"/>
|
|
<rect style="fill:url(#linearGradient7736)" transform="scale(1,-1)" rx="1.012" ry="0.166" height="0.588" width="59.347" y="-569.52" x="847.89"/>
|
|
</svg>)SVG",
|
|
|
|
// UI_ICON_TRANSPORT_REWIND - skip to start (|<<)
|
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<rect x="3" y="5" width="2.5" height="14" rx="0.5" fill="white"/>
|
|
<path d="M11 5 L11 19 L4 12 Z" fill="white"/>
|
|
<path d="M20 5 L20 19 L13 12 Z" fill="white"/>
|
|
</svg>)",
|
|
|
|
// UI_ICON_TRANSPORT_STOP - filled square
|
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<rect x="5" y="5" width="14" height="14" rx="1.5" fill="white"/>
|
|
</svg>)",
|
|
|
|
// UI_ICON_TRANSPORT_PLAY - right-pointing triangle
|
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<path d="M6 4 L6 20 L20 12 Z" fill="white"/>
|
|
</svg>)",
|
|
|
|
// UI_ICON_TRANSPORT_RECORD - filled circle
|
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<circle cx="12" cy="12" r="8" fill="white"/>
|
|
</svg>)",
|
|
|
|
// UI_ICON_POP_OUT - box with arrow pointing out (top-right)
|
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<rect x="3" y="6" width="14" height="14" rx="2" stroke="white" stroke-width="2" fill="none"/>
|
|
<path d="M14 3 L21 3 L21 10" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
|
<path d="M21 3 L12 12" stroke="white" stroke-width="2" stroke-linecap="round"/>
|
|
</svg>)",
|
|
|
|
// UI_ICON_POP_IN - arrow pointing into a box (bottom-left)
|
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<rect x="3" y="6" width="14" height="14" rx="2" stroke="white" stroke-width="2" fill="none"/>
|
|
<path d="M21 3 L12 12" stroke="white" stroke-width="2" stroke-linecap="round"/>
|
|
<path d="M12 5 L12 12 L19 12" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
|
</svg>)",
|
|
};
|
|
|
|
U8 *ui_icons_rasterize_atlas(S32 *out_w, S32 *out_h, S32 icon_size) {
|
|
// Pack icons in a row
|
|
S32 atlas_w = icon_size * UI_ICON_COUNT;
|
|
S32 atlas_h = icon_size;
|
|
|
|
// Pad to power of 2 isn't necessary for correctness, but ensure minimum size
|
|
if (atlas_w < 64) atlas_w = 64;
|
|
if (atlas_h < 64) atlas_h = 64;
|
|
|
|
U8 *atlas = (U8 *)calloc(atlas_w * atlas_h * 4, 1);
|
|
if (!atlas) return nullptr;
|
|
|
|
S32 pen_x = 0;
|
|
for (S32 i = 0; i < UI_ICON_COUNT; i++) {
|
|
auto doc = lunasvg::Document::loadFromData(g_icon_svgs[i]);
|
|
if (!doc) continue;
|
|
|
|
lunasvg::Bitmap bmp = doc->renderToBitmap(icon_size, icon_size);
|
|
if (bmp.isNull()) continue;
|
|
|
|
// Copy BGRA premultiplied → RGBA straight (un-premultiply)
|
|
U8 *src = bmp.data();
|
|
S32 bmp_w = bmp.width();
|
|
S32 bmp_h = bmp.height();
|
|
S32 stride = bmp.stride();
|
|
|
|
for (S32 y = 0; y < bmp_h && y < atlas_h; y++) {
|
|
for (S32 x = 0; x < bmp_w && (pen_x + x) < atlas_w; x++) {
|
|
U8 *s = &src[y * stride + x * 4];
|
|
S32 dst_idx = (y * atlas_w + pen_x + x) * 4;
|
|
U8 b = s[0], g = s[1], r = s[2], a = s[3];
|
|
if (a > 0 && a < 255) {
|
|
r = (U8)((r * 255) / a);
|
|
g = (U8)((g * 255) / a);
|
|
b = (U8)((b * 255) / a);
|
|
}
|
|
atlas[dst_idx + 0] = r;
|
|
atlas[dst_idx + 1] = g;
|
|
atlas[dst_idx + 2] = b;
|
|
atlas[dst_idx + 3] = a;
|
|
}
|
|
}
|
|
|
|
// Store UV and pixel info
|
|
g_icons[i].u0 = (F32)pen_x / (F32)atlas_w;
|
|
g_icons[i].v0 = 0.0f;
|
|
g_icons[i].u1 = (F32)(pen_x + bmp_w) / (F32)atlas_w;
|
|
g_icons[i].v1 = (F32)bmp_h / (F32)atlas_h;
|
|
g_icons[i].w = (F32)bmp_w;
|
|
g_icons[i].h = (F32)bmp_h;
|
|
|
|
pen_x += icon_size;
|
|
}
|
|
|
|
*out_w = atlas_w;
|
|
*out_h = atlas_h;
|
|
return atlas;
|
|
}
|