Лучший способ сделать немерцающие, сегментированные обновления графики в Delphi?

Я подумал, что могу просто выбросить это и просто спросить: я видел элементы управления Delphi, которые безупречны с точки зрения графических эффектов. Значение: без мерцания, разделенные обновления (перерисовывать только ту часть элемента управления, которая помечена как грязная) и плавная прокрутка.

За эти годы я закодировал множество графических элементов управления, поэтому я знаю о двойной буферизации, dibs, bitblts и всем "общем" (я всегда использую dibs для рисования всего, если это возможно, но это накладные расходы). Также знаю о InvalidateRect и проверке TCanvas.ClipRect на фактический прямоугольник, который необходимо обновить. Несмотря на все эти типичные решения, мне очень трудно создайте компоненты такого же качества, как, скажем, - Developer Express или Razed Components. Если графика гладкая, вы можете сделать ставку на мерцание полос прокрутки (собственное), а если полосы прокрутки и рамка гладкие, вы можете ругаться, что фон мерцает во время прокрутки. там стандартный se набор кода, чтобы справиться с этим? Это своего рода передовой опыт, который обеспечивает плавное перерисовку всего элемента управления, включая неклиентскую область элемента управления?

Например, вот элемент управления «голая кость», который принимает высоту для сегментированных обновлений (перерисовывает только то, что есть необходимо). Если вы создаете его в форме, попробуйте переместить окно над ним и посмотреть, как он заменяет части цветами (см. Метод рисования).

Есть ли у кого-нибудь аналогичный базовый класс, который может обрабатывать перерисовку неклиентской области без мерцания?

type

TMyControl = Class(TCustomControl)
private
  (* TWinControl: Erase background prior to client-area paint *)
  procedure WMEraseBkgnd(var Message: TWmEraseBkgnd);message WM_ERASEBKGND;
Protected
  (* TCustomControl: Overrides client-area paint mechanism *)
  Procedure Paint;Override;

  (* TWinControl: Adjust Win32 parameters for CreateWindow *)
  procedure CreateParams(var Params: TCreateParams);override;
public
  Constructor Create(AOwner:TComponent);override;
End;


{ TMyControl }

Constructor TMyControl.Create(AOwner:TComponent);
Begin
  inherited Create(Aowner);
  ControlStyle:=ControlStyle - [csOpaque];
end;

procedure TMyControl.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);

  (* When a window has this style set, any areas that its
     child windows occupy are excluded from the update region. *)
  params.ExStyle:=params.ExStyle + WS_CLIPCHILDREN;

  (* Exclude VREDRAW & HREDRAW *)
  with Params.WindowClass do
  Begin
    (* When a window class has either of these two styles set,
       the window contents will be completely redrawn every time it is
       resized either vertically or horizontally (or both) *)
    style:=style - CS_VREDRAW;
    style:=style - CS_HREDRAW;
  end;
end;

procedure TMyControl.Paint;

  (* Inline proc: check if a rectangle is "empty" *)
  function isEmptyRect(const aRect:TRect):Boolean;
  Begin
    result:=(arect.Right=aRect.Left) and (aRect.Bottom=aRect.Top);
  end;

  (* Inline proc: Compare two rectangles *)
  function isSameRect(const aFirstRect:TRect;const aSecondRect:TRect):Boolean;
  Begin
    result:=sysutils.CompareMem(@aFirstRect,@aSecondRect,SizeOf(TRect))
  end;

  (* Inline proc: This fills the background completely *)
  Procedure FullRepaint;
  var
    mRect:TRect;
  Begin
    mRect:=getClientRect;
    AdjustClientRect(mRect);
    Canvas.Brush.Color:=clWhite;
    Canvas.Brush.Style:=bsSolid;
    Canvas.FillRect(mRect);
  end;

begin
  (* A full redraw is only issed if:
      1. the cliprect is empty
      2. the cliprect = clientrect *)
  if isEmptyRect(Canvas.ClipRect)
  or isSameRect(Canvas.ClipRect,Clientrect) then
  FullRepaint else
  Begin
    (* Randomize a color *)
    Randomize;
    Canvas.Brush.Color:=RGB(random(255),random(255),random(255));

    (* fill "dirty rectangle" *)
    Canvas.Brush.Style:=bsSolid;
    Canvas.FillRect(canvas.ClipRect);
  end;
end;

procedure TMyControl.WMEraseBkgnd(var Message: TWmEraseBkgnd);
begin
  message.Result:=-1;
end;

Обновлено

Я просто хотел добавить, что трюк был комбинацией:

  1. ExcludeClipRect () при рисовании неклиентской области, поэтому вы не перекрываете графику в клиентской области
  2. Захват WMNCCalcSize сообщение, а не просто использовать размер границы для измерений. Мне также пришлось взять высоту для размеров ребер:

     XEdge: = GetSystemMetrics (SM_CXEDGE);
    YEdge: = GetSystemMetrics (SM_CYEDGE);
     
  3. Вызов RedrawWindow () со следующими флагами всякий раз, когда у вас есть перемещенные полосы прокрутки или изменение размера:

     mRect: = ClientRect;
    mFlags:= rdw_Invalidate
      или RDW_NOERASE
      или RDW_FRAME
      или RDW_INTERNALPAINT
      или RDW_NOCHILDREN;
    RedrawWindow (дескриптор окна, @ mRect, 0, mFlags);
     
  4. При обновлении фона во время метода Paint () избегайте рисования поверх возможных дочерних объектов, как это (см. RDW_NOCHILDREN, упомянутый выше):

     for x: = 1 to ControlCount do
    начать
      mCtrl: = Controls [x-1];
      если mCtrl.Visible, то
      Начать
      mRect: = mCtrl.BoundsRect;
      ExcludeClipRect (Canvas.Handle,
      mRect.Left, mRect.Top,
      mRect.Right, mRect.Bottom);
      конец;
    конец;
     

Спасибо за помощь, ребята!

45
задан LU RD 17 September 2015 в 05:12
поделиться