feat(lyric): 批5 封面双色渐变取色 — SDK meow_img_color纯整数分桶主色+次色(副采样≤3.6k零分配)+launcher播放页/歌词页set_colors注入渐变主题+无封面回退全局主题+meow_lyric_view灰阶随覆盖cur修正;宿主C1-C7全过,0warning,未并入meow_ui.a

This commit is contained in:
AskaEth committed 2026-09-03 20:44:33 +08:00
1 parent a3679e3688
commit 1f6d390e1c
6 files changed
+541 -22

No files matched your search

+1 -1
View File
@@ -29,7 +29,7 @@ SYSROOT="$ROOT/.tools/mipsel-linux-muslsf-cross/mipsel-linux-muslsf"
font_theme.c keys.c util.c meow_dep.c \
meow_touch.c meow_settings.c meow_notif.c meow_media.c meow_bgplay.c \
meow_ams_start.c lock_screen.c lock_screen_ui.c lock_settings.c \
"../sdk/meow_lyric.c" "../sdk/meow_lyric_view.c" \
"../sdk/meow_lyric.c" "../sdk/meow_lyric_view.c" "../sdk/meow_img_color.c" \
"../sdk/meow_ui.a" "$SYSROOT/lib/libz.a" -lpthread -o meow_launcherd
echo "== meow_launcherd =="
ls -la meow_launcherd
+80 -11
View File
@@ -12,6 +12,7 @@
#include "meow_launcher_internal.h"
#include "np_gesture.h" /* 批4:双页方向感知手势决策(header-only,宿主可单测) */
#include "meow_lyric_view.h" /* 批2控件:歌词页渲染(批4接入,数据批3 meow_media_lyric_*) */
#include "meow_img_color.h" /* 批5:封面双色取色原语(独立源码,不并入 meow_ui.a) */
#define NP_COVER 300 /* 封面边长(方,居中) */
@@ -66,6 +67,56 @@ static void np_cover_load(const meow_media_session_t *s)
g_np_cover = meow_img_load_file(path);
}
/* ==================================================================
* 批5:封面双色取色(主色+次色)→ 播放页/歌词页动态主题。
* - 何时取:进入 Now Playing 页 / 封面路径变化时(np_cover_theme_update 幂等,按路径缓存)。
* - 取色成本:meow_img_duocolor 内部自适副采样(等效缩略图,≤~3.6k 样本),不额外解码/分配。
* - 缓存:按 artwork 路径;路径未变不重取(g_np_cc_valid + g_np_cc_path)。
* - 回退:无封面/取色失败(g_np_cc_valid=0)→ 歌词页 set_colors(NULL) 用 meow_theme 默认;
* 播放页背景/文字走 s_ui_theme() 主题面。
* ================================================================== */
static meow_img_color_theme g_np_cc;
static int g_np_cc_valid = 0;
static char g_np_cc_path[200] = "";
/* 校验/刷新取色缓存(封面路径变化才重取;幂等)。 */
static void np_cover_theme_update(void)
{
const meow_media_session_t *s = meow_media_current();
const char *art = s ? s->artwork : "";
if (g_np_cc_valid && strcmp(g_np_cc_path, art) == 0) return; /* 缓存命中 */
snprintf(g_np_cc_path, sizeof g_np_cc_path, "%s", art);
g_np_cc_valid = 0;
if (!g_np_cover || !art[0]) return; /* 无封面 → 回退主题 */
uint32_t mainc = 0, secondc = 0;
if (meow_img_duocolor(g_np_cover, &mainc, &secondc) != 0) return; /* 取色失败 → 回退主题 */
meow_img_color_derive(mainc, secondc, meow_theme_get(), &g_np_cc);
g_np_cc_valid = 1;
}
/* 把封面取色注入歌词页(无取色 → NULL 回退 meow_theme 默认)。 */
static void np_apply_lyric_colors(void)
{
if (!g_np_lyric) return;
meow_lyric_view_set_colors(g_np_lyric, g_np_cc_valid ? &g_np_cc.lyr : NULL);
}
/* 竖向双色渐变填充(无 GPU,逐行整数插值;封面主题背景用)。 */
static void np_fill_gradient(int x, int y, int w, int h, uint32_t top, uint32_t bottom)
{
if (w <= 0 || h <= 0) return;
int tr = (top >> 16) & 255, tg = (top >> 8) & 255, tb = top & 255;
int br = (bottom >> 16) & 255, bg = (bottom >> 8) & 255, bb = bottom & 255;
int span = h - 1;
for (int yy = 0; yy < h; yy++) {
int t = span > 0 ? yy * 255 / span : 0;
int r = tr + ((br - tr) * t + 128) / 255;
int g = tg + ((bg - tg) * t + 128) / 255;
int b = tb + ((bb - tb) * t + 128) / 255;
fill_rect(x, y + yy, w, 1, ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b);
}
}
#if 0 /* ================================================================
* M7.0b 退役:Now Playing 手绘控制钮/进度条改走 SDK 控件(meow_iconbtn +
* meow_progress),命中/状态/进度全由 SDK 接管;保留作回退位(删除 #if 0 行恢复旧手绘)。
@@ -166,13 +217,14 @@ static void np_widgets_create(void)
g_np_init = 1;
}
/* 主题刷新:播放钮底色/图标随当前主题(明暗/风格切换后跟手)。 */
/* 主题刷新:播放钮底色/图标随当前主题(明暗/风格切换后跟手);批5 封面取色时随 accent。 */
static void np_refresh_theme(void)
{
if (!g_np_init) return;
const meow_theme *th = meow_theme_get();
meow_widget_opts op; memset(&op, 0, sizeof op);
op.bg = th->primary; op.fg = th->onPrimary;
op.bg = g_np_cc_valid ? g_np_cc.accent : th->primary;
op.fg = g_np_cc_valid ? g_np_cc.accent_fg : th->onPrimary;
if (g_np_play) meow_widget_set_opts(g_np_play, &op);
if (g_np_pause) meow_widget_set_opts(g_np_pause, &op);
}
@@ -201,8 +253,9 @@ static void np_draw_progress(const meow_media_session_t *s, uint32_t pos, const
unsigned dm = (s->dur_ms ? s->dur_ms : 0) / 60000u, ds = (s->dur_ms ? s->dur_ms : 0) % 60000u / 1000u;
snprintf(l, sizeof l, "%u:%02u", lm, ls);
snprintf(rbuf, sizeof rbuf, "%u:%02u", dm, ds);
ui_draw(NP_PROG_X, NP_TIME_Y, l, th->on_surface_variant, 16);
ui_draw(NP_PROG_X + NP_PROG_W - ui_width(rbuf, 16), NP_TIME_Y, rbuf, th->on_surface_variant, 16);
uint32_t tcol = g_np_cc_valid ? g_np_cc.fg_dim : th->on_surface_variant;
ui_draw(NP_PROG_X, NP_TIME_Y, l, tcol, 16);
ui_draw(NP_PROG_X + NP_PROG_W - ui_width(rbuf, 16), NP_TIME_Y, rbuf, tcol, 16);
}
/* ==================================================================
@@ -238,14 +291,20 @@ static void np_lyric_refresh(const meow_media_session_t *s)
g_np_lyric_last_valid = valid;
}
/* 歌词页整页绘制:铺 theme surface 底 → lyric_view(自绘 scrim + 歌词/空态)。 */
/* 歌词页整页绘制:铺封面取色渐变(或 theme surface)底 → lyric_view(自绘 scrim + 歌词/空态)。 */
static void np_draw_lyric_page(int redraw_sb)
{
const meow_ui_theme_t *th = s_ui_theme();
int W = fb_xres, H = fb_yres;
if (!g_np_init) np_widgets_create();
if (redraw_sb) fill_rect(0, 0, W, SB_BAR_H, th->surface);
fill_rect(0, SB_BAR_H, W, H - SB_BAR_H, th->surface);
/* 批5:封面取色背景(主→次渐变)或主题面;并注入歌词页 set_colors。 */
np_cover_theme_update();
if (g_np_cc_valid)
np_fill_gradient(0, SB_BAR_H, W, H - SB_BAR_H, g_np_cc.bg_top, g_np_cc.bg_bottom);
else
fill_rect(0, SB_BAR_H, W, H - SB_BAR_H, th->surface);
np_apply_lyric_colors();
np_lyric_refresh(meow_media_current());
if (g_np_lyric)
meow_widget_draw(meow_lyric_view_widget(g_np_lyric));
@@ -264,10 +323,10 @@ static void np_draw_page(int redraw_sb)
return;
}
if (redraw_sb) fill_rect(0, 0, W, SB_BAR_H, th->surface);
fill_rect(0, SB_BAR_H, W, H - SB_BAR_H, th->surface);
const meow_media_session_t *s = meow_media_current();
if (!s || s->state == MEOW_MEDIA_STOPPED) {
/* 空态占位(M7.0b 文案走 meow_str,未知 id 回退中文) */
fill_rect(0, SB_BAR_H, W, H - SB_BAR_H, th->surface);
const char *t1 = meow_str_zh("np.not_playing", "未在播放");
ui_draw((W - ui_width(t1, 26)) / 2, 300, t1, th->on_surface_variant, 26);
const char *t2 = meow_str_zh("np.open_app_hint", "打开播放 App 以显示正在播放");
@@ -278,6 +337,14 @@ static void np_draw_page(int redraw_sb)
return;
}
np_cover_load(s);
/* 批5:封面取色(主/次色)+ 背景渐变或主题面;并注入歌词页 set_colors。
* np_cover_theme_update 幂等(封面路径不变不重取);无封面/取色失败回退 meow_theme。 */
np_cover_theme_update();
if (g_np_cc_valid)
np_fill_gradient(0, SB_BAR_H, W, H - SB_BAR_H, g_np_cc.bg_top, g_np_cc.bg_bottom);
else
fill_rect(0, SB_BAR_H, W, H - SB_BAR_H, th->surface);
np_apply_lyric_colors();
/* 封面:解码缓存 + 圆角裁方;无封面回退主题色占位 + 首字 */
if (g_np_cover) {
meow_gfx_fill_round(NP_COVER_X - 6, NP_COVER_Y - 6, NP_COVER + 12, NP_COVER + 12, 18, th->surface_container);
@@ -290,11 +357,11 @@ static void np_draw_page(int redraw_sb)
ui_draw(NP_COVER_X + (NP_COVER - cw) / 2, NP_COVER_Y + (NP_COVER - meow_font_line_height(22)) / 2,
t, meow_theme_get()->onPrimary, 22);
}
/* 元数据 */
np_center_text(NP_TITLE_Y, s->title, 26, th->on_surface, W - 40);
np_center_text(NP_ARTIST_Y, s->artist, 18, th->on_surface_variant, W - 60);
/* 元数据(批5:封面取色时文字随 fg/fg_dim,否则主题面) */
np_center_text(NP_TITLE_Y, s->title, 26, g_np_cc_valid ? g_np_cc.fg : th->on_surface, W - 40);
np_center_text(NP_ARTIST_Y, s->artist, 18, g_np_cc_valid ? g_np_cc.fg_dim : th->on_surface_variant, W - 60);
if (s->album[0])
np_center_text(NP_ALBUM_Y, s->album, 16, th->on_surface_variant, W - 80);
np_center_text(NP_ALBUM_Y, s->album, 16, g_np_cc_valid ? g_np_cc.fg_dim : th->on_surface_variant, W - 80);
/* 进度(SDK meow_progress) */
uint32_t pos = meow_media_display_pos(s);
np_draw_progress(s, pos, th);
@@ -564,4 +631,6 @@ void run_now_playing(struct app *apps, int napps)
kp_close(&kp_power);
if (g_np_cover) { meow_img_free(g_np_cover); g_np_cover = NULL; }
g_np_cover_path[0] = 0;
g_np_cc_valid = 0; /* 批5:退出清取色缓存,下次进入重新取 */
g_np_cc_path[0] = 0;
}