[Delphi] 分享一个模态弹窗背景模糊的实现

这是一个很久以前写的demo,今天又看到了,就发出来记录一下。

先来看一下效果图:

代码很简单

Unit1.pas

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    CheckBox1: TCheckBox;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  Unit2;

type
  TMCustomControl = class(TCustomControl);
  
const
  HSLRange: Integer = 240;

type
  TPixelLine = Array[Word] of TRGBQuad;
  pPixelLine = ^TPixelLine;

type
  PDIBInfo = ^TDIBInfo;
  TDIBInfo = object
    BufferDC: HDC;          // 兼容内存DC
    BufferBits: Pointer;    // 位图数据
    BytesPerRow: Integer;   // 每行数据的大小
    OldBitmap, BufferBitmap: HBitmap; // 位图句柄
    bmInfo: TBitmapInfo;    // 位图信息
    function InitDIB(dc: HDC; aw, ah: Integer): Boolean;
    function GetScanline(Row: Integer): PRGBQuad;
    procedure FreeRes();
  end;

procedure InitBmpInfo(var bInfo: TBitmapInfo; w, h: Integer; bitCount: Word = 32); inline;
begin
  FillChar(bInfo, SizeOf(bInfo), 0);
  with bInfo.bmiHeader do begin
    biSize := SizeOf(TBitmapInfoHeader);
    biWidth := w;
    biHeight := h;
    biPlanes := 1;
    biBitCount := bitCount;
    biCompression := BI_RGB;
    biSizeImage := w * h * (biBitCount div 8);
  end;
end;

{ TDIBInfo }

procedure TDIBInfo.FreeRes;
begin
  if BufferDC <> 0 then begin
    SelectObject(BufferDC, OldBitmap);
    DeleteObject(BufferBitmap);
    DeleteDC(BufferDC);
  end;
end;

function TDIBInfo.GetScanline(Row: Integer): PRGBQuad;
begin
  Integer(Result) := Integer(BufferBits) + Row * BytesPerRow;
end;

function TDIBInfo.InitDIB(dc: HDC; aw, ah: Integer): Boolean;
begin
  Result := False;
  BufferDC := 0;
  if (aw < 1) or (ah < 1) then Exit;
  // 创建内存兼容DC
  BufferDC := CreateCompatibleDC(dc);
  if (BufferDC = 0) then Exit;

  // 初始化临时DIB位图信息
  InitBmpInfo(bmInfo, aw, ah, 32);

  // 创建临时DIB位图
  BufferBitmap := CreateDIBSection(BufferDC, bmInfo, DIB_RGB_COLORS,
    BufferBits, 0, 0);
  if (BufferBitmap = 0) or (BufferBits = Nil) then begin
    if BufferBitmap <> 0 then DeleteObject(BufferBitmap);
    DeleteDC(BufferDC);
    BufferDC := 0;
    Exit;
  end;
  OldBitmap := SelectObject(BufferDC, BufferBitmap);

  // DIB数据行大小
  BytesPerRow := (((bmInfo.bmiHeader.biBitCount * aw) + 31)
    and not 31) div 8;
    
  Result := True;
end;


{-------------------------------------------------------------------------------
  函数名:    FillTransRect
  作者:      YangYxd
  日期:      2013.08.28
    - dc: HDC;                     目标设备场景句柄
    - r: TRect;                    目标矩形区域
    - color: LongInt;              阴影颜色
    - alpha: Byte;                 透明度(0..255)
    - blur: Byte;                  模糊半径 (半径越大,速度越慢)
  返回值:    无
-------------------------------------------------------------------------------}
function FillTransRect(dc: HDC; r: TRect; color: LongInt; alpha: Byte; blur: Byte): LongInt;
var
  DIBInfo      : TDIBInfo;
  BufferDC     : HDC;
  Bursh        : HBRUSH;

  ImageData,
  UpRowData,
  NextRowData  : pPixelLine;

  p1, p3, p5, p6, p8: PRGBQuad;
  cr, cg, cb   : Integer;
  nalpha       : Byte;
  
  i, j, x, y : Integer;
  W, H : Integer;
