diff --git a/WidebandView.pas b/WidebandView.pas index 97e4e32..d734a1a 100644 --- a/WidebandView.pas +++ b/WidebandView.pas @@ -24,6 +24,7 @@ type TWidebandView = class private FBitmap: TBitmap; + FLabelBitmap: TBitmap; FRulerBitmap: TBitmap; FTheme: TAppTheme; FData: array of Single; @@ -57,6 +58,8 @@ type procedure PaintCPU(C: TCanvas); procedure PaintGL(C: TOpenGLControl); procedure ColorToGL(AColor: TColor; out R, G, B: GLFloat); + procedure DrawBackgroundGL(W, H, PlotW: Integer); + procedure DrawGridGL(W, H, PlotW: Integer); procedure DrawSpectrumGL(PlotW, H: Integer); procedure DrawHamBandGL(PlotW, H: Integer; F1, F2: Double; AColor: TColor); procedure DrawHamBandsGL(PlotW, H: Integer); @@ -71,7 +74,9 @@ type const Name: string; AColor: TColor); procedure DrawFrequencyMarker(C: TCanvas; PlotW, H: Integer); procedure DrawSpectrumGradient(const SpPts: array of TPoint; PlotW, H: Integer); - procedure UploadBitmapToGL; + procedure DrawGLLabels(W, H, PlotW: Integer); + procedure UploadLabelTextureToGL; + procedure DrawLabelTextureGL(W, H: Integer); public constructor Create; destructor Destroy; override; @@ -107,6 +112,8 @@ begin inherited Create; FBitmap := TBitmap.Create; FBitmap.PixelFormat := pf32bit; + FLabelBitmap := TBitmap.Create; + FLabelBitmap.PixelFormat := pf32bit; FRulerBitmap := TBitmap.Create; FRulerBitmap.PixelFormat := pf32bit; FTheme := DarkTheme; @@ -137,6 +144,7 @@ begin if FGLTex <> 0 then glDeleteTextures(1, @FGLTex); FRulerBitmap.Free; + FLabelBitmap.Free; FBitmap.Free; inherited Destroy; end; @@ -230,6 +238,11 @@ begin FBitmap.SetSize(W, H); FGLBackgroundDirty := True; end; + if (FLabelBitmap.Width <> W) or (FLabelBitmap.Height <> H) then + begin + FLabelBitmap.SetSize(W, H); + FGLBackgroundDirty := True; + end; end; procedure TWidebandView.SetBitmapSize(W, H: Integer); @@ -666,8 +679,6 @@ begin DrawHamBands(C, PlotW, H); - C.Font.Color := FTheme.Text; - C.TextOut(6, 4, 'Wideband'); if IncludeSpectrum then FDirty := False else @@ -742,6 +753,67 @@ begin B := ((AColor shr 16) and $FF) / 255.0; end; +procedure TWidebandView.DrawBackgroundGL(W, H, PlotW: Integer); +var + R1, G1, B1, R2, G2, B2: GLFloat; +begin + ColorToGL(FTheme.SpecGradTop, R1, G1, B1); + ColorToGL(FTheme.SpecGradBot, R2, G2, B2); + glBegin(GL_QUADS); + glColor3f(R1, G1, B1); glVertex2f(0, H); + glColor3f(R1, G1, B1); glVertex2f(W, H); + glColor3f(R2, G2, B2); glVertex2f(W, 0); + glColor3f(R2, G2, B2); glVertex2f(0, 0); + glEnd; + + ColorToGL(FTheme.SpecLabelBand, R1, G1, B1); + glColor3f(R1, G1, B1); + glBegin(GL_QUADS); + glVertex2f(PlotW, 0); + glVertex2f(W, 0); + glVertex2f(W, H); + glVertex2f(PlotW, H); + glEnd; +end; + +procedure TWidebandView.DrawGridGL(W, H, PlotW: Integer); +var + X, Y, TopH: Integer; + DB, DBMin, InvRange, StepHz, FreqHz: Double; + R, G, B: GLFloat; +begin + DBMin := FRefLevel - FRange; + InvRange := 1.0 / Max(1.0, FRange); + TopH := Max(1, H - 1); + ColorToGL(FTheme.SpecGrid, R, G, B); + glColor4f(R, G, B, 0.78); + glBegin(GL_LINES); + FLabelBitmap.Canvas.Font.Name := 'Courier New'; + FLabelBitmap.Canvas.Font.Size := 7; + StepHz := RulerGridStepHz(FLabelBitmap.Canvas, PlotW); + FreqHz := Ceil(FViewStartHz / StepHz) * StepHz; + while FreqHz <= FViewEndHz + 0.5 do + begin + X := FreqToX(FreqHz, PlotW); + glVertex2f(X, 0); + glVertex2f(X, H); + FreqHz := FreqHz + StepHz; + end; + + DB := FRefLevel - 20.0; + while DB >= DBMin do + begin + Y := Round((FRefLevel - DB) * InvRange * TopH); + glVertex2f(0, H - Y); + glVertex2f(W, H - Y); + DB := DB - 20.0; + end; + + glVertex2f(PlotW, 0); + glVertex2f(PlotW, H); + glEnd; +end; + procedure TWidebandView.DrawSpectrumGL(PlotW, H: Integer); var I: Integer; @@ -826,66 +898,158 @@ begin DrawHamBandGL(PlotW, H, 50000000, 51990000, TColor($003C729A)); end; -procedure TWidebandView.UploadBitmapToGL; +procedure TWidebandView.DrawGLLabels(W, H, PlotW: Integer); +var + C: TCanvas; + X1, X2, LabelX, LabelY, TW, TH, Y, TopH, ScaleX: Integer; + DB, DBMin, InvRange: Double; + + procedure BandLabel(F1, F2: Double; const Name: string); + begin + X1 := FreqToX(F1, PlotW); + X2 := FreqToX(F2, PlotW); + if (X2 <= 0) or (X1 >= PlotW) then Exit; + X1 := EnsureRange(X1, 0, PlotW - 1); + X2 := EnsureRange(X2, 0, PlotW - 1); + if X2 <= X1 then Exit; + TW := C.TextWidth(Name); + TH := C.TextHeight(Name); + LabelX := EnsureRange(((X1 + X2) div 2) - TW div 2, 2, Max(2, PlotW - TW - 5)); + LabelY := Max(1, Min(H - TH - 1, 2)); + C.TextOut(LabelX, LabelY, Name); + end; + +begin + if (W <= 0) or (H <= 0) then Exit; + if (FLabelBitmap.Width <> W) or (FLabelBitmap.Height <> H) then + FLabelBitmap.SetSize(W, H); + + C := FLabelBitmap.Canvas; + C.Brush.Style := bsSolid; + C.Brush.Color := clBlack; + C.FillRect(Rect(0, 0, W, H)); + C.Brush.Style := bsClear; + + DBMin := FRefLevel - FRange; + InvRange := 1.0 / Max(1.0, FRange); + TopH := Max(1, H - 1); + ScaleX := PlotW; + + C.Font.Name := 'Courier New'; + C.Font.Quality := fqDefault; + C.Font.Size := 7; + C.Font.Color := FTheme.SpecLabelText; + DB := FRefLevel - 20.0; + while DB >= DBMin do + begin + Y := Round((FRefLevel - DB) * InvRange * TopH); + C.TextOut(ScaleX + 2, Y - 9, Format('%4.0f', [DB])); + DB := DB - 20.0; + end; + + C.Font.Name := 'Sans'; + C.Font.Quality := fqDefault; + C.Font.Size := 7; + C.Font.Color := TColor($00EAF7D7); + BandLabel(1810000, 2000000, '160m'); + BandLabel(3500000, 3800000, '80m'); + BandLabel(5258500, 5403500, '60m'); + BandLabel(7000000, 7300000, '40m'); + BandLabel(10100000, 10150000, '30m'); + BandLabel(14000000, 14350000, '20m'); + BandLabel(18068000, 18168000, '17m'); + BandLabel(21000000, 21450000, '15m'); + BandLabel(24890000, 24990000, '12m'); + BandLabel(28000000, 29700000, '10m'); + BandLabel(50000000, 51990000, '6m'); + +end; + +procedure TWidebandView.UploadLabelTextureToGL; var X, Y, I: Integer; Src: PByte; Buf: array of Byte; + A: Byte; begin - if (FBitmap.Width <= 0) or (FBitmap.Height <= 0) then Exit; + if (FLabelBitmap.Width <= 0) or (FLabelBitmap.Height <= 0) then Exit; if FGLTex = 0 then glGenTextures(1, @FGLTex); - SetLength(Buf, FBitmap.Width * FBitmap.Height * 4); - FBitmap.BeginUpdate(False); + SetLength(Buf, FLabelBitmap.Width * FLabelBitmap.Height * 4); + FLabelBitmap.BeginUpdate(False); try I := 0; - for Y := 0 to FBitmap.Height - 1 do + for Y := 0 to FLabelBitmap.Height - 1 do begin - Src := PByte(FBitmap.ScanLine[Y]); - for X := 0 to FBitmap.Width - 1 do + Src := PByte(FLabelBitmap.ScanLine[Y]); + for X := 0 to FLabelBitmap.Width - 1 do begin - Buf[I] := Src[2]; - Buf[I + 1] := Src[1]; - Buf[I + 2] := Src[0]; - Buf[I + 3] := $FF; + A := Max(Src[0], Max(Src[1], Src[2])); + if A < 6 then A := 0; + if A > 0 then + begin + Buf[I] := Byte(Min(255, Src[2] * 255 div A)); + Buf[I + 1] := Byte(Min(255, Src[1] * 255 div A)); + Buf[I + 2] := Byte(Min(255, Src[0] * 255 div A)); + A := Byte(Min(255, A + (255 - A) div 3)); + end + else + begin + Buf[I] := 0; + Buf[I + 1] := 0; + Buf[I + 2] := 0; + end; + Buf[I + 3] := A; Inc(Src, 4); Inc(I, 4); end; end; finally - FBitmap.EndUpdate(False); + FLabelBitmap.EndUpdate(False); end; glBindTexture(GL_TEXTURE_2D, FGLTex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, FBitmap.Width, FBitmap.Height, 0, + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, FLabelBitmap.Width, FLabelBitmap.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, @Buf[0]); end; +procedure TWidebandView.DrawLabelTextureGL(W, H: Integer); +begin + if FGLTex = 0 then Exit; + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, FGLTex); + glColor4f(1, 1, 1, 1); + glBegin(GL_QUADS); + glTexCoord2f(0, 1); glVertex2f(0, 0); + glTexCoord2f(1, 1); glVertex2f(W, 0); + glTexCoord2f(1, 0); glVertex2f(W, H); + glTexCoord2f(0, 0); glVertex2f(0, H); + glEnd; + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); +end; + procedure TWidebandView.PaintGL(C: TOpenGLControl); var W, H, PlotW, X: Integer; - NeedUpload: Boolean; begin if C = nil then Exit; W := Max(1, C.Width); H := Max(1, C.Height); if not C.MakeCurrent then Exit; - NeedUpload := False; if (FBitmap.Width <> W) or (FBitmap.Height <> H) then - begin SetBitmapSize(W, H); - NeedUpload := True; - end; if FGLBackgroundDirty then begin - DrawCPU(W, H, False); - NeedUpload := True; + PlotW := Max(1, W - WB_DB_SCALE_W); + DrawGLLabels(W, H, PlotW); + UploadLabelTextureToGL; + FGLBackgroundDirty := False; end; - if FGLTex = 0 then - NeedUpload := True; - if NeedUpload then - UploadBitmapToGL; glViewport(0, 0, W, H); glMatrixMode(GL_PROJECTION); glLoadIdentity; @@ -895,21 +1059,13 @@ begin glDisable(GL_DEPTH_TEST); glClearColor(0, 0, 0, 1.0); glClear(GL_COLOR_BUFFER_BIT); - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, FGLTex); - glColor3f(1, 1, 1); - glBegin(GL_QUADS); - glTexCoord2f(0, 1); glVertex2f(0, 0); - glTexCoord2f(1, 1); glVertex2f(W, 0); - glTexCoord2f(1, 0); glVertex2f(W, H); - glTexCoord2f(0, 0); glVertex2f(0, H); - glEnd; - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); PlotW := Max(1, W - WB_DB_SCALE_W); + DrawBackgroundGL(W, H, PlotW); + DrawGridGL(W, H, PlotW); DrawSpectrumGL(PlotW, H); DrawHamBandsGL(PlotW, H); + DrawLabelTextureGL(W, H); FDirty := False; if (FMarkerHz >= FViewStartHz) and (FMarkerHz <= FViewEndHz) then