お知らせ

電子会議

ライブラリ

パレット

Delphi FAQ検索

Delphi FAQ一覧

サンプル蔵





FDelphi FAQ
16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル

"TImage 等のちらつきを効率良く抑制する"






【タイトル】フォーム上の TImage などの再描画に伴うちらつきを
      DoubleBufferd プロパティよりも効率良く抑制する。

 フォーム上の TImage や TLabel の移動・再描画に伴うちらつきを、
DoubleBuffered プロパティ(Delphi4 & C++Builder4 以降)の設定よ
りも効率的に行う手段として
    NIFTY:FDELPHI/MES/8/10397 と
    NIFTY:SINPRISE/MES/2/16412 で
コードを出しましたが場合により、失敗することが判明しました。

 原因は CreateCompatibleBitmap の引数に渡すデバイスコンテキス
トが BeginPaint により用意されるデバイスコンテキストや、コンパ
チブルなメモリデバイスコンテキストを使うとモノクロのビットマッ
プになってしまうことがある(一定しない)ためです。

 この原因がOSの仕様なのかOSのバグなのかは不明ですが、とり
あえず、下記のサンプルでは手堅い手段に変えてあります。

 また、先の電子会議室に出した方法では、対角線上に離れた2つの
TGraphicControl で同時に再描画が発生すると効率が大きく低下する
(DoubleBuffered プロパティと同程度まで)ので、この点について
も改良してあります。

// ここから Delphi4 用のサンプルコード //==================

type
  TForm1 = class(TForm)
  private
    procedure WMEraseBkgnd(var Message: TWMEraseBkgnd);
                               message WM_ERASEBKGND;
    procedure WMPaint(var Message: TWMPaint);
                               message WM_PAINT;
  end;

procedure TForm1.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
// 何もしない
end;

procedure TForm1.WMPaint(var Message: TWMPaint);
var
  PS: TPaintStruct;
  w, h: Integer;
  DC: HDC;
  bmp, bmpOld: HBITMAP;
  i, Count, SaveDCIndex: Integer;
begin
  if Message.DC <> 0 then
    // Perform(WM_PAINT.. などの特殊な場合は今までどおり
    inherited
  else
  begin
    BeginPaint(Handle, PS);
    try
      // 描画する領域の幅と高さを記録しておく
      w := PS.rcPaint.Right - PS.rcPaint.Left;
      h := PS.rcPaint.Bottom - PS.rcPaint.Top;
      DC := GetDC(HWND(0));
      bmp := CreateCompatibleBitmap(DC, w, h);
      ReleaseDC(HWND(0), DC);
      try
        Message.DC := CreateCompatibleDC(HDC(0));
        with PS do
        try
          bmpOld := SelectObject(Message.DC, bmp);

          with rcPaint do
            SetWindowOrgEx(Message.DC, Left, Top, nil);
          FillRect(Message.DC, rcPaint, Brush.Handle);

          Count := ControlCount;
          for i := 0 to Count - 1 do
          begin
            if Controls[i] is TWinControl then break;
            with Controls[i] do
            begin
              if Visible and RectVisible(hdc, BoundsRect) then
              begin
                SaveDCIndex := SaveDC(Message.DC);
                OffsetWindowOrgEx(Message.DC, -Left, -Top, 
PPoint(0)^);
                IntersectClipRect(Message.DC, 0, 0, Width, Height);
                Perform(WM_PAINT, Message.DC, 0);
                RestoreDC(Message.DC, SaveDCIndex);
              end;
            end;
          end;

          BitBlt(hdc, rcPaint.Left, rcPaint.Top, w, h,
              Message.DC, rcPaint.Left, rcPaint.Top, SRCCOPY);

          SelectObject(Message.DC, bmpOld);
        finally
          DeleteDC(Message.DC);
          Message.DC := 0;
        end;
      finally
        DeleteObject(bmp);
      end;
    finally
      EndPaint(Handle, PS);
    end;
  end;
end;


// ここから C++Builder4 用のサンプルコード //==============

class TForm1 : public TForm
{
private:
  void __fastcall WMEraseBkgnd(TWMEraseBkgnd &Message)
  { /* 何もしない */ }
  void __fastcall WMPaint(TWMPaint &Message)
  {
    if(Message.DC)
      // 特別な場合(Message.DC != 0)は通常の再描画を行う
      TForm::Dispatch(&Message);
    else
    {
      PAINTSTRUCT ps;
      HDC hdc = BeginPaint(Handle, &ps);
      try
      {
        int w = ps.rcPaint.right - ps.rcPaint.left;
        int h = ps.rcPaint.bottom - ps.rcPaint.top;
        HWND DC = GetDC((HWND)0);
        HBITMAP hbmp = CreateCompatibleBitmap(DC, w, h);
        ReleaseDC((HWND)0, DC);
        try
        {
          Message.DC = CreateCompatibleDC((HWND)0);
          try
          {
            HBITMAP hbmpOld = SelectObject(Message.DC, hbmp);

            SetWindowOrgEx(Message.DC,
              ps.rcPaint.left, ps.rcPaint.top, NULL);
            FillRect(Message.DC, &(ps.rcPaint), Brush->Handle);

            int Count = ControlCount;
            for(int i = 0; i < Count; i++)
            {
              TGraphicControl *Control
                = dynamic_cast<TGraphicControl*>(Controls[i]);
              if(!Control) break;
              RECT R = Control->BoundsRect;
              if(Control->Visible && RectVisible(hdc, &R))
              {
                int SaveDCIndex = SaveDC(Message.DC);
                OffsetWindowOrgEx(Message.DC,
                  -Control->Left, -Control->Top, NULL);
                IntersectClipRect(Message.DC,
                  0, 0, Control->Width, Control->Height);
                Control->Perform(WM_PAINT, (int)Message.DC, 0);
                RestoreDC(Message.DC, SaveDCIndex);
              }
            }

            BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, w, h,
              Message.DC, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
            SelectObject(Message.DC, hbmpOld);
          }__finally{
            DeleteDC(Message.DC);
            Message.DC = 0;
          }
        }__finally{
          DeleteObject(hbmp);
        }
      }__finally{
        EndPaint(Handle, &ps);
      }
    }
  }
  BEGIN_MESSAGE_MAP
    MESSAGE_HANDLER(WM_ERASEBKGND, TWMEraseBkgnd, WMEraseBkgnd)
    MESSAGE_HANDLER(WM_PAINT, TWMPaint, WMPaint)
  END_MESSAGE_MAP(TControl)
public:         // ユーザー宣言
  __fastcall TForm1(TComponent* Owner):TForm(Owner){}
};

// サンプルコードはここまでです。

P.S.
 私作のコンポーネント MultiImage システムの本体や MultiButton の
サンプルコードに入れてあるダブルバッファリング機構は、上記の改良
が加えられていません(1999/09/12現在)。
 どちらコンポーネントも該当部分をソースコードで出してありますが、
近日中に改定します。

1999/09/12、河邦 正(GCC02240@nifty.ne.jp)
(http://member.nifty.ne.jp/kht0000/ 自作ComponentのNifty外へ公開用)

Original document by 河邦 正         氏 ID:(GCC02240)


ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum の16番会議室「玉石混淆みんなで作るSample蔵」に投稿されたサンプルです。これらのサンプルはボーランド株式会社がサポートする公式のものではありません。また、必ずしも動作が検証されているものではありません。これらのサンプルを使用したことに起因するいかなる損害も投稿者、およびフォーラムスタッフはその責めを負いません。使用者のリスクの範疇でご使用下さい。

Copyright 1996-2002 Delphi Users' Forum