fix(macos): S-метр, линейка и зумбар больше не мылятся на Retina

Cocoa рисует канву в физических пикселях, TBitmap всегда 1x, поэтому кадр,
собранный в offscreen-битмап логического размера, растягивался вдвое.

PlatformUtils.GetScreenScale возвращает 2 на Retina и 1 на остальных
платформах. Масштаб — обычная переменная, а не условная компиляция: на
Windows/Linux умножение на 1 даёт прежние числа, а на канву кадр кладётся
через Draw, как и раньше (StretchDraw только при масштабе > 1).

В SMeterView, RulerView и PanZoomBar битмап растёт в S раз, пиксельные
константы, размеры шрифтов и толщина пера умножаются на S. Перо важно
отдельно: иначе на Retina все риски стали бы вдвое тоньше.

RulerView кэширует битмап, поэтому смена масштаба сбрасывает кэш.
PanZoomBar.ThumbGeom получил параметр AScale: отрисовка зовёт его с
физической шириной, хит-тест мыши — с логической, код мыши не менялся.

Objective-C изолирован в MacScale.pas: {$MODESWITCH OBJECTIVEC1} несовместим
с {$MODE Delphi}, а DWARF-3 (-gw3, Debug-режим lazbuild) роняет FPC 3.2.4 с
internal error 200609171 на любых Objective-C типах — отсюда {$DEBUGINFO OFF}.

