ui(spectrum): полоса главного фильтра — как у слайсов, со своим оттенком

Главный фильтр рисовался иначе, чем слайсовые полосы, причём в CPU-пути
сильнее, чем в GL.

1. Порядок отрисовки. Кромки, несущая и её треугольник у главного шли ПОСЛЕ
   кривой спектра, у слайсов — до неё, а в GL-вьюхе весь блок фильтров идёт
   перед DrawSpectrumCurve. Из-за этого на CPU главная полоса единственная
   лезла поверх сигнала и выглядела жирнее и ярче слайсовых при тех же цвете и
   толщине линий. Блок перенесён к слайсам, до кривой: порядок кадра теперь
   одинаков у обоих путей.

2. Заливка. Была НЕПРОЗРАЧНОЙ, рядом с просвечивающими слайсовыми смотрелась
   плотным блоком. Теперь та же полупрозрачность, что у слайсов; прозрачность
   вынесена в общие SPEC_BAND_ALPHA/SPEC_BAND_ALPHA_TX (VfoOverlay, рядом с
   палитрой слайсов) — раньше это были четыре магических числа по двум вьюхам.
   Осиротевшая FillBandRaw удалена.

3. Цвет. С полупрозрачностью полоса перестала читаться: SpecFilter (34,78,106)
   почти совпадает с нижним цветом фонового градиента спектра (31,79,108), и
   заливка давала не оттенок, а затемнение — над низом градиента выходило
   ровно (32,79,108), то есть фон. Полосе дан свой цвет темы SpecFilterBand:
   тёплый янтарь RGB(255,150,40) на тёмной (фон сине-стальной, тёплое на нём
   читается) и насыщённый зелёный RGB(60,150,60) на светлой. SpecFilter
   остался подложкой подписей AGC — иначе перекрасились бы и они.

4. Слайс B перекрашен из оранжевого в васильковый RGB(97,118,255): с янтарной
   полосой главного фильтра оранжевый стал неразличим. Оттенок выбран по
   свободному месту в круге: заняты 0°(TX), 31°(полоса), 55°(E), 132°(A),
   190°(C), 195°(G), 276°(F), 308°(D); самый широкий промежуток 195..276°,
   середина ≈236°. Палитра одна на всё, поэтому вместе с полосой и несущей
   перекрашиваются буква слайса на спектре и бейдж во флаге VfoOverlay.

