sdk/: - 同步 meow_ui_widget/input/gfx/lazygrid/audio/media/img 等源码与头 (SDK 默认列表/容器控件默认 MD3 — meow_list_set_container 可回退; 全局 App 内底部上滑回桌面 — meow_ui_input_is_home_swipe 系统保留手势; LazyGrid/LazyColumn/LazyRow MD3 化;叠加让位自抑改 app_id 判定; DSD 音频格式等) - 新增独立模块源码 meow_img_color / meow_lyric / meow_lyric_view 及对应宿主 test_*.c - 保留公开仓自包含适配:vendored meow_font.c/meow_font.h/meow_notif.h 不动; build_sdk.sh 自包含版本不动;meow_ui_gfx.c 仅改 meow_font.h include 路径; meow_ui_internal.h 去除 FreeType 内部声明 - meow_ipc.h 同步到主仓 launcher 版本(含 AUDIO_FMT_DOP/媒体歌词/蓝牙输出协议) - 不含 FreeType 矢量字 meow_ft_*(需 freetype vendor,公开仓以位图 meow_font 自包含);不含 mipsel 二进制 / .o / .a / 固件 / 密钥 / devdocs examples/hello/: - 新增 ui/demo/sdk2/sdk2.c;build_hello.sh 纳入该文件(保持自包含) docs / README / .gitignore: - docs/MEOW_APP_SDK.md 补 §12.5(默认 MD3 容器 + 底部上滑回桌面 + 两个新 API) - README 能力清单补「默认 MD3 容器」与「底部上滑回桌面」 - .gitignore 补新增宿主测试二进制 验证:sdk/meow_ui.a 与 examples/hello/hello 交叉编译均 0 warning;工作区无生成物。
152 lines
6.9 KiB
C
152 lines
6.9 KiB
C
/*
|
||
* meow_img_color.c — MeowOS SDK 封面双色渐变取色实现(见 meow_img_color.h 契约)。
|
||
*
|
||
* 独立源码,不并入 meow_ui.a(铁律:避免并发重编/四 App 重链)。取色算法纯函数(无 GPU/浮点/堆)。
|
||
* 唯一 meow_* 依赖:仅 meow_img_pixels(SDK 正式访问器,meow_ui_img.c §4),并经 meow_img_w/h 取宽高
|
||
* ——三者读 meow_img 的唯一入口,本 TU 不再局部补全其结构体(agent-402 遗留点已上收)。App/宿主
|
||
* 链入 meow_img_color.c 时需能解析 meow_img_pixels/w/h(宿主测试已链 meow_ui_img.c,实际 App 恒链
|
||
* meow_ui.a)。
|
||
*/
|
||
#include "meow_img_color.h"
|
||
|
||
#include <string.h>
|
||
|
||
#define MIC_BUCKETS 4 /* 每通道色阶数 ⇒ 4×4×4=64 桶 */
|
||
#define MIC_NBUCKET (MIC_BUCKETS*MIC_BUCKETS*MIC_BUCKETS)
|
||
#define MIC_ALPHA_MIN 64 /* 跳过近透明(alpha<64) */
|
||
#define MIC_LUMA_MIN 24 /* 跳过近黑 */
|
||
#define MIC_LUMA_MAX 244 /* 跳过近白 */
|
||
#define MIC_MIN_VALID 8 /* 有效采样下限(低于=取色失败回退主题) */
|
||
#define MIC_TARGET 900 /* 目标采样数(副采样步进自适,×4 作上限) */
|
||
#define MIC_SEC_FRAC 7U /* 次桶显著门槛:≥主桶 1/7(≈14%) */
|
||
|
||
static int mic_luma(uint32_t c)
|
||
{
|
||
int r = (int)((c >> 16) & 255), g = (int)((c >> 8) & 255), b = (int)(c & 255);
|
||
return (r * 299 + g * 587 + b * 114) / 1000;
|
||
}
|
||
|
||
/* 两色线性混合(t=0..255,0=纯 a、255=纯 b;逐通道整数,无浮点)。 */
|
||
static uint32_t mic_mix(uint32_t a, uint32_t b, int t)
|
||
{
|
||
if (t < 0) t = 0;
|
||
if (t > 255) t = 255;
|
||
int ar = (a >> 16) & 255, ag = (a >> 8) & 255, ab = a & 255;
|
||
int br = (b >> 16) & 255, bg = (b >> 8) & 255, bb = b & 255;
|
||
int r = ar + ((br - ar) * t + 128) / 255;
|
||
int g = ag + ((bg - ag) * t + 128) / 255;
|
||
int bl = ab + ((bb - ab) * t + 128) / 255;
|
||
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)bl;
|
||
}
|
||
|
||
int meow_img_duocolor_rgba(const uint8_t *rgba, int w, int h,
|
||
uint32_t *main, uint32_t *second)
|
||
{
|
||
if (!rgba || w <= 0 || h <= 0 || !main || !second) return -1;
|
||
|
||
/* 副采样步进:使采样数≈MIC_TARGET(×4 上限),大图/缩略图成本恒定。 */
|
||
int step = 1;
|
||
while ((long)(w / step) * (long)(h / step) > (long)MIC_TARGET * 4 && step < 64)
|
||
step++;
|
||
|
||
long cnt[MIC_NBUCKET];
|
||
long sr[MIC_NBUCKET], sg[MIC_NBUCKET], sb[MIC_NBUCKET], sw[MIC_NBUCKET];
|
||
memset(cnt, 0, sizeof cnt);
|
||
memset(sr, 0, sizeof sr);
|
||
memset(sg, 0, sizeof sg);
|
||
memset(sb, 0, sizeof sb);
|
||
memset(sw, 0, sizeof sw);
|
||
|
||
long total = 0;
|
||
for (int y = 0; y < h; y += step) {
|
||
for (int x = 0; x < w; x += step) {
|
||
const uint8_t *p = rgba + ((size_t)y * (size_t)w + (size_t)x) * 4;
|
||
int a = p[3];
|
||
if (a < MIC_ALPHA_MIN) continue;
|
||
int r = p[0], g = p[1], b = p[2];
|
||
if ((r * 299 + g * 587 + b * 114) / 1000 < MIC_LUMA_MIN) continue; /* 近黑 */
|
||
if ((r * 299 + g * 587 + b * 114) / 1000 > MIC_LUMA_MAX) continue; /* 近白 */
|
||
int mx = r, mn = r;
|
||
if (g > mx) mx = g;
|
||
if (b > mx) mx = b;
|
||
if (g < mn) mn = g;
|
||
if (b < mn) mn = b;
|
||
int sat = mx - mn; /* 饱和度 0..255 */
|
||
long wt = 1 + sat / 24; /* 1..~11:偏向鲜艳(跳过灰蒙蒙) */
|
||
int qr = r * MIC_BUCKETS / 256, qg = g * MIC_BUCKETS / 256, qb = b * MIC_BUCKETS / 256;
|
||
int bi = (qr * MIC_BUCKETS + qg) * MIC_BUCKETS + qb;
|
||
if (bi < 0 || bi >= MIC_NBUCKET) continue;
|
||
cnt[bi] += wt;
|
||
sr[bi] += (long)r * wt; sg[bi] += (long)g * wt; sb[bi] += (long)b * wt;
|
||
sw[bi] += wt;
|
||
total++;
|
||
}
|
||
}
|
||
if (total < MIC_MIN_VALID) return -1; /* 全透明/近黑白 → 失败回退主题 */
|
||
|
||
/* 主色:加权最大桶中心 */
|
||
int mi = -1;
|
||
long best = -1;
|
||
for (int i = 0; i < MIC_NBUCKET; i++)
|
||
if (cnt[i] > best) { best = cnt[i]; mi = i; }
|
||
if (mi < 0 || sw[mi] <= 0) return -1;
|
||
long mr0 = sr[mi] / sw[mi], mg0 = sg[mi] / sw[mi], mb0 = sb[mi] / sw[mi];
|
||
*main = ((uint32_t)mr0 << 16) | ((uint32_t)mg0 << 8) | (uint32_t)mb0;
|
||
|
||
/* 次色:与主色 RGB 色距最大的显著桶;无显著桶→主色缩暗 2/3(渐变互补)。 */
|
||
long thresh = best > 0 ? best / MIC_SEC_FRAC : 1;
|
||
int si = -1;
|
||
long sd = -1;
|
||
for (int i = 0; i < MIC_NBUCKET; i++) {
|
||
if (i == mi || cnt[i] < thresh || sw[i] <= 0) continue;
|
||
long dr = sr[i] / sw[i] - mr0, dg = sg[i] / sw[i] - mg0, db = sb[i] / sw[i] - mb0;
|
||
long d = dr * dr + dg * dg + db * db;
|
||
if (d > sd) { sd = d; si = i; }
|
||
}
|
||
if (si >= 0)
|
||
*second = ((uint32_t)(sr[si] / sw[si]) << 16) | ((uint32_t)(sg[si] / sw[si]) << 8) |
|
||
(uint32_t)(sb[si] / sw[si]);
|
||
else
|
||
*second = ((uint32_t)(mr0 * 2 / 3) << 16) | ((uint32_t)(mg0 * 2 / 3) << 8) |
|
||
(uint32_t)(mb0 * 2 / 3);
|
||
return 0;
|
||
}
|
||
|
||
int meow_img_duocolor(const meow_img *im, uint32_t *main, uint32_t *second)
|
||
{
|
||
if (!im) return -1;
|
||
/* 经 SDK 正式访问器读像素(meow_img opaque;meow_img_pixels 为唯一读入口)。 */
|
||
const unsigned char *rgba = meow_img_pixels(im);
|
||
if (!rgba) return -1;
|
||
return meow_img_duocolor_rgba(rgba, meow_img_w(im), meow_img_h(im), main, second);
|
||
}
|
||
|
||
void meow_img_color_derive(uint32_t main, uint32_t second, const meow_theme *th,
|
||
meow_img_color_theme *out)
|
||
{
|
||
if (!out) return;
|
||
uint32_t mainv = main ? main : (th ? th->primary : 0x808080);
|
||
uint32_t secv = second ? second : (th ? th->secondary : mainv);
|
||
/* 背景平均亮度 → 文字可读色(黑/白) */
|
||
int luavg = (mic_luma(mainv) + mic_luma(secv)) / 2;
|
||
uint32_t fg = (luavg >= 140) ? 0x1C1C1C : 0xFFFFFF;
|
||
uint32_t fgd = mic_mix(fg, mic_mix(mainv, 0x0A0A0A, 150), 90); /* 次要文字向背景靠(降对比) */
|
||
/* 背景渐变:主/次各压暗 55%(防刺眼 + 保文字可读) */
|
||
uint32_t bg_top = mic_mix(mainv, 0x0A0A0A, 150);
|
||
uint32_t bg_bottom = mic_mix(secv, 0x0A0A0A, 150);
|
||
/* 强调=次色(与主色底对比);其上图标按 accent 亮度选黑/白 */
|
||
uint32_t accent = secv;
|
||
uint32_t accent_fg = (mic_luma(accent) >= 140) ? 0x1C1C1C : 0xFFFFFF;
|
||
|
||
out->bg_top = bg_top; out->bg_bottom = bg_bottom;
|
||
out->fg = fg; out->fg_dim = fgd;
|
||
out->accent = accent; out->accent_fg = accent_fg;
|
||
|
||
/* 歌词页覆盖:scrim=主色压暗、cur=fg(可读)、gray 留 0(lyric_view 以 cur 自动递减)。 */
|
||
meow_lyric_view_colors *lc = &out->lyr;
|
||
memset(lc, 0, sizeof *lc);
|
||
lc->scrim = mic_mix(mainv, 0x000000, 150);
|
||
lc->cur = fg;
|
||
lc->scrim_alpha = 176;
|
||
}
|