121 lines
6.2 KiB
Python
121 lines
6.2 KiB
Python
"""实际 HUD 函数与生成资产的 ASan/UBSan 回归;不启动 LVGL/GFX 引擎。"""
|
|
from pathlib import Path
|
|
import subprocess
|
|
import tempfile
|
|
import re
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
src = (ROOT/'components/platform/src/lcd_direct.c').read_text()
|
|
def function(name):
|
|
matches = list(re.finditer(r'static [^;{}]+\b'+name+r'\([^;{}]*\)\s*\{', src))
|
|
assert len(matches) == 1, name
|
|
start = matches[0].start(); p = src.index('{', start); depth = 1; end = p+1
|
|
while depth:
|
|
if src[end] == '{': depth += 1
|
|
if src[end] == '}': depth -= 1
|
|
end += 1
|
|
return src[start:end]
|
|
prefix = '''#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include "lcd_status_assets.h"
|
|
#define LCD_DIRECT_CLOCK_X 12U
|
|
#define LCD_DIRECT_CLOCK_Y 14U
|
|
#define LCD_DIRECT_CLOCK_TEXT_LENGTH 5U
|
|
#define LCD_DIRECT_CLOCK_COLOR 0xffffU
|
|
#define LCD_DIRECT_CLOCK_OUTLINE_COLOR 0U
|
|
#define LCD_DIRECT_BATTERY_WIDTH 40U
|
|
#define LCD_DIRECT_BATTERY_HEIGHT 20U
|
|
#define LCD_DIRECT_BATTERY_X (LCD_DIRECT_WIDTH - 48U)
|
|
#define LCD_DIRECT_BATTERY_Y 12U
|
|
#define portENTER_CRITICAL(x) ((void)0)
|
|
#define portEXIT_CRITICAL(x) ((void)0)
|
|
typedef struct {bool low_battery_visible, charging_visible, battery_valid; uint8_t battery_percent;} platform_lcd_status_overlay_t;
|
|
static platform_lcd_status_overlay_t s_status_overlay;
|
|
#define PLATFORM_LCD_PRESENTATION_IDLE 0
|
|
typedef struct {int kind;} lcd_direct_request_t;
|
|
static lcd_direct_request_t s_pending;
|
|
static bool s_pending_valid, s_stop_requested;
|
|
'''
|
|
test = '''int main(int argc, char **argv) {
|
|
uint16_t full[LCD_DIRECT_WIDTH*240+2], blocks[LCD_DIRECT_WIDTH*240+2];
|
|
for (unsigned valid=0;valid<2;++valid)
|
|
for (unsigned charge=0;charge<2;++charge)
|
|
for (unsigned low=0;low<2;++low)
|
|
for (unsigned percent=0;percent<=255;++percent) {
|
|
s_status_overlay=(platform_lcd_status_overlay_t){low,charge,valid,percent};
|
|
for (unsigned i=0;i<LCD_DIRECT_WIDTH*240+2;++i) full[i]=blocks[i]=0x1234;
|
|
lcd_direct_overlay_battery(full+1,0,240);
|
|
for(unsigned row=0;row<240;row+=8)
|
|
lcd_direct_overlay_battery(blocks+1+row*LCD_DIRECT_WIDTH,row,8);
|
|
assert(memcmp(full,blocks,sizeof(full))==0);
|
|
assert(full[0]==0x1234 && full[LCD_DIRECT_WIDTH*240+1]==0x1234);
|
|
unsigned level=percent<=10?0:percent<=25?1:percent<=50?2:percent<=75?3:4;
|
|
assert(lcd_direct_battery_icon_index(percent,charge)==level+(charge?5:0));
|
|
for(unsigned y=0;y<240;++y) for(unsigned x=0;x<LCD_DIRECT_WIDTH;++x) {
|
|
if(!valid || x<LCD_DIRECT_BATTERY_X || x>=LCD_DIRECT_BATTERY_X+40 || y<12 || y>=32)
|
|
assert(full[1+y*LCD_DIRECT_WIDTH+x]==0x1234);
|
|
}
|
|
if(valid) {
|
|
const lcd_status_icon_t *icon=&lcd_status_battery_icons[level+(charge?5:0)];
|
|
unsigned p=0;
|
|
for(unsigned r=0;r<icon->run_bytes;r+=2) {
|
|
const uint8_t *value=icon->palette+icon->runs[r+1]*3;
|
|
for(unsigned n=0;n<icon->runs[r];++n,++p) {
|
|
unsigned index=1+(12+p/40)*LCD_DIRECT_WIDTH+LCD_DIRECT_BATTERY_X+p%40;
|
|
uint16_t color=((uint16_t)value[0]<<8)|value[1];
|
|
assert(full[index]==lcd_direct_swap_color(lcd_direct_blend_rgb565(lcd_direct_swap_color(0x1234),color,value[2])));
|
|
}
|
|
}
|
|
assert(p==800);
|
|
}
|
|
}
|
|
for(unsigned hour=0;hour<24;++hour) for(unsigned minute=0;minute<60;++minute) {
|
|
char text[6]; snprintf(text,sizeof(text),"%02u:%02u",hour,minute);
|
|
for(unsigned i=0;i<LCD_DIRECT_WIDTH*240+2;++i) full[i]=blocks[i]=0x1234;
|
|
lcd_direct_overlay_clock(full+1,0,240,text);
|
|
for(unsigned row=0;row<240;row+=8)
|
|
lcd_direct_overlay_clock(blocks+1+row*LCD_DIRECT_WIDTH,row,8,text);
|
|
assert(memcmp(full,blocks,sizeof(full))==0);
|
|
assert(full[0]==0x1234 && full[LCD_DIRECT_WIDTH*240+1]==0x1234);
|
|
for(unsigned y=0;y<240;++y) for(unsigned x=0;x<LCD_DIRECT_WIDTH;++x)
|
|
if(x<12 || x>=64 || y<14 || y>=31) assert(full[1+y*LCD_DIRECT_WIDTH+x]==0x1234);
|
|
}
|
|
lcd_direct_overlay_clock(full+1,0,240,"--:--");
|
|
lcd_direct_overlay_clock(NULL,0,240,"--:--");
|
|
lcd_direct_overlay_clock(full+1,0,240,NULL);
|
|
lcd_direct_overlay_battery(NULL,0,240);
|
|
for(unsigned pending=0;pending<2;++pending)
|
|
for(unsigned stopping=0;stopping<2;++stopping) {
|
|
s_pending=(lcd_direct_request_t){99}; s_pending_valid=pending; s_stop_requested=stopping;
|
|
lcd_direct_queue_idle_if_available();
|
|
assert(s_pending.kind==((pending || stopping)?99:PLATFORM_LCD_PRESENTATION_IDLE));
|
|
assert(s_pending_valid==(pending || !stopping));
|
|
}
|
|
if(argc==2) {
|
|
memset(full,0,sizeof(full));
|
|
lcd_direct_overlay_clock(full,0,240,"12:34");
|
|
s_status_overlay=(platform_lcd_status_overlay_t){false,true,true,50};
|
|
lcd_direct_overlay_battery(full,0,240);
|
|
FILE *f=fopen(argv[1],"wb"); assert(f); fwrite(full,2,LCD_DIRECT_WIDTH*240,f); fclose(f);
|
|
}
|
|
puts("PASS: 2048 battery states, all 1440 times, alpha/background, byte order, 8-row equivalence, bounds and idle guards");
|
|
}'''
|
|
names = ['lcd_direct_swap_color', 'lcd_direct_rgb888_to_rgb565', 'lcd_direct_blend_rgb565', 'lcd_direct_clock_glyph_index',
|
|
'lcd_direct_hud_blend_pixel', 'lcd_direct_draw_clock_pass', 'lcd_direct_overlay_clock',
|
|
'lcd_direct_battery_icon_index', 'lcd_direct_overlay_battery', 'lcd_direct_queue_idle_if_available']
|
|
with tempfile.TemporaryDirectory(prefix='lucky-hud-') as tmp:
|
|
c = Path(tmp)/'hud.c'; exe = Path(tmp)/'hud'
|
|
c.write_text(prefix+'\n'+'\n'.join(function(n) for n in names)+'\n'+test)
|
|
for width in (320, 240):
|
|
for swap in (0, 1):
|
|
subprocess.run(['clang','-std=c11','-fsanitize=address,undefined','-g',
|
|
f'-DLCD_DIRECT_WIDTH={width}', f'-DCONFIG_TQ_SCREEN_SWAP_COLOR_BYTES={swap}',
|
|
'-I'+str(ROOT/'components/platform/internal'),str(c),
|
|
str(ROOT/'components/platform/src/generated/lcd_status_assets.c'),
|
|
'-o',str(exe)],check=True)
|
|
preview = ['/tmp/lucky-status-hud.rgb565'] if width == 320 and swap == 0 else []
|
|
subprocess.run([str(exe), *preview],check=True)
|