Files
MeowOS-For-R3P2/patch_v2/sdk/meow_ui_img.c
T
AskaEth 68ce46a5d8 fix(ncm §32): 内存瘦身修 OOM 被杀 —— 封面降采样(8.28MB→0.78MB) + 瞬态 rbuf 缩减 + 线程栈显式
真机 dmesg 实锤:OOM Kill ncm (score 460, anon-rss 26.9MB, 设备 56MB)。
画像:封面全分辨率 RGBA 8.28MB(1448x1448 原生解码驻留) 是最大可控块。

瘦身(按收益排序):
1. sdk meow_ui_img 新增纯增量 meow_img_downscale + cover.c cover_cap(≤480px)
   → 封面 8.28MB→0.78MB,-7.65MB(mem_sim 实测 PASS)。
2. ncm_api.c 瞬态响应 rbuf:song_url→64KB、lyric/song_detail/cloudsearch/user_playlist→256KB
   (detail/track_all 保 1MB)。
3. ncm_app_internal.h 新增 ncm_thread_create(显式 128KB 栈),替换全部 14 处 pthread_create。
4. 滑动窗口/meta 复核零回退(window_sim:608B/首,6000 首 280KB 恒定)。

验证:mem_sim 封面收益 PASS + host home/player 渲染 OK + stream_sim/player_flow_sim/
window_sim/search_sim/cache_evict_sim 全 PASS + test_ncm_core host+mipsel(qemu) PASS
+ build_ncm.sh -Wall -Wextra 0 warning。真机 scp 播放在跑确认(缓存曲,0 播放请求)。

新 ncm md5 44fa16fe(text 1015150/bss 1362500)。正式部署留主会话。
2026-09-02 03:43:42 +08:00