Замер после правки: 37.5% CPU против 38% базовых, отрисовка этих трёх
элементов ниже уровня шума в профиле.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 19:09:53 +03:00
co-authored by Claude Opus 4.8
parent 406f32b52c
commit fd8769898a
5 changed files with 232 additions and 112 deletions
+91 -69
View File
@@ -13,7 +13,7 @@ interface
uses
Classes, SysUtils, Graphics, ExtCtrls, Controls, Math,
IntfGraphics, FPImage, LCLType, GraphType, AppTheme;
IntfGraphics, FPImage, LCLType, GraphType, AppTheme, PlatformUtils;
const
SM_CLR_METER_ON = TColor($0000CC44);
@@ -32,6 +32,9 @@ type
FPAMaxPower: Double;
FTransmitting: Boolean;
FPbSMeterRight: TPaintBox;
// Масштаб канвы: 2 на Retina, 1 на Windows/Linux. Битмап растёт во столько
// же раз, пиксельные константы и шрифты умножаются на него.
FScale: Integer;
procedure DrawSMeterWide(ACanvas: TCanvas; R: TRect;
Value, Peak, MinVal: Double;
@@ -74,6 +77,7 @@ begin
FLastSWR := 1;
FPAMaxPower := 100.0;
FTransmitting := False;
FScale := 1;
end;
destructor TSMeterView.Destroy;
@@ -112,6 +116,7 @@ var
i, X, TW: Integer; Lbl: string; SNum, Over: Integer;
LblDbm, LblS: string;
TWDbm, LblZoneL, TH10, TH6, MaxTWDbm, MaxTWS, LabelNeed: Integer;
Sc: Integer; // масштаб канвы, см. FScale
function DBtoX(dB: Double): Integer; inline;
begin
@@ -146,9 +151,10 @@ begin
CLR_SCALE_S_BLUE := FTheme.SMeterScaleSBlue;
CLR_BDR := FTheme.SMeterBdr;
Sc := FScale;
W := R.Right - R.Left; H := R.Bottom - R.Top;
ZX1 := 0; ZX2 := 0; ZY1 := 0; ZY2 := 0;
if (W < 80) or (H < 16) then Exit;
if (W < 80 * Sc) or (H < 16 * Sc) then Exit;
LblDbm := Format('%.1f', [Value]);
if Value >= -73.0 then
@@ -164,14 +170,14 @@ begin
end;
ACanvas.Font.Name := 'Courier New'; ACanvas.Font.Style := [fsBold];
ACanvas.Font.Size := 10; TWDbm := ACanvas.TextWidth(LblDbm);
ACanvas.Font.Size := 10 * Sc; TWDbm := ACanvas.TextWidth(LblDbm);
MaxTWDbm := ACanvas.TextWidth('-120.0');
MaxTWS := ACanvas.TextWidth('S9+60');
ACanvas.Font.Size := 6; ACanvas.Font.Style := [];
LabelNeed := Max(MaxTWDbm + 2 + ACanvas.TextWidth('dBm '), MaxTWS) + LABEL_PAD * 2;
LeftInfo := Max(LEFT_INFO_MIN, LabelNeed + 4);
BX := R.Left + LeftInfo; BW := W - LeftInfo - RIGHT_PAD;
if BW < 10 then Exit;
ACanvas.Font.Size := 6 * Sc; ACanvas.Font.Style := [];
LabelNeed := Max(MaxTWDbm + 2 * Sc + ACanvas.TextWidth('dBm '), MaxTWS) + LABEL_PAD * 2 * Sc;
LeftInfo := Max(LEFT_INFO_MIN * Sc, LabelNeed + 4 * Sc);
BX := R.Left + LeftInfo; BW := W - LeftInfo - RIGHT_PAD * Sc;
if BW < 10 * Sc then Exit;
LblZoneL := BX - LabelNeed;
Y_BAR_TOP := R.Top + H * 44 div 100;
@@ -180,6 +186,8 @@ begin
ACanvas.Brush.Color := CLR_SMETER_BG; ACanvas.Brush.Style := bsSolid;
ACanvas.Pen.Style := psClear; ACanvas.FillRect(R); ACanvas.Pen.Style := psSolid;
// Перо в физических пикселях: иначе на Retina все риски станут вдвое тоньше.
ACanvas.Pen.Width := Sc;
FillBar(BX, Y_BAR_TOP, S9X, Y_BAR_BOT, CLR_BAR_GREEN);
FillBar(S9X, Y_BAR_TOP, OvrX, Y_BAR_BOT, CLR_BAR_GREEN);
FillBar(OvrX, Y_BAR_TOP, BX + BW, Y_BAR_BOT, CLR_BAR_OVER);
@@ -189,65 +197,65 @@ begin
PeakRight := DBtoX(Peak);
ZX1 := Max(BX + 1, Min(PeakLeft, PeakRight));
ZX2 := Min(BX + BW - 1, Max(PeakLeft, PeakRight));
ZY1 := Y_BAR_TOP + 1; ZY2 := Y_BAR_BOT - 1;
ZY1 := Y_BAR_TOP + Sc; ZY2 := Y_BAR_BOT - Sc;
if BarEnd > BX then
begin
if BarEnd <= S9X then
FillBar(BX, Y_BAR_TOP+1, BarEnd, Y_BAR_BOT-1, CLR_BAR_GREEN)
FillBar(BX, Y_BAR_TOP+Sc, BarEnd, Y_BAR_BOT-Sc, CLR_BAR_GREEN)
else begin
FillBar(BX, Y_BAR_TOP+1, S9X, Y_BAR_BOT-1, CLR_BAR_GREEN);
FillBar(S9X, Y_BAR_TOP+1, BarEnd, Y_BAR_BOT-1, CLR_BAR_OVER);
FillBar(BX, Y_BAR_TOP+Sc, S9X, Y_BAR_BOT-Sc, CLR_BAR_GREEN);
FillBar(S9X, Y_BAR_TOP+Sc, BarEnd, Y_BAR_BOT-Sc, CLR_BAR_OVER);
end;
end;
if (PeakRight > BX) and (PeakRight <= BX + BW) then
begin
ACanvas.Pen.Color := CLR_PEAK_MARKER; ACanvas.Pen.Width := 2;
ACanvas.Pen.Color := CLR_PEAK_MARKER; ACanvas.Pen.Width := 2 * Sc;
ACanvas.MoveTo(PeakRight, Y_BAR_TOP - (Y_BAR_BOT - Y_BAR_TOP));
ACanvas.LineTo(PeakRight, Y_BAR_BOT + (Y_BAR_BOT - Y_BAR_TOP));
ACanvas.Pen.Width := 1;
ACanvas.Pen.Width := Sc;
end;
ACanvas.Brush.Style := bsClear; ACanvas.Pen.Color := CLR_BDR;
ACanvas.Rectangle(LblZoneL, Y_BAR_TOP, BX + BW, Y_BAR_BOT);
ACanvas.Pen.Color := FTheme.SMeterDivider;
ACanvas.MoveTo(BX, Y_BAR_TOP + 1); ACanvas.LineTo(BX, Y_BAR_BOT - 1);
ACanvas.MoveTo(BX, Y_BAR_TOP + Sc); ACanvas.LineTo(BX, Y_BAR_BOT - Sc);
ACanvas.Font.Size := 6; ACanvas.Font.Style := []; ACanvas.Brush.Style := bsClear;
ACanvas.Font.Size := 6 * Sc; ACanvas.Font.Style := []; ACanvas.Brush.Style := bsClear;
for i := 0 to High(DBM_MARKS) do
begin
X := DBtoX(DBM_MARKS[i]);
VLine(X, Y_BAR_TOP - TICK_LONG, Y_BAR_TOP - 1, CLR_TICK_WHITE);
VLine(X, Y_BAR_TOP - TICK_LONG * Sc, Y_BAR_TOP - Sc, CLR_TICK_WHITE);
Lbl := DBM_LABELS[i]; TW := ACanvas.TextWidth(Lbl);
ACanvas.Font.Color := CLR_SCALE_DBM;
ACanvas.TextOut(X - TW div 2, Y_BAR_TOP - TICK_LONG - ACanvas.TextHeight(Lbl) - 1, Lbl);
ACanvas.TextOut(X - TW div 2, Y_BAR_TOP - TICK_LONG * Sc - ACanvas.TextHeight(Lbl) - Sc, Lbl);
end;
i := -125;
while i < -13 do
begin
X := DBtoX(i); VLine(X, Y_BAR_TOP - TICK_MED, Y_BAR_TOP - 1, CLR_TICK_GREEN);
X := DBtoX(i); VLine(X, Y_BAR_TOP - TICK_MED * Sc, Y_BAR_TOP - Sc, CLR_TICK_GREEN);
Inc(i, 5);
end;
for i := 0 to High(S_MARKS_DBM) do
begin
X := DBtoX(S_MARKS_DBM[i]);
if S_MARKS_DBM[i] >= DB_S9 then VLine(X, Y_BAR_BOT+1, Y_BAR_BOT+TICK_LONG, CLR_TICK_BLUE)
else VLine(X, Y_BAR_BOT+1, Y_BAR_BOT+TICK_LONG, CLR_TICK_GREEN);
if S_MARKS_DBM[i] >= DB_S9 then VLine(X, Y_BAR_BOT+Sc, Y_BAR_BOT+TICK_LONG*Sc, CLR_TICK_BLUE)
else VLine(X, Y_BAR_BOT+Sc, Y_BAR_BOT+TICK_LONG*Sc, CLR_TICK_GREEN);
Lbl := S_MARKS_LBL[i]; TW := ACanvas.TextWidth(Lbl);
if S_MARKS_DBM[i] >= DB_S9 then ACanvas.Font.Color := CLR_SCALE_S_BLUE
else ACanvas.Font.Color := CLR_SCALE_S_GREEN;
ACanvas.TextOut(X - TW div 2, Y_BAR_BOT + TICK_LONG + 1, Lbl);
ACanvas.TextOut(X - TW div 2, Y_BAR_BOT + TICK_LONG * Sc + Sc, Lbl);
end;
ACanvas.Brush.Style := bsClear;
ACanvas.Font.Size := 10; ACanvas.Font.Style := [fsBold]; TH10 := ACanvas.TextHeight('0');
ACanvas.Font.Size := 6; ACanvas.Font.Style := []; TH6 := ACanvas.TextHeight('0');
ACanvas.Font.Size := 10; ACanvas.Font.Style := [fsBold]; ACanvas.Font.Color := CLR_LABEL_DBM;
ACanvas.TextOut(LblZoneL + 4, Y_BAR_TOP - TH10 - 2, LblDbm);
ACanvas.Font.Size := 6; ACanvas.Font.Style := []; ACanvas.Font.Color := CLR_TICK_BLUE;
ACanvas.TextOut(LblZoneL + 4 + TWDbm + 2, Y_BAR_TOP - TH6 - 4, 'dBm');
ACanvas.Font.Size := 10; ACanvas.Font.Style := [fsBold]; ACanvas.Font.Color := CLR_LABEL_S;
ACanvas.TextOut(LblZoneL + 4, Y_BAR_BOT + 2, LblS);
ACanvas.Font.Size := 10 * Sc; ACanvas.Font.Style := [fsBold]; TH10 := ACanvas.TextHeight('0');
ACanvas.Font.Size := 6 * Sc; ACanvas.Font.Style := []; TH6 := ACanvas.TextHeight('0');
ACanvas.Font.Size := 10 * Sc; ACanvas.Font.Style := [fsBold]; ACanvas.Font.Color := CLR_LABEL_DBM;
ACanvas.TextOut(LblZoneL + 4 * Sc, Y_BAR_TOP - TH10 - 2 * Sc, LblDbm);
ACanvas.Font.Size := 6 * Sc; ACanvas.Font.Style := []; ACanvas.Font.Color := CLR_TICK_BLUE;
ACanvas.TextOut(LblZoneL + 4 * Sc + TWDbm + 2 * Sc, Y_BAR_TOP - TH6 - 4 * Sc, 'dBm');
ACanvas.Font.Size := 10 * Sc; ACanvas.Font.Style := [fsBold]; ACanvas.Font.Color := CLR_LABEL_S;
ACanvas.TextOut(LblZoneL + 4 * Sc, Y_BAR_BOT + 2 * Sc, LblS);
end;
// ────────────────────────────────────────────────────────────────────────────
@@ -300,6 +308,7 @@ var
PBarEnd, SBarEnd, S_WarnX: Integer;
LblPwr, LblSWR: string;
TWPwr, LblZoneL, TH10, TH6, MaxTWPwr, MaxTWSWR, LabelNeed: Integer;
Sc: Integer; // масштаб канвы, см. FScale
i, X, TW: Integer;
Lbl: string;
SWRHigh: Boolean;
@@ -345,23 +354,24 @@ begin
CLR_BDR := FTheme.SMeterBdr;
CLR_SWR_HIGH := TColor($000000CC);
Sc := FScale;
W := R.Right - R.Left; H := R.Bottom - R.Top;
if (W < 80) or (H < 16) then Exit;
if (W < 80 * Sc) or (H < 16 * Sc) then Exit;
SWRHigh := FLastSWR > SWR_HIGH_THRESH;
LblPwr := Format('%.0fW', [FLastFwdW]);
LblSWR := Format('SWR:%.1f', [FLastSWR]);
ACanvas.Font.Name := 'Courier New'; ACanvas.Font.Style := [fsBold];
ACanvas.Font.Size := 10;
ACanvas.Font.Size := 10 * Sc;
TWPwr := ACanvas.TextWidth(LblPwr);
MaxTWPwr := ACanvas.TextWidth(Format('%.0fW', [FPAMaxPower]));
MaxTWSWR := ACanvas.TextWidth('SWR:10.0');
ACanvas.Font.Size := 6; ACanvas.Font.Style := [];
LabelNeed := Max(MaxTWPwr, MaxTWSWR) + LABEL_PAD * 2;
LeftInfo := Max(LEFT_INFO_MIN, LabelNeed + 4);
BX := R.Left + LeftInfo; BW := W - LeftInfo - RIGHT_PAD;
if BW < 10 then Exit;
ACanvas.Font.Size := 6 * Sc; ACanvas.Font.Style := [];
LabelNeed := Max(MaxTWPwr, MaxTWSWR) + LABEL_PAD * 2 * Sc;
LeftInfo := Max(LEFT_INFO_MIN * Sc, LabelNeed + 4 * Sc);
BX := R.Left + LeftInfo; BW := W - LeftInfo - RIGHT_PAD * Sc;
if BW < 10 * Sc then Exit;
LblZoneL := BX - LabelNeed;
Y_BAR_TOP := R.Top + H * 44 div 100;
@@ -369,14 +379,16 @@ begin
Y_BAR_MID := (Y_BAR_TOP + Y_BAR_BOT) div 2;
PY1 := Y_BAR_TOP;
PY2 := Y_BAR_MID - 1;
SY1 := Y_BAR_MID + 1;
PY2 := Y_BAR_MID - Sc;
SY1 := Y_BAR_MID + Sc;
SY2 := Y_BAR_BOT;
S_WarnX := SWRToX(SWR_HIGH_THRESH);
ACanvas.Brush.Color := CLR_SMETER_BG; ACanvas.Brush.Style := bsSolid;
ACanvas.Pen.Style := psClear; ACanvas.FillRect(R); ACanvas.Pen.Style := psSolid;
// Перо в физических пикселях, как и в DrawSMeterWide.
ACanvas.Pen.Width := Sc;
FillBar(BX, PY1, BX + BW, PY2, CLR_BAR_GREEN);
FillBar(BX, SY1, S_WarnX, SY2, CLR_BAR_GREEN);
@@ -384,16 +396,16 @@ begin
if FPAMaxPower > 0 then PwrPct := FLastFwdW / FPAMaxPower else PwrPct := 0;
PBarEnd := PwrToX(PwrPct);
FillBar(BX, PY1 + 1, PBarEnd, PY2 - 1, SM_CLR_METER_ON);
FillBar(BX, PY1 + Sc, PBarEnd, PY2 - Sc, SM_CLR_METER_ON);
SBarEnd := SWRToX(FLastSWR);
if not SWRHigh then
FillBar(BX, SY1 + 1, SBarEnd, SY2 - 1, SM_CLR_AMBER)
FillBar(BX, SY1 + Sc, SBarEnd, SY2 - Sc, SM_CLR_AMBER)
else
begin
FillBar(BX, SY1 + 1, Min(S_WarnX, SBarEnd), SY2 - 1, SM_CLR_AMBER);
FillBar(BX, SY1 + Sc, Min(S_WarnX, SBarEnd), SY2 - Sc, SM_CLR_AMBER);
if SBarEnd > S_WarnX then
FillBar(S_WarnX, SY1 + 1, SBarEnd, SY2 - 1, CLR_SWR_HIGH);
FillBar(S_WarnX, SY1 + Sc, SBarEnd, SY2 - Sc, CLR_SWR_HIGH);
end;
VLine(S_WarnX, SY1, SY2, FTheme.SMeterDivider);
@@ -405,10 +417,10 @@ begin
ACanvas.Rectangle(LblZoneL, SY1, BX + BW, SY2);
ACanvas.Pen.Color := FTheme.SMeterDivider;
ACanvas.MoveTo(BX, PY1 + 1); ACanvas.LineTo(BX, PY2 - 1);
ACanvas.MoveTo(BX, SY1 + 1); ACanvas.LineTo(BX, SY2 - 1);
ACanvas.MoveTo(BX, PY1 + Sc); ACanvas.LineTo(BX, PY2 - Sc);
ACanvas.MoveTo(BX, SY1 + Sc); ACanvas.LineTo(BX, SY2 - Sc);
ACanvas.Font.Size := 6; ACanvas.Font.Style := []; ACanvas.Brush.Style := bsClear;
ACanvas.Font.Size := 6 * Sc; ACanvas.Font.Style := []; ACanvas.Brush.Style := bsClear;
i := 5;
while i < 100 do
@@ -416,7 +428,7 @@ begin
if (i mod 25) <> 0 then
begin
X := PwrToX(i / 100.0);
VLine(X, PY1 - TICK_MED, PY1 - 1, CLR_TICK_GREEN);
VLine(X, PY1 - TICK_MED * Sc, PY1 - Sc, CLR_TICK_GREEN);
end;
Inc(i, 5);
end;
@@ -424,28 +436,28 @@ begin
for i := 0 to 4 do
begin
X := PwrToX(PWR_MARKS[i]);
VLine(X, PY1 - TICK_LONG, PY1 - 1, CLR_TICK_WHITE);
VLine(X, PY1 - TICK_LONG * Sc, PY1 - Sc, CLR_TICK_WHITE);
Lbl := Format('%.0f', [PWR_MARKS[i] * FPAMaxPower]);
TW := ACanvas.TextWidth(Lbl);
ACanvas.Font.Color := CLR_SCALE_DBM;
ACanvas.TextOut(X - TW div 2, PY1 - TICK_LONG - ACanvas.TextHeight(Lbl) - 1, Lbl);
ACanvas.TextOut(X - TW div 2, PY1 - TICK_LONG * Sc - ACanvas.TextHeight(Lbl) - Sc, Lbl);
end;
for i := 2 to 9 do
begin
if i = 5 then Continue;
X := SWRToX(i);
if i >= 3 then VLine(X, SY2 + 1, SY2 + TICK_MED, CLR_TICK_BLUE)
else VLine(X, SY2 + 1, SY2 + TICK_MED, CLR_TICK_GREEN);
if i >= 3 then VLine(X, SY2 + Sc, SY2 + TICK_MED * Sc, CLR_TICK_BLUE)
else VLine(X, SY2 + Sc, SY2 + TICK_MED * Sc, CLR_TICK_GREEN);
end;
for i := 0 to 3 do
begin
X := SWRToX(SWR_MARKS_VAL[i]);
if SWR_MARKS_VAL[i] >= SWR_HIGH_THRESH then
VLine(X, SY2 + 1, SY2 + TICK_LONG, CLR_TICK_BLUE)
VLine(X, SY2 + Sc, SY2 + TICK_LONG * Sc, CLR_TICK_BLUE)
else
VLine(X, SY2 + 1, SY2 + TICK_LONG, CLR_TICK_GREEN);
VLine(X, SY2 + Sc, SY2 + TICK_LONG * Sc, CLR_TICK_GREEN);
Lbl := SWR_MARKS_LBL[i]; TW := ACanvas.TextWidth(Lbl);
if SWR_MARKS_VAL[i] >= SWR_HIGH_THRESH then
begin
@@ -453,25 +465,25 @@ begin
else ACanvas.Font.Color := CLR_SCALE_S_BLUE;
end else
ACanvas.Font.Color := CLR_SCALE_S_GREEN;
ACanvas.TextOut(X - TW div 2, SY2 + TICK_LONG + 1, Lbl);
ACanvas.TextOut(X - TW div 2, SY2 + TICK_LONG * Sc + Sc, Lbl);
end;
ACanvas.Brush.Style := bsClear;
ACanvas.Font.Size := 10; ACanvas.Font.Style := [fsBold]; TH10 := ACanvas.TextHeight('0');
ACanvas.Font.Size := 6; ACanvas.Font.Style := []; TH6 := ACanvas.TextHeight('0');
ACanvas.Font.Size := 10 * Sc; ACanvas.Font.Style := [fsBold]; TH10 := ACanvas.TextHeight('0');
ACanvas.Font.Size := 6 * Sc; ACanvas.Font.Style := []; TH6 := ACanvas.TextHeight('0');
ACanvas.Font.Size := 10; ACanvas.Font.Style := [fsBold]; ACanvas.Font.Color := CLR_LABEL_DBM;
ACanvas.TextOut(LblZoneL + 4, PY1 - TH10 - 2, LblPwr);
ACanvas.Font.Size := 10 * Sc; ACanvas.Font.Style := [fsBold]; ACanvas.Font.Color := CLR_LABEL_DBM;
ACanvas.TextOut(LblZoneL + 4 * Sc, PY1 - TH10 - 2 * Sc, LblPwr);
ACanvas.Font.Size := 10; ACanvas.Font.Style := [fsBold];
ACanvas.Font.Size := 10 * Sc; ACanvas.Font.Style := [fsBold];
if SWRHigh then ACanvas.Font.Color := CLR_SWR_HIGH
else ACanvas.Font.Color := CLR_LABEL_S;
ACanvas.TextOut(LblZoneL + 4, SY2 + 2, LblSWR);
ACanvas.TextOut(LblZoneL + 4 * Sc, SY2 + 2 * Sc, LblSWR);
if SWRHigh then
begin
ACanvas.Font.Size := 6; ACanvas.Font.Style := [fsBold]; ACanvas.Font.Color := CLR_SWR_HIGH;
ACanvas.TextOut(LblZoneL + 4 + TWPwr + 2, PY1 - TH6 - 4, 'SWR High');
ACanvas.Font.Size := 6 * Sc; ACanvas.Font.Style := [fsBold]; ACanvas.Font.Color := CLR_SWR_HIGH;
ACanvas.TextOut(LblZoneL + 4 * Sc + TWPwr + 2 * Sc, PY1 - TH6 - 4 * Sc, 'SWR High');
end;
end;
@@ -480,28 +492,38 @@ end;
// ────────────────────────────────────────────────────────────────────────────
procedure TSMeterView.PaintSMeterRight(Sender: TObject);
var PB: TPaintBox; W, H, ZX1, ZX2, ZY1, ZY2: Integer;
var PB: TPaintBox; W, H, BmpW, BmpH, ZX1, ZX2, ZY1, ZY2: Integer;
begin
PB := TPaintBox(Sender);
W := PB.Width; H := PB.Height;
if (W <= 0) or (H <= 0) then Exit;
if (FSmBitmap = nil) or (FSmBitmap.Width <> W) or (FSmBitmap.Height <> H) then
// Битмап держим в физических пикселях канвы, иначе Cocoa растянет его вдвое
// и картинка замылится. На Windows/Linux FScale=1 и всё как было.
FScale := GetScreenScale;
BmpW := W * FScale; BmpH := H * FScale;
if (FSmBitmap = nil) or (FSmBitmap.Width <> BmpW) or (FSmBitmap.Height <> BmpH) then
begin
FreeAndNil(FSmBitmap);
FSmBitmap := TBitmap.Create;
FSmBitmap.SetSize(W, H);
FSmBitmap.SetSize(BmpW, BmpH);
end;
if FTransmitting then
begin
DrawTXMeter(FSmBitmap.Canvas, Rect(0, 0, W, H));
DrawTXMeter(FSmBitmap.Canvas, Rect(0, 0, BmpW, BmpH));
end else
begin
DrawSMeterWide(FSmBitmap.Canvas, Rect(0, 0, W, H),
DrawSMeterWide(FSmBitmap.Canvas, Rect(0, 0, BmpW, BmpH),
FLastSMeter, FSMeterPeak, FSMeterMin,
ZX1, ZX2, ZY1, ZY2);
DrawSMeterZone(FSmBitmap.Canvas, Rect(0, 0, W, H), ZX1, ZX2, ZY1, ZY2);
DrawSMeterZone(FSmBitmap.Canvas, Rect(0, 0, BmpW, BmpH), ZX1, ZX2, ZY1, ZY2);
end;
PB.Canvas.Draw(0, 0, FSmBitmap);
if FScale = 1 then
PB.Canvas.Draw(0, 0, FSmBitmap)
else
PB.Canvas.StretchDraw(Rect(0, 0, W, H), FSmBitmap);
end;
end.