如何及时重绘画布?

Posted

技术标签:

【中文标题】如何及时重绘画布?【英文标题】:How to repaint canvas just in time? 【发布时间】:2014-07-24 10:53:00 【问题描述】:

问题是: 我在桌面上画了一些矩形,而鼠标移动(矩形大小增加)我没有滞后、伪影等,一切都很好:

但是,当我将矩形的大小调整为低于原来的尺寸时,我得到了人工制品:

红色矩形是真正的矩形,其他都是bug。

完美的解决方案是重画画布,但我不能在鼠标移动时一直这样做。

鼠标在移动后绝对停止时有解决办法吗?

更新

代码:

    unit Unit2;

interface

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

type
  TForm2 = class(TForm)
    Timer1: TTimer;
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure FormCreate(Sender: TObject);
    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormDestroy(Sender: TObject);
  private
     Private declarations 
    isDown: Boolean;
    downX, downY: Integer;
  public
     Public declarations 
    Bild: TBitMap;
  end;

implementation

表格道具: 边框样式= bsNone AlphaBlend 真实,150 透明色 = true, clBlack

$R *.dfm

procedure TForm2.FormCreate(Sender: TObject);
begin
  Bild := TBitMap.Create;
end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
  Bild.Free;
end;

procedure TForm2.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  isDown := true;
  downX := X;
  downY := Y;
end;

procedure TForm2.FormMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
const
  cVal = 4;
begin
  if isDown then
  begin
    Self.Canvas.Lock;
    Self.Repaint;
    Self.Canvas.Pen.Color := clNone;
    Self.Canvas.Pen.Width := 1;

    Self.Canvas.Pen.Style := psDot;
    //Self.Canvas.Pen.Mode := pmNotCopy;
    Self.Canvas.Brush.Color := clGreen;
    Self.Canvas.Rectangle(downX, downY, X, Y);
    Self.Canvas.Pen.Style := psSolid;
    Self.Canvas.Brush.Color := clNone;
    Self.Canvas.Unlock;
     Self.Canvas.Rectangle(downX - cVal, downY - cVal, downX + cVal, downY + cVal);
     Self.Canvas.Rectangle(X - cVal, Y - cVal, X + cVal, Y + cVal);
     Self.Canvas.Rectangle(X - cVal, downY - cVal, X + cVal, downY + cVal);
     Self.Canvas.Rectangle(downX - cVal, Y - cVal, downX + cVal, Y + cVal);

     Self.Canvas.Rectangle(downX - cVal, (downY + Y) div 2 - cVal, downX + cVal,
       (downY + Y) div 2 + cVal);
     Self.Canvas.Rectangle(X - cVal, (downY + Y) div 2 - cVal, X + cVal,
       (downY + Y) div 2 + cVal);

     Self.Canvas.Rectangle((downX + X) div 2 - cVal, downY - cVal,
       (downX + X) div 2 + cVal, downY + cVal);
     Self.Canvas.Rectangle((downX + X) div 2 - cVal, Y - cVal, (downX + X) div 2 + cVal,
       Y + cVal);   
  end;
end;

function CaptureRect(aRect: TRect; out aBmp: TBitmap): Boolean;
var
  ScreenDC: HDC;
begin
  Result := False;
  try
    with aBmp, aRect do
    begin
      Width := Right - Left;
      Height := Bottom - Top;
      ScreenDC := GetDC(0);
      try
        BitBlt(Canvas.Handle, 0, 0, Width, Height, ScreenDC, Left, Top, SRCCOPY);
      finally
        ReleaseDC(0, ScreenDC);
      end;
    end;
  except
  end;
end;

procedure TForm2.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  r: TRect;
begin
  isDown := false;
  r.Left := downX;
  r.Top := downY;
  r.Right := X;
  r.Bottom := Y;
  CaptureRect(r, Bild);
  Self.Close;
end;

end.

【问题讨论】:

你可以用pmXor模式的钢笔覆盖旧矩形 不,这不起作用。 你有代码吗?是什么让你认为你可以在你不拥有的窗户上画画。如果其他应用程序开始在您的 Windows 上绘图,您会怎么想?如果您的程序停止工作,您会感到惊讶吗?您会尝试通过更改代码来适应这个问题,还是会责怪其他程序?你了解绘画的工作原理吗?具体来说,窗口需要能够随时更新自己以响应WM_PAINT 消息,并且不需要具有持久状态。 【参考方案1】:

你的问题是你画错了地方。在OnMouseMove 事件处理程序中停止绘制。将绘画代码移动到绘画处理程序。例如表单的OnPaint 处理程序。

然后,在OnMouseMove 事件处理程序中,实际上是OnMouseDownOnMouseUp,调用表单上的Invalidate,或Win32 的InvalidateRect 函数,以强制绘制循环。

【讨论】:

非常感谢!问题消失了。【参考方案2】:

改为绘制到分层窗口中。这将在没有人工制品的情况下为您提供极快的速度,并且 Windows 会负责绘图。

分层窗口是在使用 CreateWindowEx 函数创建窗口时通过指定 WS_EX_LAYERED 创建的窗口。稍后您可以使用 UpdateLayeredWindow 来设置此窗口的内容。这样您就可以在画布上进行绘画,而无需修改画布的内容。

当然,这是解决问题的更高级方法。因此,您需要对 Windows API 有所了解。

【讨论】:

您能否更简单地描述一下“改为绘制到分层窗口”下的意思?我的英语词典不是那么完美,无法真正理解。 我使用带 bsNone 的窗口,并且宽度和高度等于屏幕尺寸 @AlexLL 如果你不显示任何代码,我们只需要猜测你在做什么。这根本没有生产力。如果您需要帮助,您将更加努力地向我们所有人说明您在做什么。在你这样做之前,这将是一个相当无用和痛苦的练习。 @Srbastian_Z,请回答。 我更新了我的答案。但是现在您已经展示了您的代码,可能有一种更简单的方法来归档所需的结果。

以上是关于如何及时重绘画布?的主要内容,如果未能解决你的问题,请参考以下文章

js中的canvas画图,clearrect清除画布之后,重绘页面空白,重绘不出来,但没报错,js方法正常执行。

Android 4+ html5画布不重绘

无法使用 invalidate() 重绘我的画布位图;

html5画布在调整大小时重绘

重绘或删除现有画布并创建具有已修改内容的新画布

在 MFC 中重绘形状