Files
MeowOS-SDK/sdk/meow_img_color.c
T
AskaEth c0396e40ce sync(SDK): 增量同步主仓 M6.37–M6.39 至公开 SDK 仓
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;工作区无生成物。
2026-09-04 18:05:36 +08:00

152 lines
6.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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;
}