155 lines
5.6 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_ui_img.c — 图片层(B1.2)。基于 stb_image(单头文件、静态编入),
* 解 PNG/JPEG/BMP → RGBA,统一最近邻缩放/居中裁方。
*/
#include "meow_ui.h"
#include "meow_ui_internal.h"
#include "stb_image.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct meow_img {
int w, h;
unsigned char *rgba; /* 4 通道 straight alpha */
};
static meow_img *img_alloc(int w, int h, unsigned char *rgba)
{
if (w <= 0 || h <= 0 || !rgba) { if (rgba) stbi_image_free(rgba); return NULL; }
meow_img *im = (meow_img *)malloc(sizeof *im);
if (!im) { stbi_image_free(rgba); return NULL; }
im->w = w; im->h = h; im->rgba = rgba;
return im;
}
meow_img *meow_img_load_mem(const void *buf, int len)
{
if (!buf || len <= 0) return NULL;
int w = 0, h = 0, c = 0;
unsigned char *p = stbi_load_from_memory((const unsigned char *)buf, len, &w, &h, &c, 4);
if (!p) return NULL;
return img_alloc(w, h, p);
}
meow_img *meow_img_load_file(const char *path)
{
if (!path) return NULL;
int w = 0, h = 0, c = 0;
unsigned char *p = stbi_load(path, &w, &h, &c, 4);
if (!p) return NULL;
return img_alloc(w, h, p);
}
void meow_img_free(meow_img *im)
{
if (!im) return;
if (im->rgba) stbi_image_free(im->rgba);
free(im);
}
int meow_img_w(meow_img *im) { return im ? im->w : 0; }
int meow_img_h(meow_img *im) { return im ? im->h : 0; }
/* 最近邻采样缩放(cover=1 居中裁方),返回 dw*dh*4 临时 RGBA 缓冲 */
static unsigned char *scale_buf(const meow_img *im, int dw, int dh, int cover)
{
if (!im || dw <= 0 || dh <= 0) return NULL;
unsigned char *out = (unsigned char *)malloc((size_t)dw * dh * 4);
if (!out) return NULL;
float s = 1.0f;
if (cover) {
/* 真正铺满(cover):每目标像素对应 s 个源像素,s = min(W/dw, H/dh)——缩放后图像
* 宽/高两维都不小于目标(可能外溢),居中裁方。居中裁剪偏移(源像素)= (W-dw*s)/2
* 恒 ≥ 0。曾误作 `s = max(sx,sy)` 且采样 `(dx+ox)/s`(除以缩放比):把整块输出映射到
* 源左上角 ~dw/s 像素的透明角——图标(透明角 logo)整块透明、不绘制,即
* 「icon: ... OK 但渲染不出(只显首字母占位)」根因。正确:源索引 = dx*s + 裁剪偏移
* (与下方非 cover 分支 `dx*im->w/dw` 同一乘向)。 */
float sx = (float)im->w / dw, sy = (float)im->h / dh;
s = sx < sy ? sx : sy; /* 源像素/目标像素(铺满 → 取小) */
float oxs = ((float)im->w - (float)dw * s) * 0.5f; /* 居中裁剪起点(源像素,≥0) */
float oys = ((float)im->h - (float)dh * s) * 0.5f;
if (oxs < 0) oxs = 0; if (oys < 0) oys = 0;
for (int dy = 0; dy < dh; dy++) {
int syi = (int)((float)dy * s + oys);
if (syi < 0) syi = 0; if (syi >= im->h) syi = im->h - 1;
for (int dx = 0; dx < dw; dx++) {
int sxi = (int)((float)dx * s + oxs);
if (sxi < 0) sxi = 0; if (sxi >= im->w) sxi = im->w - 1;
memcpy(out + ((size_t)dy * dw + dx) * 4,
im->rgba + ((size_t)syi * im->w + sxi) * 4, 4);
}
}
} else {
for (int dy = 0; dy < dh; dy++) {
int syi = (int)((float)dy * im->h / dh);
if (syi >= im->h) syi = im->h - 1;
for (int dx = 0; dx < dw; dx++) {
int sxi = (int)((float)dx * im->w / dw);
if (sxi >= im->w) sxi = im->w - 1;
memcpy(out + ((size_t)dy * dw + dx) * 4,
im->rgba + ((size_t)syi * im->w + sxi) * 4, 4);
}
}
}
return out;
}
/* 内存瘦身:把大图按最近邻等比缩放到「最长边 ≤ maxdim」,返回新图;调用方负责
* meow_img_free 旧图 + 新图。原图已 ≤maxdim → 返回 NULL(无需缩放)。
* 典型:网易云全尺寸封面 1448×1448 RGBA=8MB,显示只需 ≤480px(屏幕 480 宽)→
* 480×480=0.9MB,省 ~7MB anon(设备 RAM 仅 56MB,OOM 真机被杀)。
* 复用 scale_buf(cover=0,最近邻锁比例),不做插值(App 封面/背景无需高质量缩放)。 */
meow_img *meow_img_downscale(meow_img *im, int maxdim)
{
if (!im || maxdim <= 0) return NULL;
if (im->w <= maxdim && im->h <= maxdim) return NULL; /* 已够小,不缩放 */
int dw, dh;
if (im->w >= im->h) {
dw = maxdim;
dh = (int)((long)im->h * maxdim / im->w);
} else {
dh = maxdim;
dw = (int)((long)im->w * maxdim / im->h);
}
if (dw < 1) dw = 1;
if (dh < 1) dh = 1;
unsigned char *tmp = scale_buf(im, dw, dh, 0);
if (!tmp) return NULL;
return img_alloc(dw, dh, tmp);
}
void meow_ui_img(int x, int y, meow_img *im)
{
if (!im) return;
meow_gfx_blit_rgba(x, y, im->w, im->h, im->rgba, 255);
}
void meow_ui_img_scaled(int x, int y, int w, int h, meow_img *im)
{
if (!im) return;
unsigned char *tmp = scale_buf(im, w, h, 0);
if (!tmp) return;
meow_gfx_blit_rgba(x, y, w, h, tmp, 255);
free(tmp);
}
void meow_ui_img_alpha(int x, int y, int w, int h, meow_img *im, int alpha)
{
if (!im) return;
unsigned char *tmp = scale_buf(im, w, h, 0);
if (!tmp) return;
meow_gfx_blit_rgba(x, y, w, h, tmp, alpha);
free(tmp);
}
void meow_ui_img_cover(int x, int y, int size, meow_img *im)
{
if (!im) return;
unsigned char *tmp = scale_buf(im, size, size, 1);
if (!tmp) return;
meow_gfx_blit_rgba(x, y, size, size, tmp, 255);
free(tmp);
}