Сборка ewsdr (--ws=qt6) и ewsdrd — ОК. На экране не проверено: нужен эфир со
слайсами; цвета правятся одной строкой (SpecFilterBand в AppTheme,
SLICE_COLORS в VfoOverlay).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-14 23:14:59 +03:00
co-authored by Claude Opus 5
parent d0c82ade73
commit eb4af61fad
4 changed files with 61 additions and 61 deletions
+31 -57
View File
@@ -185,7 +185,6 @@ type
procedure DrawIMDMarkersRaw(W, H: Integer; DBmax, InvRange: Double);
procedure RawCircle(CX, CY, R: Integer; Color: TColor);
procedure BlendBand(X1, X2, H: Integer; R, G, B, Alpha: Byte);
procedure FillBandRaw(X1, X2, H: Integer; Color: TColor);
procedure CopyGridToSpectrum(W, H: Integer);
procedure DrawADCOverloadRaw(W, H: Integer);
procedure CalcFilterBandX(VfoFreq: Double; W: Integer;
@@ -1168,41 +1167,6 @@ end;
// Canvas.FillRect для полосы фильтра в DrawSpectrum: полоса рисуется в
// raw-фазе (между memcpy сетки и градиентом), а FillRect дёргал бы Canvas
// и форсил лишнюю синхронизацию битмапа на Qt6.
procedure TSpectrumView.FillBandRaw(X1, X2, H: Integer; Color: TColor);
var
Y, W: Integer;
Row: PLongWord;
Px: LongWord;
R, G, B: Byte;
begin
if FSpectrumBitmap = nil then Exit;
W := FSpectrumBitmap.Width;
if X1 < 0 then X1 := 0;
if X2 > W then X2 := W;
if X2 <= X1 then Exit;
R := Color and $FF; G := (Color shr 8) and $FF; B := (Color shr 16) and $FF;
{$IFDEF DARWIN}
// ARGB в памяти (см. BlendBand): байт 0 = альфа, дальше R,G,B.
Px := LongWord($FF) or (LongWord(R) shl 8) or (LongWord(G) shl 16) or
(LongWord(B) shl 24);
{$ELSE}
// BGRA в памяти: байты B,G,R,A.
Px := LongWord(B) or (LongWord(G) shl 8) or (LongWord(R) shl 16) or $FF000000;
{$ENDIF}
FSpectrumBitmap.BeginUpdate(False);
try
for Y := 0 to H - 1 do
begin
Row := PLongWord(FSpectrumBitmap.ScanLine[Y]);
if Row = nil then Continue;
Inc(Row, X1);
FillDWord(Row^, X2 - X1, Px);
end;
finally
FSpectrumBitmap.EndUpdate(False);
end;
end;
// Маркер фильтра каждого слайса (B+) на спектре — полупрозрачная полоса
// пропускания + края + центральная несущая. Янтарный цвет, как в GL-вьюхе
// (SpectrumViewOpengl.DrawSliceFilterMarkers). Полосы (raw ScanLine-блендинг)
@@ -1224,7 +1188,7 @@ begin
CalcSliceBandX(O, W, SX1, SX2, SVfoX);
if SX2 > SX1 then
BlendBand(SX1, SX2, H, Clr and $FF, (Clr shr 8) and $FF, (Clr shr 16) and $FF,
IfThen(O.TxActive, 100, 60));
IfThen(O.TxActive, SPEC_BAND_ALPHA_TX, SPEC_BAND_ALPHA));
end;
end;
@@ -1789,7 +1753,14 @@ begin
end else
if X2 > X1 then BlendBand(X1, X2, H, $E0, $30, $30, 100);
end else
if X2 > X1 then FillBandRaw(X1, X2, H, FTheme.SpecFilter);
// Полупрозрачная заливка тем же способом, что у слайсов (SPEC_BAND_ALPHA):
// раньше главная полоса заливалась НЕПРОЗРАЧНО и выглядела плотным блоком
// рядом с просвечивающими слайсовыми. Цвет — SpecFilterBand (свой, не
// подложка подписей AGC): см. комментарий в AppTheme.
if X2 > X1 then
BlendBand(X1, X2, H, FTheme.SpecFilterBand and $FF,
(FTheme.SpecFilterBand shr 8) and $FF,
(FTheme.SpecFilterBand shr 16) and $FF, SPEC_BAND_ALPHA);
// Полосы фильтра слайсов (B+) — под градиентом/кривой, как в GL-вьюхе.
DrawSliceFilterBands(W, H);
@@ -1800,16 +1771,34 @@ begin
DrawSpectrumGradient(FSpPts, W, H);
// ★ Кромки, несущая и буква ГЛАВНОГО фильтра — здесь, ДО кривой спектра,
// ровно как у слайсов ниже и как в GL-вьюхе (там весь блок фильтров идёт
// перед DrawSpectrumCurve). Раньше этот кусок стоял ПОСЛЕ кривой, и на
// CPU-пути главный фильтр единственный лез поверх сигнала — с виду толще и
// ярче слайсовых, хотя цвета и толщина те же.
RawVLine(X1, 0, H - 1, FTheme.SpecFilterEdge);
RawVLine(X2, 0, H - 1, FTheme.SpecFilterEdge);
// Буква главного флага (A) на его полосе — только когда есть слайсы,
// иначе одиночный приём не засоряем. Букву запоминаем: под неё ниже
// иначе одиночный приём не засоряем. Букву запоминаем: под неё
// подстраиваются вертикаль несущей и её треугольник.
MainLetter := #0;
if (FSliceOverlays <> nil) and (FSliceOverlays.Count > 0) and
(FActiveVfo = 0) and Assigned(FVfoOverlay) then
begin
MainLetter := FVfoOverlay.SliceLetter;
DrawBandLetterRaw(X1, X2, MainLetter, SliceColor(MainLetter));
CarrY := CarrierTopY(X1, X2, VfoX, MainLetter);
RawVLine(VfoX, CarrY, H - 13, FTheme.SpecVfoCursor, 2);
RawTriangleDown(VfoX, CarrY, 5, 8, FTheme.SpecVfoCursor);
if FTXOverlay and (FTXVfoIndex <> FActiveVfo) then
begin
RawVLine(TXX1, 0, H - 1, TColor($002030E0));
RawVLine(TXX2, 0, H - 1, TColor($002030E0));
// Буква нарисована на RX-полосе (X1..X2) — с ней и сверяемся.
CarrY := CarrierTopY(X1, X2, TXVfoX, MainLetter);
RawVLine(TXVfoX, CarrY, H - 13, TColor($002030E0), 2);
RawTriangleDown(TXVfoX, CarrY, 5, 8, TColor($002030E0));
end;
if MainLetter <> #0 then
DrawBandLetterRaw(X1, X2, MainLetter, SliceColor(MainLetter));
// Края/несущие/буквы слайсов (полосы уже нарисованы выше).
DrawSliceFilterLinesRaw(W, H);
@@ -1845,22 +1834,7 @@ begin
// Линия спектра
RawCurve(W, FTheme.SpecLine);
// Края фильтра + VFO
RawVLine(X1, 0, H - 1, FTheme.SpecFilterEdge);
RawVLine(X2, 0, H - 1, FTheme.SpecFilterEdge);
// Несущая и её треугольник — под буквой полосы, если она там есть.
CarrY := CarrierTopY(X1, X2, VfoX, MainLetter);
RawVLine(VfoX, CarrY, H - 13, FTheme.SpecVfoCursor, 2);
RawTriangleDown(VfoX, CarrY, 5, 8, FTheme.SpecVfoCursor);
if FTXOverlay and (FTXVfoIndex <> FActiveVfo) then
begin
RawVLine(TXX1, 0, H - 1, TColor($002030E0));
RawVLine(TXX2, 0, H - 1, TColor($002030E0));
// Буква нарисована на RX-полосе (X1..X2) — с ней и сверяемся.
CarrY := CarrierTopY(X1, X2, TXVfoX, MainLetter);
RawVLine(TXVfoX, CarrY, H - 13, TColor($002030E0), 2);
RawTriangleDown(TXVfoX, CarrY, 5, 8, TColor($002030E0));
end;
// (кромки/несущая главного фильтра нарисованы выше — до кривой)
if FMarkerActive then DrawMarkerLineRaw(W, H);
if FBeaconMarkActive or FBeaconDecActive then DrawBeaconMarkersRaw(W, H);