From fc2127450fa887851b719369bcbcdbb1f14e3ed4 Mon Sep 17 00:00:00 2001 From: Sads Date: Sat, 13 Jun 2026 19:33:04 +0200 Subject: [PATCH] Cloud-status badges from Immich SVGs; remove the bottom HUD bar Replace the grid status dot with the Immich cloud icons (rasterized from the provided SVGs to white transparent PNGs: cloud = on server, cloud-off = on device, cloud-done = on both). Drawn white in the cell's bottom-right corner, no background, with a 1px dark shadow for legibility (red tint on failure). Icons rendered high-res, thinned, and LANCZOS-downscaled so they stay anti-aliased at badge size. Also drop the bottom HUD hint bar on every page (draw_hud is now a no-op). Co-Authored-By: Claude Opus 4.8 --- CMakeLists.txt | 3 ++ assets/cloud_both.png | Bin 0 -> 1069 bytes assets/cloud_device.png | Bin 0 -> 1092 bytes assets/cloud_server.png | Bin 0 -> 897 bytes src/main.c | 107 ++++++++++++++++++++++------------------ 5 files changed, 62 insertions(+), 48 deletions(-) create mode 100644 assets/cloud_both.png create mode 100644 assets/cloud_device.png create mode 100644 assets/cloud_server.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 89e7ccd..6e2c53f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,4 +57,7 @@ vita_create_vpk(${PROJECT_NAME}.vpk ${VITA_TITLEID} ${PROJECT_NAME}.self FILE ${CMAKE_SOURCE_DIR}/assets/Overpass-SemiBold.ttf font.ttf FILE ${CMAKE_SOURCE_DIR}/assets/OFL-Overpass.txt OFL-Overpass.txt FILE ${CMAKE_SOURCE_DIR}/assets/immich_logo.png logo.png + FILE ${CMAKE_SOURCE_DIR}/assets/cloud_server.png cloud_server.png + FILE ${CMAKE_SOURCE_DIR}/assets/cloud_device.png cloud_device.png + FILE ${CMAKE_SOURCE_DIR}/assets/cloud_both.png cloud_both.png ) diff --git a/assets/cloud_both.png b/assets/cloud_both.png new file mode 100644 index 0000000000000000000000000000000000000000..ac4836bcc3242612fb690a6687aab4a0cb4b9de2 GIT binary patch literal 1069 zcmV+|1k(G7P)3OfJtNvZn_3!Ri zs4Qn`ooN95Apc)$zVj2C_^R>PB_Ovhv$ad^DLg>YAAO8(F}n;EJqaxlCr& z%d;eO9bdN>7nH0&O-a5F0k6cnPeNbRjH{DYj5Fw^0NxJWU71Z+sqV#z?NlN;V0VDq4Lk`Psf_9BEb}={xtmM^mjSP4dIQuoz>UC+*lLtUZ%ss0mH zTdm||ceQ$74ans9D)pBL{%oep`Sg%_F#2CJuoD$uMsL>5B(vKSc27A^*om}n zQl~2V#DU%D=K^(GBu8~i9zWj^p10(u%{AFe*(%fx*{pYD`YY9=mCRN5L zdU2+!9!673YbUMcZg#y|^08X|Lp>3{QCoE+I~gGNThd0;Q_C=It-3ejK9l3gFHPx` zvI2Mu7?1apz(>rlwWatRmiGM?a0{>rxPchPw;r-g0mcm6*zZOOywdNGHi z?%J7uz8IxEIIIWGtLTT76nKjAv@^xxPbWR^#e>~nhnL!T{Vwk5!_ZP?t(&BC;bH)N n0G@GK_TLB30vuMBJ;46}!!ryyZ+2EA00000NkvXXu0mjf(tQac literal 0 HcmV?d00001 diff --git a/assets/cloud_device.png b/assets/cloud_device.png new file mode 100644 index 0000000000000000000000000000000000000000..3622baaae29516b3a9f16219a6b6f4aa4d8d5f20 GIT binary patch literal 1092 zcmV-K1iSl*P)VYPzY!H!E3Vs8(${ z16(wadu?pIWP$sE!@wD!Pa%C9I}F@c^HPlgdbX-S=D;NIF@tgrSg`SP4)7B2K5%Or zkX42{kP|=$_|$Csfh{F#Yyn;b&fENxz&%Yt))~t{&L_61k~y7Z><-{Vn|~TuM}wFg z<`98A;FyRY$DL2QVP@*%_NdMOvI&c$(A>4>yFiZ+Tl0ynPICw5I|I4Hey40V1{hhm z4fu&cNtqnwOFUvPdBWBn%3>WbU;CzQ;nTp&z$F7&SAxpc6mZtoy3;Y-2%w*^4tRnP zpeeI@Awg#*S> zvn>y4URUgSyFz=-YJ3L(MsU-Tj!D`kY0)Ok;Om4g@k|5u+I;SuqGv}H@Gx)%SOnHbUw_)|StjNRWuvJq zTZYE$zNAos_F7htn_u62drrHOPD=Vj(xjxPV`h7R{}`~1ExW+hoqFoS2aWv`zKU?fPYcViE%ezEHOs40_wEi78zg^KUEde{t z;UciLB(R(6EjIrliUUS^3W|vmtH@=F>p|wY=Q?Wqq`QE>4CI_6rV+r^(pU?hBJ^JfnLfZ|-G8oD zTVlugT+$zsc1t>Mw*TU=DoNH-d_tBQZ?wdN!k9`fP&WgEwel~X#Zl$gKeCqq0000< KMNUMnLSTYuFab3H literal 0 HcmV?d00001 diff --git a/assets/cloud_server.png b/assets/cloud_server.png new file mode 100644 index 0000000000000000000000000000000000000000..860436e115b13c3a0068c10c107653deeff23feb GIT binary patch literal 897 zcmV-{1AhF8P)Nklx(b41KI8X5PQHx)e3Zt^O!I0ACOM453pssK7$fUz0%v<+Afv|0Z-FbX^d zCX47~BT1FEoblz7`XvpkIK7s1T+*t9Zst~L1alUV-M~d)v%S9!i~!H9zXjLL;M&cQBy|V!SW-UGqCkEk0NGv% z$SmL@1JEexr5WlnS>9x&9P>a1Om|39T^YTK0OpPNnt}7yuge(yD464Jii1@Fj15Sg zq*s!DNouzDnIaHGGLp1M(xhDG=d#RE%Ik2Aw3z3Y1A|$?g>xdeOr|7l2!c>lFKsW5 zaaDg3XaNob7wp|5+zt|@=#`_K>0P(}fxuo-O76uGuG%*N1Hdt$F_6DCptIc{1^h5O zc>`C{*$|+QTq~~dwgY3pIbaBQ1H7}b*$ZtU<&6Kq1!F1zS}<=2FfxGG4BQpqH1NI( zFli(MwGr3~d;q?htZ=_hEc$*+hMU1c%?BA5m0HkcnlC9Q^*{ZTbW2i4S^|dI)gbA+ zq%q6duzuB|dBeSOhlm%(KL=pXBteFrb=sh-X`akMi>$4d)Mvqwq@K_|n%NP~%GIat zIf!1C)@k-fB{g{m!WuNrOpTk5%W@R(dR*nW1FQiK8irh)P!OVb# z$zzIxfe&p^7xNRrG#WQPU20-nag2Z`z(-&|unTt?s+%WZ{_C~b^QUP^B4hh3lZGX& z4P?o~Xf0d4*=d&aP10|BMpB$srv?L%9h&>>MN+Gz2WIzFh$p->`3!ErZFimaeh|11 zeDOiA(I*AB#I^%RfOWW&P7gCnui!iwZe@LvMl?S(Ay= s * 0.34f + k && px <= s * 0.66f - k && + py >= s * 0.52f + k && py <= yb); + return mid || lp || rp || base; +} + static int cov_cloud(float px, float py, int s) { - float yb = s * 0.68f; /* flat bottom */ - int mid = sqrtf((px - s * 0.50f) * (px - s * 0.50f) + - (py - s * 0.42f) * (py - s * 0.42f)) <= s * 0.22f; - int lp = sqrtf((px - s * 0.34f) * (px - s * 0.34f) + - (py - s * 0.52f) * (py - s * 0.52f)) <= s * 0.16f; - int rp = sqrtf((px - s * 0.66f) * (px - s * 0.66f) + - (py - s * 0.52f) * (py - s * 0.52f)) <= s * 0.16f; - int base = (px >= s * 0.34f && px <= s * 0.66f && - py >= s * 0.52f && py <= yb); - return mid || lp || rp || base; + return cloud_filled(px, py, s, 0.0f); +} + +/* a cloud outline (the filled shape minus an eroded copy) */ +static int cov_cloud_line(float px, float py, int s) +{ + float w = s * 0.12f; + if (w < 2.0f) w = 2.0f; + return cloud_filled(px, py, s, 0.0f) && !cloud_filled(px, py, s, w); } static vita2d_texture *bake_icon(int s, int (*inside)(float, float, int)) @@ -2272,9 +2290,9 @@ static int cov_play(float px, float py, int s) /* cache one texture per glyph; all are drawn at a fixed size each frame */ static vita2d_texture *icon_tex(int which, int s) { - static vita2d_texture *cache[8]; - static int cs[8]; - if (which < 0 || which > 7) + static vita2d_texture *cache[9]; + static int cs[9]; + if (which < 0 || which > 8) return NULL; if (cache[which] && cs[which] == s) return cache[which]; @@ -2289,7 +2307,8 @@ static vita2d_texture *icon_tex(int which, int s) which == 3 ? cov_tri : which == 4 ? cov_sq : which == 5 ? cov_cross : - which == 6 ? cov_circle : cov_play; + which == 6 ? cov_circle : + which == 7 ? cov_play : cov_cloud_line; cache[which] = bake_icon(s, fn); cs[which] = s; return cache[which]; @@ -2302,6 +2321,7 @@ static vita2d_texture *icon_tex(int which, int s) #define ICON_CROSS 5 #define ICON_CIRCLE 6 #define ICON_PLAY 7 +#define ICON_CLOUD_LINE 8 /* a DualShock-style face button: a dark disc (subtle lighter rim) centred at * (cx,cy) with the coloured glyph on top. each glyph keeps a per-call size. */ @@ -2870,9 +2890,7 @@ static int run_smart_search(const char *query) static void draw_hud(const char *text) { - vita2d_draw_rectangle(0, SCREEN_H - 32, SCREEN_W, 32, RGBA8(0, 0, 0, 180)); - draw_text(10, SCREEN_H - 9, - RGBA8(255, 255, 255, 255), 1.0f, text); + (void)text; /* bottom HUD bar removed */ } /* spinning throbber: a ring of dots with a brightness tail */ @@ -4180,10 +4198,10 @@ static void save_autobackup(void) /* small colour dot in the top-left corner of a grid cell; a backed-up * item also gets a tiny white check, a local-only item an up-arrow */ -static void draw_status_badge(float bx, float by, int d, unsigned int frame) +static void draw_status_badge(float bx, float by, float bw, float bh, + int d, unsigned int frame) { struct disp_item *it = &g_disp[d]; - uint32_t col; int kind; /* 0 none, 1 cloud, 2 local, 3 busy, 4 backed, 5 failed */ if (it->src == SRC_SERVER) { @@ -4201,32 +4219,22 @@ static void draw_status_badge(float bx, float by, int d, unsigned int frame) if (kind == 0) return; - switch (kind) { - case 1: col = RGBA8(120, 150, 200, 255); break; /* cloud: blue-grey */ - case 2: col = RGBA8(240, 160, 40, 255); break; /* local: orange */ - case 3: /* busy: blinking orange */ - col = (frame / 20) & 1 ? RGBA8(240, 160, 40, 255) - : RGBA8(120, 90, 30, 255); - break; - case 4: col = RGBA8(80, 200, 100, 255); break; /* backed: green */ - default: col = RGBA8(220, 70, 70, 255); break; /* failed: red */ - } - - float cx = bx + 14, cy = by + 14, r = 9; - vita2d_draw_fill_circle(cx, cy, r + 2, RGBA8(0, 0, 0, 160)); - vita2d_draw_fill_circle(cx, cy, r, col); - - uint32_t w = RGBA8(255, 255, 255, 255); - if (kind == 4) { /* check mark */ - vita2d_draw_rectangle(cx - 4, cy, 3, 5, w); - vita2d_draw_rectangle(cx - 2, cy + 2, 3, 3, w); - vita2d_draw_rectangle(cx, cy - 1, 3, 6, w); - vita2d_draw_rectangle(cx + 2, cy - 4, 3, 5, w); - } else if (kind == 2 || kind == 3) { /* up arrow */ - vita2d_draw_rectangle(cx - 1, cy - 4, 3, 9, w); - vita2d_draw_rectangle(cx - 4, cy - 1, 3, 3, w); - vita2d_draw_rectangle(cx + 2, cy - 1, 3, 3, w); - } + /* Immich cloud status icons (the bundled SVGs): cloud = on server only, + * cloud-off = on device only, cloud-done = on both. White, bottom-right + * of the cell, no background; a 1px dark shadow keeps it legible. */ + vita2d_texture *ic = (kind == 4) ? g_ic_both : + (kind == 1) ? g_ic_server : g_ic_device; + if (!ic) + return; + const float isz = 26.0f; + float ix = bx + bw - isz - 4; /* bottom-right corner */ + float iy = by + bh - isz - 4; + float s = isz / vita2d_texture_get_width(ic); + uint32_t fg = (kind == 5) ? RGBA8(235, 90, 90, 255) + : RGBA8(255, 255, 255, 255); + vita2d_draw_texture_tint_scale(ic, ix + 1, iy + 1, s, s, + RGBA8(0, 0, 0, 140)); /* shadow */ + vita2d_draw_texture_tint_scale(ic, ix, iy, s, s, fg); } /* count local items in a given state */ @@ -4320,6 +4328,9 @@ int main(void) g_font = vita2d_load_default_pgf(); g_ttf = vita2d_load_font_file("app0:font.ttf"); g_logo = vita2d_load_PNG_file("app0:logo.png"); + g_ic_server = vita2d_load_PNG_file("app0:cloud_server.png"); + g_ic_device = vita2d_load_PNG_file("app0:cloud_device.png"); + g_ic_both = vita2d_load_PNG_file("app0:cloud_both.png"); /* on-screen keyboard (smart search). the IME runs as a common dialog; * the config tells it the system language + enter/cancel button map */ @@ -5009,7 +5020,7 @@ int main(void) RGBA8(255, 255, 255, 255), 0.85f, "VIDEO"); } - draw_status_badge(bx, by, i, frame); + draw_status_badge(bx, by, bw, bh, i, frame); if (i == sel && show_sel) draw_sel_outline(x + 2, y + 2, g_item_w[i] - 4,