diff --git a/AppTheme.pas b/AppTheme.pas index 02a8bcb..144376a 100644 --- a/AppTheme.pas +++ b/AppTheme.pas @@ -54,6 +54,12 @@ type // Спектр — сигнал и маркеры SpecLine: TColor; SpecFilter: TColor; + // Заливка полосы главного фильтра. Отдельно от SpecFilter (тот остался + // подложкой подписей AGC): полоса теперь полупрозрачная, как у слайсов, и + // цвет ей нужен СВОЙ — SpecFilter почти совпадает с нижним цветом фонового + // градиента спектра, поэтому такая заливка читалась не оттенком, а + // затемнением. + SpecFilterBand: TColor; SpecFilterEdge: TColor; SpecAgcColor: TColor; SpecAgcHangColor: TColor; @@ -158,6 +164,10 @@ begin Result.SpecGrid := TColor($00685624); Result.SpecLine := TColor($0040FF80); Result.SpecFilter := TColor($006A4E22); + // Оттенок полосы — тёплый янтарный, из семейства курсора VFO (Amber): фон + // спектра сине-стальной, поэтому янтарь на нём читается как оттенок, а не + // как «потемнее фона». RGB(255,150,40). + Result.SpecFilterBand := TColor($002896FF); Result.SpecFilterEdge := TColor($0040FFCC); Result.SpecAgcColor := TColor($0000AAFF); Result.SpecAgcHangColor := TColor($00FFCC00); @@ -266,7 +276,10 @@ begin Result.SpecLabelText := TColor($00404038); Result.SpecGrid := TColor($00B8B4A4); Result.SpecLine := TColor($00006400); // тёмно-зелёная линия - Result.SpecFilter := TColor($00C8DCC8); // светло-зелёная полоса фильтра + Result.SpecFilter := TColor($00C8DCC8); // подложка подписей AGC + // Полоса фильтра на светлой теме: насыщённее фона (тот пергаментный), иначе + // при той же полупрозрачности её не видно. RGB(60,150,60). + Result.SpecFilterBand := TColor($003C963C); Result.SpecFilterEdge := TColor($00407840); Result.SpecAgcColor := TColor($00904000); // тёмно-оранжевый AGC Result.SpecAgcHangColor := TColor($00802000); diff --git a/SpectrumView.pas b/SpectrumView.pas index 83d9264..73420f2 100644 --- a/SpectrumView.pas +++ b/SpectrumView.pas @@ -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); diff --git a/SpectrumViewOpengl.pas b/SpectrumViewOpengl.pas index 4d1417d..50104cc 100644 --- a/SpectrumViewOpengl.pas +++ b/SpectrumViewOpengl.pas @@ -622,7 +622,10 @@ begin else if X2 > X1 then DrawRect(X1, 0, X2, H, CLR_TX_BAND, 100 / 255); end else if X2 > X1 then - DrawRect(X1, 0, X2, H, FTheme.SpecFilter, 1); + // Как у слайсов: полупрозрачная заливка (раньше здесь была alpha=1, и + // главная полоса выглядела плотным блоком рядом с просвечивающими). + // Цвет — SpecFilterBand, свой у полосы (см. AppTheme). + DrawRect(X1, 0, X2, H, FTheme.SpecFilterBand, SPEC_BAND_ALPHA / 255); // Буква главного флага (A) на его полосе — только когда есть слайсы. Саму // букву рисуем ниже, но знать о ней надо здесь: под неё уходит начало @@ -669,7 +672,8 @@ begin else Clr := SliceColor(O.SliceLetter); CalcSliceBandX(O, W, SX1, SX2, SVfoX); if SX2 > SX1 then - DrawRect(SX1, 0, SX2, H, Clr, IfThen(O.TxActive, 100, 60) / 255); + DrawRect(SX1, 0, SX2, H, Clr, + IfThen(O.TxActive, SPEC_BAND_ALPHA_TX, SPEC_BAND_ALPHA) / 255); DrawLine(SX1, 0, SX1, H, Clr, 1, False); DrawLine(SX2, 0, SX2, H, Clr, 1, False); // Несущая — под буквой, если она приходится на неё (AM/FM/DSB). diff --git a/VfoOverlay.pas b/VfoOverlay.pas index 71f9ce4..7ef6f0e 100644 --- a/VfoOverlay.pas +++ b/VfoOverlay.pas @@ -38,7 +38,11 @@ const // бейджа-карточки во флаге и для буквы на полосе фильтра в спектре. SLICE_COLORS: array[0..7] of TColor = ( TColor($0060E040), // A — зелёный - TColor($0010A8FF), // B — оранжевый + // B был оранжевым и спорил с янтарной полосой главного фильтра + // (AppTheme.SpecFilterBand). Васильковый выбран по свободному месту в круге + // оттенков: занято 0°(TX)/31°(полоса)/38/55/132/190/195/276/308, самый + // широкий незанятый промежуток — 195..276°, середина ≈236°. RGB(97,118,255). + TColor($00FF7661), // B — васильковый (indigo) TColor($00F0D030), // C — голубой TColor($00E070F0), // D — розовый/маджента TColor($0030E0F0), // E — жёлтый @@ -46,6 +50,11 @@ const TColor($00E0C060), // G — бирюзовый TColor($00C0C0C0)); // H — серый + // Прозрачность заливки полосы фильтра на спектре — одна на главный VFO и на + // слайсы, чтобы полосы читались одинаково (и в CPU-, и в GL-вьюхе). + SPEC_BAND_ALPHA = 60; // приём + SPEC_BAND_ALPHA_TX = 100; // передающая полоса — плотнее + // Цвет слайса по его букве (A..). За пределами таблицы — по кругу. function SliceColor(L: Char): TColor;