mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
ui: плашка ADC OVERLOAD — общий образ CPU/GL без пер-кадрового жора
AlertOverlay переписан в общий builder: BuildAlertImage строит RGBA-образ плашки (попиксельная альфа, Canvas один раз на своём битмапе). GL грузит образ текстурой (кэш как был), CPU блендит кэш в raw-кадр — убраны canvas-путь в кадре и полный проход восстановления альфы битмапа. GL-плашка теперь 1:1 с CPU (было: упрощённый бокс по центру), позиция — правый верхний угол в обоих рендерах. Удалена мёртвая подсветочная линия (перекрывалась рамкой). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+161
-84
@@ -1,7 +1,15 @@
|
||||
unit AlertOverlay;
|
||||
|
||||
{
|
||||
Reusable overlays for spectrum-like bitmap surfaces.
|
||||
Общая плашка-алерт для CPU- и GL-рендеров спектра.
|
||||
|
||||
Единственный источник внешнего вида: BuildAlertImage строит готовый
|
||||
RGBA-образ (straight alpha, попиксельная) ОДИН раз — Canvas используется
|
||||
только здесь, на собственном маленьком битмапе. Дальше каждый рендер
|
||||
использует образ по-своему без пер-кадрового жора CPU:
|
||||
- GL: загружает как текстуру (один раз) и рисует квадом;
|
||||
- CPU: блендит кэшированный образ в raw-кадр (BlendAlertImage,
|
||||
~10 тыс. пикселей — без Canvas и синков режима доступа Qt6).
|
||||
}
|
||||
|
||||
{$IFDEF FPC}
|
||||
@@ -11,105 +19,174 @@ unit AlertOverlay;
|
||||
interface
|
||||
|
||||
uses
|
||||
Graphics, Types, Math;
|
||||
Classes, Graphics, Types, Math;
|
||||
|
||||
procedure DrawAlertOverlay(Target: TBitmap; C: TCanvas; W, H: Integer;
|
||||
const Title, Detail: string);
|
||||
const
|
||||
ALERT_PAD = 12; // отступ плашки от правого/верхнего края спектра
|
||||
|
||||
type
|
||||
TAlertImage = record
|
||||
W, H: Integer;
|
||||
RGBA: array of Byte; // W*H*4, порядок R,G,B,A; straight alpha
|
||||
end;
|
||||
|
||||
// Построить образ плашки (полупрозрачный красный фон, рамка, акцент-полоса,
|
||||
// заголовок + деталь). Вызывать один раз и кэшировать результат.
|
||||
procedure BuildAlertImage(const Title, Detail: string; out Img: TAlertImage);
|
||||
|
||||
// Смешать образ в raw-битмап кадра (32bpp, BGRA / ARGB на Darwin) по
|
||||
// straight-alpha. (X,Y) — левый верхний угол плашки; клиппинг внутри.
|
||||
procedure BlendAlertImage(const Img: TAlertImage; Base: PByte;
|
||||
BPL, RawW, RawH, X, Y: Integer);
|
||||
|
||||
implementation
|
||||
|
||||
procedure BlendRect(Target: TBitmap; const Rct: TRect; R, G, B, Alpha: Byte);
|
||||
procedure BuildAlertImage(const Title, Detail: string; out Img: TAlertImage);
|
||||
const
|
||||
INNER_X = 12; ACCENT_W = 4; BOX_H = 42;
|
||||
var
|
||||
Y, X, InvA: Integer;
|
||||
Row: PByte;
|
||||
RR: TRect;
|
||||
begin
|
||||
if (Target = nil) or (Alpha = 0) then Exit;
|
||||
RR := Rct;
|
||||
if RR.Left < 0 then RR.Left := 0;
|
||||
if RR.Top < 0 then RR.Top := 0;
|
||||
if RR.Right > Target.Width then RR.Right := Target.Width;
|
||||
if RR.Bottom > Target.Height then RR.Bottom := Target.Height;
|
||||
if (RR.Right <= RR.Left) or (RR.Bottom <= RR.Top) then Exit;
|
||||
B: TBitmap;
|
||||
TitleW, DetailW, BoxW: Integer;
|
||||
|
||||
InvA := 255 - Alpha;
|
||||
Target.BeginUpdate(False);
|
||||
try
|
||||
for Y := RR.Top to RR.Bottom - 1 do
|
||||
procedure FillBuf(X1, Y1, X2, Y2: Integer; R, G, Bl, A: Byte); // [X1..X2)
|
||||
var X, Y, I: Integer;
|
||||
begin
|
||||
for Y := Max(0, Y1) to Min(BOX_H, Y2) - 1 do
|
||||
begin
|
||||
Row := PByte(Target.ScanLine[Y]);
|
||||
if Row = nil then Continue;
|
||||
Inc(Row, RR.Left * 4);
|
||||
for X := RR.Left to RR.Right - 1 do
|
||||
I := (Y * BoxW + Max(0, X1)) * 4;
|
||||
for X := Max(0, X1) to Min(BoxW, X2) - 1 do
|
||||
begin
|
||||
{$IFDEF DARWIN}
|
||||
Row[1] := Byte((Alpha * R + InvA * Row[1]) div 255);
|
||||
Row[2] := Byte((Alpha * G + InvA * Row[2]) div 255);
|
||||
Row[3] := Byte((Alpha * B + InvA * Row[3]) div 255);
|
||||
{$ELSE}
|
||||
Row[0] := Byte((Alpha * B + InvA * Row[0]) div 255);
|
||||
Row[1] := Byte((Alpha * G + InvA * Row[1]) div 255);
|
||||
Row[2] := Byte((Alpha * R + InvA * Row[2]) div 255);
|
||||
{$ENDIF}
|
||||
Inc(Row, 4);
|
||||
Img.RGBA[I] := R; Img.RGBA[I+1] := G;
|
||||
Img.RGBA[I+2] := Bl; Img.RGBA[I+3] := A;
|
||||
Inc(I, 4);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure BlendText(TxtB: TBitmap);
|
||||
// Композит текстового слоя (белым на чёрном) в образ: покрытие = max(R,G,B),
|
||||
// цвет по строке (заголовок/деталь), сложение по straight-alpha.
|
||||
var
|
||||
X, Y, I, MaxC: Integer;
|
||||
Src: PByte;
|
||||
CR, CG, CB: Byte;
|
||||
AT, AB, AO: Double;
|
||||
begin
|
||||
TxtB.BeginUpdate(False);
|
||||
try
|
||||
for Y := 0 to BOX_H - 1 do
|
||||
begin
|
||||
Src := PByte(TxtB.ScanLine[Y]);
|
||||
I := Y * BoxW * 4;
|
||||
for X := 0 to BoxW - 1 do
|
||||
begin
|
||||
{$IFDEF DARWIN}
|
||||
MaxC := Max(Src[1], Max(Src[2], Src[3]));
|
||||
{$ELSE}
|
||||
MaxC := Max(Src[0], Max(Src[1], Src[2]));
|
||||
{$ENDIF}
|
||||
if MaxC > 0 then
|
||||
begin
|
||||
if Y < 24 then begin CR := $FF; CG := $88; CB := $88; end // $008888FF
|
||||
else begin CR := $E8; CG := $C8; CB := $C8; end; // $00C8C8E8
|
||||
AT := MaxC / 255.0;
|
||||
AB := Img.RGBA[I+3] / 255.0 * (1.0 - AT);
|
||||
AO := AT + AB;
|
||||
Img.RGBA[I] := Round((CR * AT + Img.RGBA[I] * AB) / AO);
|
||||
Img.RGBA[I+1] := Round((CG * AT + Img.RGBA[I+1] * AB) / AO);
|
||||
Img.RGBA[I+2] := Round((CB * AT + Img.RGBA[I+2] * AB) / AO);
|
||||
Img.RGBA[I+3] := Round(AO * 255.0);
|
||||
end;
|
||||
Inc(Src, 4);
|
||||
Inc(I, 4);
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
TxtB.EndUpdate(False);
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
Img.W := 0; Img.H := 0; Img.RGBA := nil;
|
||||
B := TBitmap.Create;
|
||||
try
|
||||
B.PixelFormat := pf32bit;
|
||||
B.SetSize(8, 8);
|
||||
B.Canvas.Font.Name := 'Courier New';
|
||||
B.Canvas.Font.Style := [fsBold];
|
||||
B.Canvas.Font.Size := 9;
|
||||
TitleW := B.Canvas.TextWidth(Title);
|
||||
B.Canvas.Font.Style := [];
|
||||
B.Canvas.Font.Size := 7;
|
||||
DetailW := B.Canvas.TextWidth(Detail);
|
||||
BoxW := Max(TitleW, DetailW) + INNER_X * 2 + ACCENT_W + 8;
|
||||
|
||||
SetLength(Img.RGBA, BoxW * BOX_H * 4);
|
||||
Img.W := BoxW;
|
||||
Img.H := BOX_H;
|
||||
FillBuf(0, 0, BoxW, BOX_H, $A8, $10, $16, 118); // фон
|
||||
FillBuf(0, 0, BoxW, 1, $C8, $38, $38, 255); // рамка
|
||||
FillBuf(0, BOX_H - 1, BoxW, BOX_H, $C8, $38, $38, 255);
|
||||
FillBuf(0, 0, 1, BOX_H, $C8, $38, $38, 255);
|
||||
FillBuf(BoxW - 1, 0, BoxW, BOX_H, $C8, $38, $38, 255);
|
||||
FillBuf(7, 7, 7 + ACCENT_W, BOX_H - 7, $F0, $28, $28, 255); // акцент
|
||||
|
||||
B.SetSize(BoxW, BOX_H);
|
||||
B.Canvas.Brush.Color := clBlack;
|
||||
B.Canvas.Brush.Style := bsSolid;
|
||||
B.Canvas.FillRect(Rect(0, 0, BoxW, BOX_H));
|
||||
B.Canvas.Brush.Style := bsClear;
|
||||
B.Canvas.Font.Name := 'Courier New';
|
||||
B.Canvas.Font.Color := clWhite;
|
||||
B.Canvas.Font.Style := [fsBold];
|
||||
B.Canvas.Font.Size := 9;
|
||||
B.Canvas.TextOut(INNER_X + ACCENT_W + 8, 7, Title);
|
||||
B.Canvas.Font.Style := [];
|
||||
B.Canvas.Font.Size := 7;
|
||||
B.Canvas.TextOut(INNER_X + ACCENT_W + 8, 25, Detail);
|
||||
BlendText(B);
|
||||
finally
|
||||
Target.EndUpdate(False);
|
||||
B.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure DrawAlertOverlay(Target: TBitmap; C: TCanvas; W, H: Integer;
|
||||
const Title, Detail: string);
|
||||
const
|
||||
PAD = 12;
|
||||
INNER_X = 12;
|
||||
ACCENT_W = 4;
|
||||
procedure BlendAlertImage(const Img: TAlertImage; Base: PByte;
|
||||
BPL, RawW, RawH, X, Y: Integer);
|
||||
var
|
||||
R, AccentR: TRect;
|
||||
TW, DW, BoxW, BoxH: Integer;
|
||||
SX, SY, DX, DY, I, A, InvA: Integer;
|
||||
P: PByte;
|
||||
begin
|
||||
if (Target = nil) or (C = nil) then Exit;
|
||||
if (W < 220) or (H < 60) then Exit;
|
||||
|
||||
C.Font.Name := 'Courier New';
|
||||
C.Font.Style := [fsBold];
|
||||
C.Font.Size := 9;
|
||||
TW := C.TextWidth(Title);
|
||||
C.Font.Style := [];
|
||||
C.Font.Size := 7;
|
||||
DW := C.TextWidth(Detail);
|
||||
|
||||
BoxW := Max(TW, DW) + INNER_X * 2 + ACCENT_W + 8;
|
||||
BoxH := 42;
|
||||
R := Rect(W - PAD - BoxW, PAD, W - PAD, PAD + BoxH);
|
||||
|
||||
BlendRect(Target, R, $A8, $10, $16, 118);
|
||||
BlendRect(Target, Rect(R.Left, R.Top, R.Right, R.Top + 1), $FF, $68, $68, 80);
|
||||
|
||||
C.Brush.Style := bsClear;
|
||||
C.Pen.Style := psSolid;
|
||||
C.Pen.Width := 1;
|
||||
C.Pen.Color := TColor($003838C8);
|
||||
C.Rectangle(R);
|
||||
|
||||
AccentR := Rect(R.Left + 7, R.Top + 7, R.Left + 7 + ACCENT_W, R.Bottom - 7);
|
||||
C.Brush.Style := bsSolid;
|
||||
C.Brush.Color := TColor($002828F0);
|
||||
C.Pen.Style := psClear;
|
||||
C.FillRect(AccentR);
|
||||
|
||||
C.Brush.Style := bsClear;
|
||||
C.Font.Name := 'Courier New';
|
||||
C.Font.Style := [fsBold];
|
||||
C.Font.Size := 9;
|
||||
C.Font.Color := TColor($008888FF);
|
||||
C.TextOut(R.Left + INNER_X + ACCENT_W + 8, R.Top + 7, Title);
|
||||
|
||||
C.Font.Style := [];
|
||||
C.Font.Size := 7;
|
||||
C.Font.Color := TColor($00C8C8E8);
|
||||
C.TextOut(R.Left + INNER_X + ACCENT_W + 8, R.Top + 25, Detail);
|
||||
if (Img.W <= 0) or (Base = nil) then Exit;
|
||||
for SY := 0 to Img.H - 1 do
|
||||
begin
|
||||
DY := Y + SY;
|
||||
if (DY < 0) or (DY >= RawH) then Continue;
|
||||
I := SY * Img.W * 4;
|
||||
for SX := 0 to Img.W - 1 do
|
||||
begin
|
||||
DX := X + SX;
|
||||
A := Img.RGBA[I + 3];
|
||||
if (DX >= 0) and (DX < RawW) and (A > 0) then
|
||||
begin
|
||||
InvA := 255 - A;
|
||||
P := Base + DY * BPL + DX * 4;
|
||||
{$IFDEF DARWIN}
|
||||
// ARGB: byte0=A, byte1=R, byte2=G, byte3=B
|
||||
P[1] := Byte((Img.RGBA[I] * A + P[1] * InvA) div 255);
|
||||
P[2] := Byte((Img.RGBA[I+1] * A + P[2] * InvA) div 255);
|
||||
P[3] := Byte((Img.RGBA[I+2] * A + P[3] * InvA) div 255);
|
||||
P[0] := $FF;
|
||||
{$ELSE}
|
||||
// BGRA: byte0=B, byte1=G, byte2=R
|
||||
P[0] := Byte((Img.RGBA[I+2] * A + P[0] * InvA) div 255);
|
||||
P[1] := Byte((Img.RGBA[I+1] * A + P[1] * InvA) div 255);
|
||||
P[2] := Byte((Img.RGBA[I] * A + P[2] * InvA) div 255);
|
||||
P[3] := $FF;
|
||||
{$ENDIF}
|
||||
end;
|
||||
Inc(I, 4);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user