summaryrefslogtreecommitdiff
path: root/gfx/emulayer/geldirty.pas
blob: 1015f1968bae360488f91dc6776a21869d6bd112 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
{
    fpGFX  -  Free Pascal Graphics Library
    Copyright (C) 2000 - 2001 by
      Areca Systems GmbH / Sebastian Guenther, sg@freepascal.org
    Copyright (C) 2006 by Graeme Geldenhuys 
      member of the fpGFX development team.

    Window dirty list (redrawing queue)

    See the file COPYING.modifiedLGPL, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}

unit GELDirty;

{$IFDEF Debug}
  {$ASSERTIONS On}
{$ENDIF}
{$mode objfpc}{$H+}

interface
uses
  Classes,
  GfxBase;		// fpGFX units


type

  PDirtyListEntry = ^TDirtyListEntry;
  TDirtyListEntry = record
    Prev, Next: PDirtyListEntry;
    Window: TFCustomWindow;
    Rect: TRect;
  end;


  TDirtyList = class
  private
    FFirst, FLast: PDirtyListEntry;
  protected
    procedure   RemoveEntry(AEntry: PDirtyListEntry);
  public
    destructor  Destroy; override;
    procedure   AddRect(AWindow: TFCustomWindow; const ARect: TRect);
    procedure   ClearQueueForWindow(AWindow: TFCustomWindow);
    procedure   PaintQueueForWindow(AWindow: TFCustomWindow);
    procedure   PaintAll;
    property    First: PDirtyListEntry read FFirst write FFirst;  // !!!
  end;


implementation


destructor TDirtyList.Destroy;
var
  Entry, NextEntry: PDirtyListEntry;
begin
  Entry := FFirst;
  while Assigned(Entry) do
  begin
    NextEntry := Entry^.Next;
    Dispose(Entry);
    Entry := NextEntry;
  end;
  inherited Destroy;
end;


procedure TDirtyList.AddRect(AWindow: TFCustomWindow; const ARect: TRect);
var
  Entry, NextEntry: PDirtyListEntry;
begin
  // Check for empty or invalid update rectangle
  if (ARect.Left >= ARect.Right) or (ARect.Top >= ARect.Bottom) or
    (ARect.Right < 0) or (ARect.Top < 0) or
    (ARect.Left >= AWindow.ClientWidth) or
    (ARect.Top >= AWindow.ClientHeight) then
    exit;

  { Check if the new rectangle is already contained in some other rectangle
    in the dirty list for the same window }
  Entry := FFirst;
  while Assigned(Entry) do
  begin
    NextEntry := Entry^.Next;
    with Entry^.Rect do
      if AWindow = Entry^.Window then
        if (ARect.Left >= Left) and (ARect.Top >= Top) and
          (ARect.Right <= Right) and (ARect.Bottom <= Bottom) then
	  // Rectangle is already contained in dirt list -> do nothing
	  exit
	else if (Left >= ARect.Left) and (Top >= ARect.Top) and
	  (Right <= ARect.Right) and (Bottom <= ARect.Bottom) then
	begin
	  // The new rectangle contains the currently checked rectangle
	  Entry^.Rect := ARect;
	  exit;
	end;
    Entry := NextEntry;
  end;


  // If we got this far, then we really have to add the rectangle to our list

  New(Entry);
  Entry^.Window := AWindow;
  Entry^.Rect := ARect;
  Entry^.Next := nil;

  if Assigned(FFirst) then
  begin
    Entry^.Prev := FLast;
    FLast^.Next := Entry;
    FLast := Entry;
  end else
  begin
    Entry^.Prev := nil;
    FFirst := Entry;
    FLast := Entry;
  end;
end;


procedure TDirtyList.ClearQueueForWindow(AWindow: TFCustomWindow);
var
  Entry, NextEntry: PDirtyListEntry;
begin
  Entry := FFirst;
  while Assigned(Entry) do
  begin
    NextEntry := Entry^.Next;
    if Entry^.Window = AWindow then
      RemoveEntry(Entry);
    Entry := NextEntry;
  end;
end;


procedure TDirtyList.PaintQueueForWindow(AWindow: TFCustomWindow);
var
  IsNotEmpty: Boolean;
  Entry, NextEntry: PDirtyListEntry;
begin
  IsNotEmpty := False;
  AWindow.Canvas.SaveState;
  AWindow.Canvas.EmptyClipRect;

  Entry := First;
  while Assigned(Entry) do
  begin
    NextEntry := Entry^.Next;
    if Entry^.Window = AWindow then
    begin
      IsNotEmpty := AWindow.Canvas.UnionClipRect(Entry^.Rect);
      RemoveEntry(Entry);
    end;
    Entry := NextEntry;
  end;

  if IsNotEmpty and Assigned(AWindow.OnPaint) then
    AWindow.OnPaint(AWindow, AWindow.Canvas.GetClipRect);

  AWindow.Canvas.RestoreState;
end;


procedure TDirtyList.PaintAll;
begin
  while Assigned(FFirst) do
    PaintQueueForWindow(FFirst^.Window);
end;


procedure TDirtyList.RemoveEntry(AEntry: PDirtyListEntry);
begin
  if Assigned(AEntry^.Prev) then
    AEntry^.Prev^.Next := AEntry^.Next
  else
    FFirst := AEntry^.Next;
  if Assigned(AEntry^.Next) then
    AEntry^.Next^.Prev := AEntry^.Prev
  else
    FLast := AEntry^.Prev;
  Dispose(AEntry);
end;


end.