unit SMeterView; { SMeterView.pas — рендеринг S-метра, TX-метра и индикаторов мощности/SWR. TSMeterView — изолированный класс без зависимостей на SpectrumView. } {$IFDEF FPC} {$MODE Delphi} {$ENDIF} interface uses Classes, SysUtils, Graphics, ExtCtrls, Controls, Math, IntfGraphics, FPImage, LCLType, GraphType, AppTheme; const SM_CLR_METER_ON = TColor($0000CC44); SM_CLR_AMBER = TColor($0000AAFF); type TSMeterView = class private FSmBitmap: TBitmap; FTheme: TAppTheme; FLastSMeter: Double; FSMeterPeak: Double; FSMeterMin: Double; FLastFwdW: Double; FLastSWR: Double; FPAMaxPower: Double; FTransmitting: Boolean; FPbSMeterRight: TPaintBox; procedure DrawSMeterWide(ACanvas: TCanvas; R: TRect; Value, Peak, MinVal: Double; out ZX1, ZX2, ZY1, ZY2: Integer); procedure DrawSMeterZone(ACanvas: TCanvas; R: TRect; X1, X2, Y1, Y2: Integer); procedure DrawTXMeter(ACanvas: TCanvas; R: TRect); public constructor Create; destructor Destroy; override; property LastSMeter: Double read FLastSMeter write FLastSMeter; property SMeterPeak: Double read FSMeterPeak write FSMeterPeak; property SMeterMin: Double read FSMeterMin write FSMeterMin; property LastFwdW: Double read FLastFwdW write FLastFwdW; property LastSWR: Double read FLastSWR write FLastSWR; property PAMaxPower: Double read FPAMaxPower write FPAMaxPower; property Transmitting: Boolean read FTransmitting write FTransmitting; property PbSMeterRight: TPaintBox write FPbSMeterRight; procedure PaintSMeterRight(Sender: TObject); procedure SetTheme(const T: TAppTheme); end; implementation // ════════════════════════════════════════════════════════════════════════════ // TSMeterView // ════════════════════════════════════════════════════════════════════════════ constructor TSMeterView.Create; begin inherited Create; FSmBitmap := nil; FTheme := DarkTheme; FLastSMeter := -130; FSMeterPeak := -130; FSMeterMin := -130; FLastFwdW := 0; FLastSWR := 1; FPAMaxPower := 100.0; FTransmitting := False; end; destructor TSMeterView.Destroy; begin FreeAndNil(FSmBitmap); inherited; end; procedure TSMeterView.SetTheme(const T: TAppTheme); begin FTheme := T; end; // ──────────────────────────────────────────────────────────────────────────── // DrawSMeterWide // ──────────────────────────────────────────────────────────────────────────── procedure TSMeterView.DrawSMeterWide(ACanvas: TCanvas; R: TRect; Value, Peak, MinVal: Double; out ZX1, ZX2, ZY1, ZY2: Integer); const DB_MIN = -127.0; DB_MAX = -13.0; DB_S9 = -73.0; DB_OVR = -43.0; DBM_MARKS: array[0..5] of Double = (-120,-100,-80,-60,-40,-20); DBM_LABELS: array[0..5] of string = ('-120','-100','-80','-60','-40','-20'); S_MARKS_DBM: array[0..7] of Double = (-121,-109,-97,-85,-73,-53,-33,-13); S_MARKS_LBL: array[0..7] of string = ('S1','S3','S5','S7','S9','+20','+40','+60'); LEFT_INFO_MIN = 62; RIGHT_PAD = 8; LABEL_PAD = 4; TICK_LONG = 5; TICK_MED = 3; var CLR_SMETER_BG, CLR_BAR_GREEN, CLR_BAR_OVER: TColor; CLR_PEAK_MARKER, CLR_TICK_GREEN, CLR_TICK_WHITE, CLR_TICK_BLUE: TColor; CLR_LABEL_DBM, CLR_LABEL_S, CLR_SCALE_DBM: TColor; CLR_SCALE_S_GREEN, CLR_SCALE_S_BLUE, CLR_BDR: TColor; W, H, BX, BW, LeftInfo: Integer; Y_BAR_TOP, Y_BAR_BOT: Integer; BarEnd, PeakLeft, PeakRight, S9X, OvrX: Integer; i, X, TW: Integer; Lbl: string; SNum, Over: Integer; LblDbm, LblS: string; TWDbm, LblZoneL, TH10, TH6, MaxTWDbm, MaxTWS, LabelNeed: Integer; function DBtoX(dB: Double): Integer; inline; begin Result := BX + Round((dB - DB_MIN) / (DB_MAX - DB_MIN) * BW); if Result < BX then Result := BX; if Result > BX + BW then Result := BX + BW; end; procedure VLine(X2, Y1, Y2: Integer; C: TColor); begin ACanvas.Pen.Color := C; ACanvas.MoveTo(X2, Y1); ACanvas.LineTo(X2, Y2); end; procedure FillBar(X1, Y1, X2, Y2: Integer; C: TColor); begin ACanvas.Brush.Color := C; ACanvas.Brush.Style := bsSolid; ACanvas.Pen.Style := psClear; if X2 > X1 then ACanvas.FillRect(Rect(X1, Y1, X2, Y2)); ACanvas.Pen.Style := psSolid; end; begin CLR_SMETER_BG := FTheme.SMeterBG; CLR_BAR_GREEN := FTheme.SMeterBarGreen; CLR_BAR_OVER := FTheme.SMeterBarOver; CLR_PEAK_MARKER := FTheme.SMeterPeakMarker; CLR_TICK_GREEN := FTheme.SMeterTickGreen; CLR_TICK_WHITE := FTheme.SMeterTickWhite; CLR_TICK_BLUE := FTheme.SMeterTickBlue; CLR_LABEL_DBM := FTheme.SMeterLabelDbm; CLR_LABEL_S := FTheme.SMeterLabelS; CLR_SCALE_DBM := FTheme.SMeterScaleDbm; CLR_SCALE_S_GREEN:= FTheme.SMeterScaleSGreen; CLR_SCALE_S_BLUE := FTheme.SMeterScaleSBlue; CLR_BDR := FTheme.SMeterBdr; 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; LblDbm := Format('%.1f', [Value]); if Value >= -73.0 then begin Over := Round(Value - (-73.0)); if Over < 5 then LblS := 'S9' else begin Over := ((Over + 5) div 10) * 10; if Over = 0 then Over := 10; LblS := Format('S9+%d', [Over]); end; end else if Value <= -121.0 then LblS := 'S1' else begin LblS := 'S1'; for SNum := 1 to 8 do if Value >= (-121.0 + (SNum - 1) * 6.0) then LblS := 'S' + IntToStr(SNum); end; ACanvas.Font.Name := 'Courier New'; ACanvas.Font.Style := [fsBold]; ACanvas.Font.Size := 10; 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; LblZoneL := BX - LabelNeed; Y_BAR_TOP := R.Top + H * 44 div 100; Y_BAR_BOT := R.Top + H * 58 div 100; S9X := DBtoX(DB_S9); OvrX := DBtoX(DB_OVR); ACanvas.Brush.Color := CLR_SMETER_BG; ACanvas.Brush.Style := bsSolid; ACanvas.Pen.Style := psClear; ACanvas.FillRect(R); ACanvas.Pen.Style := psSolid; 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); BarEnd := DBtoX(Value); PeakLeft := DBtoX(MinVal); 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; if BarEnd > BX then begin if BarEnd <= S9X then FillBar(BX, Y_BAR_TOP+1, BarEnd, Y_BAR_BOT-1, 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); end; end; if (PeakRight > BX) and (PeakRight <= BX + BW) then begin ACanvas.Pen.Color := CLR_PEAK_MARKER; ACanvas.Pen.Width := 2; 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; 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.Font.Size := 6; 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); 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); 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); 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); 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); 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); end; // ──────────────────────────────────────────────────────────────────────────── // DrawSMeterZone // ──────────────────────────────────────────────────────────────────────────── procedure TSMeterView.DrawSMeterZone(ACanvas: TCanvas; R: TRect; X1, X2, Y1, Y2: Integer); const ALPHA = 16384; var Img: TLazIntfImage; FC: TFPColor; IX, IY: Integer; begin if (X2 <= X1) or (FSmBitmap = nil) then Exit; Img := FSmBitmap.CreateIntfImage; try for IY := Y1 to Y2 - 1 do for IX := X1 to X2 - 1 do begin FC := Img.Colors[IX, IY]; FC.Red := Min(65535, FC.Red + ALPHA); FC.Green := Min(65535, FC.Green + ALPHA); FC.Blue := Min(65535, FC.Blue + ALPHA); Img.Colors[IX, IY] := FC; end; FSmBitmap.LoadFromIntfImage(Img); finally Img.Free; end; end; // ──────────────────────────────────────────────────────────────────────────── // DrawTXMeter // ──────────────────────────────────────────────────────────────────────────── procedure TSMeterView.DrawTXMeter(ACanvas: TCanvas; R: TRect); const SWR_HIGH_THRESH = 2.5; SWR_MAX = 10.0; LEFT_INFO_MIN = 62; RIGHT_PAD = 8; LABEL_PAD = 4; TICK_LONG = 5; TICK_MED = 3; PWR_MARKS: array[0..4] of Double = (0.0, 0.25, 0.5, 0.75, 1.0); SWR_MARKS_VAL: array[0..3] of Double = (1.0, 2.5, 5.0, 10.0); SWR_MARKS_LBL: array[0..3] of string = ('1', '2.5', '5', '10'); var CLR_SMETER_BG, CLR_BAR_GREEN, CLR_BAR_OVER: TColor; CLR_TICK_GREEN, CLR_TICK_WHITE, CLR_TICK_BLUE: TColor; CLR_LABEL_DBM, CLR_LABEL_S: TColor; CLR_SCALE_DBM, CLR_SCALE_S_GREEN, CLR_SCALE_S_BLUE, CLR_BDR: TColor; CLR_SWR_HIGH: TColor; W, H, BX, BW, LeftInfo: Integer; Y_BAR_TOP, Y_BAR_BOT, Y_BAR_MID: Integer; PY1, PY2, SY1, SY2: Integer; PBarEnd, SBarEnd, S_WarnX: Integer; LblPwr, LblSWR: string; TWPwr, LblZoneL, TH10, TH6, MaxTWPwr, MaxTWSWR, LabelNeed: Integer; i, X, TW: Integer; Lbl: string; SWRHigh: Boolean; PwrPct: Double; function PwrToX(Pct: Double): Integer; inline; begin Result := BX + Round(Max(0.0, Min(1.0, Pct)) * BW); if Result < BX then Result := BX; if Result > BX + BW then Result := BX + BW; end; function SWRToX(SWR: Double): Integer; inline; begin Result := BX + Round(Max(0.0, Min(1.0, (SWR - 1.0) / (SWR_MAX - 1.0))) * BW); if Result < BX then Result := BX; if Result > BX + BW then Result := BX + BW; end; procedure VLine(X2, Y1, Y2: Integer; C: TColor); begin ACanvas.Pen.Color := C; ACanvas.MoveTo(X2, Y1); ACanvas.LineTo(X2, Y2); end; procedure FillBar(X1, Y1, X2, Y2: Integer; C: TColor); begin ACanvas.Brush.Color := C; ACanvas.Brush.Style := bsSolid; ACanvas.Pen.Style := psClear; if X2 > X1 then ACanvas.FillRect(Rect(X1, Y1, X2, Y2)); ACanvas.Pen.Style := psSolid; end; begin CLR_SMETER_BG := FTheme.SMeterBG; CLR_BAR_GREEN := FTheme.SMeterBarGreen; CLR_BAR_OVER := FTheme.SMeterBarOver; CLR_TICK_GREEN := FTheme.SMeterTickGreen; CLR_TICK_WHITE := FTheme.SMeterTickWhite; CLR_TICK_BLUE := FTheme.SMeterTickBlue; CLR_LABEL_DBM := FTheme.SMeterLabelDbm; CLR_LABEL_S := FTheme.SMeterLabelS; CLR_SCALE_DBM := FTheme.SMeterScaleDbm; CLR_SCALE_S_GREEN:= FTheme.SMeterScaleSGreen; CLR_SCALE_S_BLUE := FTheme.SMeterScaleSBlue; CLR_BDR := FTheme.SMeterBdr; CLR_SWR_HIGH := TColor($000000CC); W := R.Right - R.Left; H := R.Bottom - R.Top; if (W < 80) or (H < 16) 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; 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; LblZoneL := BX - LabelNeed; Y_BAR_TOP := R.Top + H * 44 div 100; Y_BAR_BOT := R.Top + H * 58 div 100; 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; 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; FillBar(BX, PY1, BX + BW, PY2, CLR_BAR_GREEN); FillBar(BX, SY1, S_WarnX, SY2, CLR_BAR_GREEN); FillBar(S_WarnX, SY1, BX + BW, SY2, CLR_BAR_OVER); if FPAMaxPower > 0 then PwrPct := FLastFwdW / FPAMaxPower else PwrPct := 0; PBarEnd := PwrToX(PwrPct); FillBar(BX, PY1 + 1, PBarEnd, PY2 - 1, SM_CLR_METER_ON); SBarEnd := SWRToX(FLastSWR); if not SWRHigh then FillBar(BX, SY1 + 1, SBarEnd, SY2 - 1, SM_CLR_AMBER) else begin FillBar(BX, SY1 + 1, Min(S_WarnX, SBarEnd), SY2 - 1, SM_CLR_AMBER); if SBarEnd > S_WarnX then FillBar(S_WarnX, SY1 + 1, SBarEnd, SY2 - 1, CLR_SWR_HIGH); end; VLine(S_WarnX, SY1, SY2, FTheme.SMeterDivider); ACanvas.Brush.Style := bsClear; ACanvas.Pen.Color := CLR_BDR; ACanvas.Rectangle(LblZoneL, PY1, BX + BW, PY2); if SWRHigh then ACanvas.Pen.Color := CLR_SWR_HIGH else ACanvas.Pen.Color := CLR_BDR; 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.Font.Size := 6; ACanvas.Font.Style := []; ACanvas.Brush.Style := bsClear; i := 5; while i < 100 do begin if (i mod 25) <> 0 then begin X := PwrToX(i / 100.0); VLine(X, PY1 - TICK_MED, PY1 - 1, CLR_TICK_GREEN); end; Inc(i, 5); end; for i := 0 to 4 do begin X := PwrToX(PWR_MARKS[i]); VLine(X, PY1 - TICK_LONG, PY1 - 1, 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); 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); 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) else VLine(X, SY2 + 1, SY2 + TICK_LONG, CLR_TICK_GREEN); Lbl := SWR_MARKS_LBL[i]; TW := ACanvas.TextWidth(Lbl); if SWR_MARKS_VAL[i] >= SWR_HIGH_THRESH then begin if SWRHigh then ACanvas.Font.Color := CLR_SWR_HIGH 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); 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, PY1 - TH10 - 2, LblPwr); ACanvas.Font.Size := 10; 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); 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'); end; end; // ──────────────────────────────────────────────────────────────────────────── // PaintSMeterRight // ──────────────────────────────────────────────────────────────────────────── procedure TSMeterView.PaintSMeterRight(Sender: TObject); var PB: TPaintBox; W, H, 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 begin FreeAndNil(FSmBitmap); FSmBitmap := TBitmap.Create; FSmBitmap.SetSize(W, H); end; if FTransmitting then begin DrawTXMeter(FSmBitmap.Canvas, Rect(0, 0, W, H)); end else begin DrawSMeterWide(FSmBitmap.Canvas, Rect(0, 0, W, H), FLastSMeter, FSMeterPeak, FSMeterMin, ZX1, ZX2, ZY1, ZY2); DrawSMeterZone(FSmBitmap.Canvas, Rect(0, 0, W, H), ZX1, ZX2, ZY1, ZY2); end; PB.Canvas.Draw(0, 0, FSmBitmap); end; end.