begin
  Bursh := CreateSolidBrush(color);
  if alpha < 1 then begin
    FillRect(dc, r, Bursh);
  end else begin
    w := r.Right - r.Left;
    h := r.Bottom - r.Top;
    if not DIBInfo.InitDIB(dc, w, h) then begin
      DIBInfo.FreeRes;
      Exit;
    end;
    BufferDC := DIBInfo.BufferDC;

    cr := color and MaxByte;;
    cg := (color shr 8) and MaxByte;;
    cb := (color shr 16) and MaxByte;
    nalpha := not alpha;
    BitBlt(BufferDC, 0, 0, w, h, DC, r.Left, r.Top, SRCCOPY);

    ImageData := DIBInfo.BufferBits;
    for y := 1 to H do begin
      for x := 0 to W - 1 do begin
        p1 := @ImageData^[x];
        p1.rgbBlue := ($7F + p1.rgbBlue * alpha +
          cb * (nalpha)) div $FF;
        p1.rgbGreen := ($7F + p1.rgbGreen * alpha +
          cg * (nalpha)) div $FF;
        p1.rgbRed := ($7F + p1.rgbRed * alpha +
          cr * (nalpha)) div $FF;
      end;
      inc(Longint(ImageData), DIBInfo.BytesPerRow);
    end;
    
    for i := 1 to blur - 1 do begin
      UpRowData := DIBInfo.BufferBits;
      ImageData := UpRowData;
      Inc(Longint(ImageData), DIBInfo.BytesPerRow);
      NextRowData := ImageData;
      Inc(Longint(NextRowData), DIBInfo.BytesPerRow);
      for y := 2 to H - 1 do begin
        for x := 1 to W - 3 do begin
          p1 := @ImageData^[x];
          p3 := @UpRowData^[x];
          p5 := @ImageData^[x-1];
          p6 := @ImageData^[x+1];
          p8 := @NextRowData^[x];

          p1.rgbBlue := (p1.rgbBlue + p3.rgbBlue + p5.rgbBlue + p6.rgbBlue + p8.rgbBlue) div 5;
          p1.rgbGreen := (p1.rgbGreen + p3.rgbGreen + p5.rgbGreen + p6.rgbGreen + p8.rgbGreen) div 5;
          p1.rgbRed := (p1.rgbRed + p3.rgbRed + p5.rgbRed + p6.rgbRed + p8.rgbRed) div 5;
        end;
        UpRowData := ImageData;
        ImageData := NextRowData;
        inc(Longint(NextRowData), DIBInfo.BytesPerRow);
      end;
    end;

    BitBlt(dc, r.Left, r.Top, w, h, BufferDC, 0, 0, SRCCOPY);  
    DIBInfo.FreeRes;
  end;
  DeleteObject(Bursh);
end;

function ShowModel(AOwner: TCustomForm; const FromCls: TFormClass): Integer;

  function CaptureScreen(const R: TRect): TBitmap;
  const
    CAPTUREBLT = $40000000;
  var
    hdcScreen: HDC;
    hdcCompatible: HDC;
    hbmScreen: HBITMAP;
  begin
    hdcScreen := GetDC(0);
    hdcCompatible := CreateCompatibleDC(hdcScreen);
    hbmScreen := CreateCompatibleBitmap(hdcScreen, GetDeviceCaps(hdcScreen, HORZRES), GetDeviceCaps(hdcScreen, VERTRES));
    if hbmScreen <> 0 then begin
      Result := TBitmap.Create;
      Result.Handle := hbmScreen;
      SelectObject(hdcCompatible, hbmScreen);
      BitBlt(hdcCompatible, 0, 0, Result.Width, Result.Height, hdcScreen, R.Left, R.Top, SRCCOPY or CAPTUREBLT);
    end;
    DeleteDC(hdcScreen);
    DeleteDC(hdcCompatible);
    // 画上半透明区域
    FillTransRect(Result.Canvas.Handle, Result.Canvas.ClipRect, clBlack, 110, 5);
  end;

  function CaptureWindow(const Wnd: THandle): TBitmap;
  var
    R: TRect;
    PT: TPoint;
  begin
    GetWindowRect(Wnd, R);
    SetRect(R, 0, 0, R.Right - R.Left, R.Bottom - R.Top);
    PT := R.TopLeft;
    ClientToScreen(AOwner.Handle, PT);
    R.TopLeft := PT;
    PT := R.BottomRight;
    ClientToScreen(AOwner.Handle, PT);
    R.BottomRight := PT;
    Result := CaptureScreen(R);
  end;

var
  P: TMCustomControl;
  V: Integer;
  Bmp: TBitmap;
  F: TCustomForm;
begin
  Bmp := CaptureWindow(AOwner.Handle);
  P := TMCustomControl.Create(AOwner);
  try
    P.Parent := AOwner;
    P.Left := 0;
    P.Top := 0;
    P.Width := AOwner.Width;
    P.Height := AOwner.Height;
    P.Enabled := False;
    P.Canvas.Draw(0, 0, Bmp);
    P.Visible := True;
    P.SetZOrder(True);
    FreeAndNil(Bmp);

    F := FromCls.Create(AOwner);
    Result := F.ShowModal;
  finally
    FreeAndNil(Bmp);
    AOwner.RemoveControl(P);
    P.Free; 
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowModel(Self, TForm2);
end;

end.

 

Unit2.pas

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm2 = class(TForm)
    procedure FormDblClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.FormDblClick(Sender: TObject);
begin
  Close;
end;

end.

 

实现原理就是将背景窗口截个图,再模糊一下,显示在一个置顶的控件上,再显示模态窗口。

这个实现性能不怎么好,正式使用的不是这个实现。

 

posted @ 2020-05-27 10:06  我爱我家喵喵  阅读(771)  评论(0编辑  收藏  举报