perf(explorer): 修半透明/圆角混合底,并优化阴影与混合热点

① 混合底:上一版背景裁剪会跳过窗口覆盖的列,导致 back 在那些列保留上一帧内容;而 WIN7 标题栏 alpha 混合、WIN11/XP 圆角逐像素裁剪都需要读到正确背景。现在按样式在必要行补铺背景:WIN7 铺标题栏行 + 底部圆角段,WIN11/XP 铺上下圆角段,CLASSIC/WIN10 不铺(直角不透明)。补铺发生在所有窗口绘制之前,下层窗口随后正常覆盖。② 性能:阴影 alpha 改按行查表 + 无除法 SWAR 变暗;_blend/_blend_row 改 SWAR 并行混合(无除法、无格式分支);内容行不再先整行 fill 再 blit,直接 blit + 左右 1px 边框;圆角行逐像素只保留左右各 r 列,中段整段拷贝(宽 800 窗每圆角行从约 800 次降到 16 次);关闭键圆角先扫连续区间再整段 fill;render_range 缓存每个可见窗口的几何/样式,_top_z 只算一次并传入。
This commit is contained in:
TermiNexus committed 2026-10-17 11:36:34 +08:00
1 parent 9f372ce814
commit a19c5d1a53
1 file changed
+211 -136
+211 -136
View File
@@ -156,30 +156,12 @@ class DeskServer:
return (uint32(255) << 24) | (r << 16) | (g << 8) | b
return (uint32(255) << 24) | (b << 16) | (g << 8) | r
# ---- 按 framebuffer 格式把 fg 以 alpha/255 混到 bg 上 ----
# ---- 按 alpha/255 把 fg 混到 bg 上(RGBA/BGRA 同为 R/B@0,16、G@8,可共用 SWAR)----
def _blend(self, bg: uint32, fg: uint32, a: int) -> uint32:
ia: int = 255 - a
if self.fmt == uint32(1):
br: int = int((bg >> 16) & 0xFF)
bg_g: int = int((bg >> 8) & 0xFF)
bg_b: int = int(bg & 0xFF)
fr: int = int((fg >> 16) & 0xFF)
fg_g: int = int((fg >> 8) & 0xFF)
fg_b: int = int(fg & 0xFF)
r: int = (fr * a + br * ia) >> 8
g: int = (fg_g * a + bg_g * ia) >> 8
b: int = (fg_b * a + bg_b * ia) >> 8
return (uint32(255) << 24) | (uint32(r) << 16) | (uint32(g) << 8) | uint32(b)
br = int(bg & 0xFF)
bg_g = int((bg >> 8) & 0xFF)
bg_b = int((bg >> 16) & 0xFF)
fr = int(fg & 0xFF)
fg_g = int((fg >> 8) & 0xFF)
fg_b = int((fg >> 16) & 0xFF)
r = (fr * a + br * ia) >> 8
g = (fg_g * a + bg_g * ia) >> 8
b = (fg_b * a + bg_b * ia) >> 8
return (uint32(255) << 24) | (uint32(b) << 16) | (uint32(g) << 8) | uint32(r)
v: int = ((int(fg & uint32(0x00FF00FF)) * a) + (int(bg & uint32(0x00FF00FF)) * ia)) >> 8
g: int = ((int((fg >> 8) & uint32(0xFF)) * a) + (int((bg >> 8) & uint32(0xFF)) * ia)) >> 8
return (uint32(255) << 24) | (uint32(v) & uint32(0x00FF00FF)) | (uint32(g & 0xFF) << 8)
def init(self, fb: uint32*, w: int, h: int, pitch: int, fmt: uint32,
shm: uint64, kfd: int, mfd: int, fb_fd: int):
@@ -375,6 +357,20 @@ class DeskServer:
i += 1
# ---------------- 窗口阴影 ----------------
# 单个阴影像素的 alpha(与原逐像素算法完全相同)
def _shadow_alpha(self, adx: int, ady: int, me_x: int, me_y: int) -> int:
dist: int = adx
if ady > dist: dist = ady
dist = dist + ((adx + ady - dist) * 3) / 8
me: int = me_x
if adx + ady > 0:
me = (me_x * adx + me_y * ady) / (adx + ady)
if me > 0 and dist > 0 and dist < me:
t_val: int = (256 * dist) / me
t_inv: int = 256 - t_val
return (SHADOW_ALPHA * t_inv * t_inv) / 65536
return 0
def _draw_shadow(self, s: desk.WinSlot*, y0: int, y1: int):
wx: int = s.ux
wy: int = s.uy
@@ -384,54 +380,76 @@ class DeskServer:
sr: int = SHADOW_RIGHT
st: int = SHADOW_TOP
sb: int = SHADOW_BOTTOM
sa: int = SHADOW_ALPHA
shadow_col: uint32 = self.rgb(uint32(0), uint32(0), uint32(0))
# 左右窄带(dy=0)的 alpha 只随 adx 变化,预建表
lt: [16]int
rt: [16]int
tl: [16]int
tr: [16]int
i: int = 0
while i <= sl:
lt[i] = self._shadow_alpha(i, 0, sl, st)
i += 1
i = 0
while i <= sr:
rt[i] = self._shadow_alpha(i, 0, sr, st)
i += 1
yy: int = y0
if yy < wy - st: yy = wy - st
yend: int = y1
if yend > wy + wh + sb: yend = wy + wh + sb
while yy < yend:
if yy >= 0 and yy < self.fb_h:
side: int = 0
ady: int = 0
me_y: int = st
if yy < wy:
ady = wy - yy
elif yy >= wy + wh:
ady = yy - (wy + wh - 1)
me_y = sb
else:
side = 1
tm: int = 0
if side == 0:
i = 0
while i <= sl:
tl[i] = self._shadow_alpha(i, ady, sl, me_y)
i += 1
i = 0
while i <= sr:
tr[i] = self._shadow_alpha(i, ady, sr, me_y)
i += 1
tm = self._shadow_alpha(0, ady, sl, me_y)
px: int = wx - sl
if px < 0: px = 0
xend: int = wx + ww + sr
if xend > self.fb_w: xend = self.fb_w
base: int = yy * self.fb_w
while px < xend:
if px < wx or px >= wx + ww or yy < wy or yy >= wy + wh:
cpx: int = px
if cpx < wx: cpx = wx
elif cpx > wx + ww - 1: cpx = wx + ww - 1
cpy: int = yy
if cpy < wy: cpy = wy
elif cpy > wy + wh - 1: cpy = wy + wh - 1
dx: int = px - cpx
dy: int = yy - cpy
adx: int = dx
if adx < 0: adx = 0 - adx
ady: int = dy
if ady < 0: ady = 0 - ady
dist: int = adx
if ady > dist: dist = ady
dist = dist + ((adx + ady - dist) * 3) / 8
me_x: int = sl
if dx > 0: me_x = sr
me_y: int = st
if dy > 0: me_y = sb
me: int = me_x
if adx + ady > 0:
me = (me_x * adx + me_y * ady) / (adx + ady)
if me > 0 and dist > 0 and dist < me:
t_val: int = (256 * dist) / me
t_inv: int = 256 - t_val
a: int = (sa * t_inv * t_inv) / 65536
if a > 0:
idx: int = yy * self.fb_w + px
self.back[idx] = self._blend(self.back[idx], shadow_col, a)
a: int = 0
adx: int = 0
if px < wx:
adx = wx - px
if side != 0: a = lt[adx]
else: a = tl[adx]
elif px >= wx + ww:
adx = px - (wx + ww - 1)
if side != 0: a = rt[adx]
else: a = tr[adx]
elif side == 0:
a = tm
if a > 0:
idx: int = base + px
bg: uint32 = self.back[idx]
ia: int = 255 - a
rb: int = (int(bg & uint32(0x00FF00FF)) * ia) >> 8
g: int = (int((bg >> 8) & uint32(0xFF)) * ia) >> 8
self.back[idx] = (uint32(255) << 24) | (uint32(rb) & uint32(0x00FF00FF)) | (uint32(g & 0xFF) << 8)
px += 1
yy += 1
# ---------------- 窗口逐行绘制(按样式)----------------
# 把 col 以 alpha 混到后备缓冲已有像素上(WIN7 半透明标题栏用)
# 把 col 以 alpha 混到后备缓冲已有像素上(WIN7 半透明标题栏用);逐像素内联 SWAR
def _blend_row(self, x0: int, y: int, w: int, col: uint32, a: int):
if y < 0 or y >= self.fb_h or w <= 0 or a <= 0: return
x: int = x0
@@ -442,11 +460,17 @@ class DeskServer:
if x + dw > self.fb_w:
dw = self.fb_w - x
if dw <= 0: return
ia: int = 255 - a
frb: int = int(col & uint32(0x00FF00FF)) * a
fg: int = int((col >> 8) & uint32(0xFF)) * a
base: int = y * self.fb_w
i: int = 0
while i < dw:
idx: int = base + x + i
self.back[idx] = self._blend(self.back[idx], col, a)
bg: uint32 = self.back[idx]
v: int = (frb + int(bg & uint32(0x00FF00FF)) * ia) >> 8
g: int = (fg + int((bg >> 8) & uint32(0xFF)) * ia) >> 8
self.back[idx] = (uint32(255) << 24) | (uint32(v) & uint32(0x00FF00FF)) | (uint32(g & 0xFF) << 8)
i += 1
# 标题栏每行颜色:WIN7/WINXP 竖直渐变 + 首两行高光 + 末行暗边,其余样式纯色
@@ -494,7 +518,7 @@ class DeskServer:
col = self._blend(col, self.rgb(uint32(100), uint32(160), uint32(230)), 40)
return col
# 直边行:整行底色 + 内容 blit + 左右/上下 1px 边框
# 直边行:整行底色/内容 blit + 左右/上下 1px 边框(内容行不再先铺整行底色)
def _draw_flat_row(self, s: desk.WinSlot*, yy: int, rel: int, row_col: uint32,
row_is_title: int, border_col: uint32, title_alpha: int,
buf: uint32*, fw: int, fh: int, content_y0: int):
@@ -504,17 +528,17 @@ class DeskServer:
content_row: int = yy - content_y0
if row_is_title != 0 and title_alpha < 255:
self._blend_row(wx + 1, yy, ww - 2, row_col, title_alpha)
elif rel >= TITLE_H + TITLE_GAP and rel < wh - 1 and content_row >= 0 and content_row < fh:
self.row_blit(wx + 1, yy, buf, content_row * fw + 1, ww - 2)
else:
self.row_fill(wx, yy, ww, row_col)
if rel >= TITLE_H + TITLE_GAP and rel < wh - 1 and content_row >= 0 and content_row < fh:
self.row_blit(wx + 1, yy, buf, content_row * fw + 1, ww - 2)
if rel == 0 or rel == wh - 1:
self.row_fill(wx, yy, ww, border_col)
else:
self.row_fill(wx, yy, 1, border_col)
self.row_fill(wx + ww - 1, yy, 1, border_col)
# 圆角带的行:逐像素按圆角形状裁剪,画边框/标题渐变/内容
# 圆角带的行:只有左右各 r 列需要逐像素,中段按直行整段处理
def _draw_round_row(self, s: desk.WinSlot*, yy: int, rel: int, row_col: uint32,
row_is_title: int, outline_col: uint32, r: int, title_alpha: int,
buf: uint32*, fw: int, fh: int, content_y0: int):
@@ -525,84 +549,110 @@ class DeskServer:
do_blit: int = 0
if rel >= TITLE_H + TITLE_GAP and rel < wh - 1 and content_row >= 0 and content_row < fh:
do_blit = 1
cyc: int = r
if rel >= r: cyc = wh - r - 1
if ww > r + r:
seg: int = 0
while seg < 2:
xs: int = wx
xe: int = wx + r
cxc: int = r
if seg == 1:
xs = wx + ww - r
xe = wx + ww
cxc = ww - r - 1
px: int = xs
while px < xe:
if px >= 0 and px < self.fb_w:
rx: int = px - wx
if _is_in_corner(rx, rel, cxc, cyc, r) != 0:
on_border: int = _is_on_border(rx, rel, ww, wh, r)
idx: int = yy * self.fb_w + px
if on_border == 0 and do_blit != 0:
self.back[idx] = buf[content_row * fw + rx]
px += 1
continue
col: uint32 = row_col
if on_border != 0:
col = outline_col
ca: int = _corner_alpha(rx, rel, cxc, cyc, r)
if on_border != 0 and ca > 0 and ca < 255:
ca = 255
if row_is_title != 0 and title_alpha < 255 and rel >= 1 and rx >= 1 and rx < ww - 1 and ca == 255 and on_border == 0:
ca = title_alpha
if ca > 0:
if ca < 255:
self.back[idx] = self._blend(self.back[idx], col, ca)
else:
self.back[idx] = col
px += 1
seg += 1
mid0: int = wx + r
mid1: int = wx + ww - r
midw: int = mid1 - mid0
if midw > 0:
if rel == 0 or rel == wh - 1:
self.row_fill(mid0, yy, midw, outline_col)
elif do_blit != 0:
self.row_blit(mid0, yy, buf, content_row * fw + r, midw)
elif row_is_title != 0 and title_alpha < 255:
self._blend_row(mid0, yy, midw, row_col, title_alpha)
else:
self.row_fill(mid0, yy, midw, row_col)
return
# 窄窗回退:整行逐像素
px: int = wx
xend: int = wx + ww
while px < xend:
if px >= 0 and px < self.fb_w:
rx: int = px - wx
in_corner: int = 1
on_border: int = 0
if rel < r:
if rx < r:
in_corner = _is_in_corner(rx, rel, r, r, r)
elif rx >= ww - r:
in_corner = _is_in_corner(rx, rel, ww - r - 1, r, r)
elif rel >= wh - r:
if rx < r:
in_corner = _is_in_corner(rx, rel, r, wh - r - 1, r)
elif rx >= ww - r:
in_corner = _is_in_corner(rx, rel, ww - r - 1, wh - r - 1, r)
if in_corner == 0:
on_border = _is_on_border(rx, rel, ww, wh, r)
if on_border == 0:
cxr: int = r
if rx >= ww - r: cxr = ww - r - 1
if _is_in_corner(rx, rel, cxr, cyc, r) != 0:
on_border: int = _is_on_border(rx, rel, ww, wh, r)
idx: int = yy * self.fb_w + px
if on_border == 0 and do_blit != 0:
self.back[idx] = buf[content_row * fw + rx]
px += 1
continue
is_border_pixel: int = 0
if on_border != 0 or _is_on_border(rx, rel, ww, wh, r) != 0:
is_border_pixel = 1
idx: int = yy * self.fb_w + px
if is_border_pixel == 0 and do_blit != 0:
self.back[idx] = buf[content_row * fw + rx]
px += 1
continue
col: uint32 = row_col
if is_border_pixel != 0:
col = outline_col
ca: int = 255
if rel < r:
if rx < r:
ca = _corner_alpha(rx, rel, r, r, r)
elif rx >= ww - r:
ca = _corner_alpha(rx, rel, ww - r - 1, r, r)
elif rel >= wh - r:
if rx < r:
ca = _corner_alpha(rx, rel, r, wh - r - 1, r)
elif rx >= ww - r:
ca = _corner_alpha(rx, rel, ww - r - 1, wh - r - 1, r)
if is_border_pixel != 0 and ca > 0 and ca < 255:
ca = 255
if row_is_title != 0 and title_alpha < 255 and rel >= 1 and rx >= 1 and rx < ww - 1 and ca == 255 and is_border_pixel == 0:
ca = title_alpha
if ca <= 0:
px += 1
continue
if ca < 255:
self.back[idx] = self._blend(self.back[idx], col, ca)
else:
self.back[idx] = col
col: uint32 = row_col
if on_border != 0:
col = outline_col
ca: int = _corner_alpha(rx, rel, cxr, cyc, r)
if on_border != 0 and ca > 0 and ca < 255:
ca = 255
if row_is_title != 0 and title_alpha < 255 and rel >= 1 and rx >= 1 and rx < ww - 1 and ca == 255 and on_border == 0:
ca = title_alpha
if ca > 0:
if ca < 255:
self.back[idx] = self._blend(self.back[idx], col, ca)
else:
self.back[idx] = col
px += 1
# ---------------- 窗口按钮(按样式逐行绘制)----------------
# 关闭键底色圆角(WIN7/WINXP 的红色圆角块)
# 圆角块的一行是否保留该列
def _round_keep(self, lx: int, lry: int, bw: int, bh: int, r: int) -> int:
if lry < r:
if lx < r: return _is_in_corner(lx, lry, r, r, r)
if lx >= bw - r: return _is_in_corner(lx, lry, bw - r - 1, r, r)
elif lry >= bh - r:
if lx < r: return _is_in_corner(lx, lry, r, bh - r - 1, r)
if lx >= bw - r: return _is_in_corner(lx, lry, bw - r - 1, bh - r - 1, r)
return 1
# 关闭键底色圆角(WIN7/WINXP 的红色圆角块):整行只做一次 row_fill
def _round_fill_row(self, bx: int, by: int, bw: int, bh: int, yy: int, col: uint32, r: int):
lry: int = yy - by
if lry < 0 or lry >= bh: return
lx: int = 0
while lx < bw:
keep: int = 1
if lry < r:
if lx < r:
keep = _is_in_corner(lx, lry, r, r, r)
elif lx >= bw - r:
keep = _is_in_corner(lx, lry, bw - r - 1, r, r)
elif lry >= bh - r:
if lx < r:
keep = _is_in_corner(lx, lry, r, bh - r - 1, r)
elif lx >= bw - r:
keep = _is_in_corner(lx, lry, bw - r - 1, bh - r - 1, r)
if keep != 0:
self.row_fill(bx + lx, yy, 1, col)
lx += 1
lo: int = 0
while lo < bw and self._round_keep(lo, lry, bw, bh, r) == 0:
lo += 1
hi: int = bw
while hi > lo and self._round_keep(hi - 1, lry, bw, bh, r) == 0:
hi -= 1
if hi > lo:
self.row_fill(bx + lo, yy, hi - lo, col)
def _draw_close_row(self, bx: int, by: int, bw: int, bh: int, yy: int, style: int):
lry: int = yy - by
@@ -767,13 +817,30 @@ class DeskServer:
# ---------------- 渲染 ----------------
def render_range(self, y0: int, y1: int):
# 窗口按 Z 序升序(选择排序,窗口数 ≤ 8)—— 背景裁剪与窗口绘制都要用
# 窗口按 Z 序升序(选择排序,窗口数 ≤ 8);顺带缓存几何/样式,避免逐行查槽
order: [desk.DESK_MAX_WIN]int
sx: [desk.DESK_MAX_WIN]int
sy: [desk.DESK_MAX_WIN]int
sw: [desk.DESK_MAX_WIN]int
sh: [desk.DESK_MAX_WIN]int
smode: [desk.DESK_MAX_WIN]int
cnt: int = 0
top_z: int = -1
for i in range(desk.DESK_MAX_WIN):
s: desk.WinSlot* = self.slot(i)
if s.used != 0 and s.active != 0 and s.minimized == 0:
order[cnt] = i
sx[i] = s.ux
sy[i] = s.uy
sw[i] = s.w
sh[i] = s.h
if s.style == STYLE_WIN7:
smode[i] = 2
elif s.style == STYLE_WIN11 or s.style == STYLE_WINXP:
smode[i] = 1
else:
smode[i] = 0
if s.z_order > top_z: top_z = s.z_order
cnt += 1
for a in range(cnt - 1):
for b in range(a + 1, cnt):
@@ -783,18 +850,26 @@ class DeskServer:
t: int = order[a]
order[a] = order[b]
order[b] = t
# 1) 背景:只铺"没有被窗口覆盖"的 x 区间。整行铺背景对窗口完全覆盖的那几百列是纯浪费
# (Scene3D 每帧刷全窗,等于每帧多拷一整屏背景)。
# 1) 背景:只铺"没有被窗口覆盖"的 x 区间;混合样式的可见行再单独铺一次底
yy: int = y0
while yy < y1:
xx: int = 0
for i in range(cnt):
s: desk.WinSlot* = self.slot(order[i])
if yy < s.uy or yy >= s.uy + s.h: continue
if s.ux > xx:
self.row_blit(xx, yy, self.bg_row, xx, s.ux - xx)
ex: int = s.ux + s.w
w: int = order[i]
if yy < sy[w] or yy >= sy[w] + sh[w]: continue
if sx[w] > xx:
self.row_blit(xx, yy, self.bg_row, xx, sx[w] - xx)
ex: int = sx[w] + sw[w]
if ex > xx: xx = ex
if smode[w] != 0:
rel: int = yy - sy[w]
need: int = 0
if smode[w] == 2:
if rel < TITLE_H or rel >= sh[w] - WIN_RADIUS: need = 1
elif rel < WIN_RADIUS or rel >= sh[w] - WIN_RADIUS:
need = 1
if need != 0:
self.row_blit(sx[w], yy, self.bg_row, sx[w], sw[w])
if xx < self.fb_w:
self.row_blit(xx, yy, self.bg_row, xx, self.fb_w - xx)
yy += 1
@@ -802,11 +877,11 @@ class DeskServer:
self._draw_taskbar_rows(y0, y1)
# 3) 窗口
for i in range(cnt):
self.render_window_rows(order[i], y0, y1)
self.render_window_rows(order[i], y0, y1, top_z)
# 4) 光标
self.render_cursor_rows(y0, y1)
def render_window_rows(self, win: int, y0: int, y1: int):
def render_window_rows(self, win: int, y0: int, y1: int, top_z: int):
s: desk.WinSlot* = self.slot(win)
wx: int = s.ux
wy: int = s.uy
@@ -814,7 +889,7 @@ class DeskServer:
th: int = TITLE_H
style: int = s.style
active: int = 0
if s.z_order == self._top_z(): active = 1
if s.z_order == top_z: active = 1
# 每样式调色板(对齐内核桌面 desktop.vp 的 _draw_app_window)
title_col: uint32 = self.rgb(uint32(50), uint32(50), uint32(90))
border_col: uint32 = self.rgb(uint32(100), uint32(100), uint32(180))