📄 gr32_microtiles.pas
字号:
unit GR32_MicroTiles;
(* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is MicroTiles Repaint Optimizer Extension for Graphics32
*
* The Initial Developer of the Original Code is
* Andre Beckedorf - metaException OHG
* Andre@metaException.de
*
* Portions created by the Initial Developer are Copyright (C) 2005-2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** *)
interface
{$I GR32.inc}
{-$DEFINE CODESITE}
{-$DEFINE CODESITE_HIGH}
{-$DEFINE PROFILINGDRYRUN}
{-$DEFINE MICROTILES_DEBUGDRAW}
{-$DEFINE MICROTILES_DEBUGDRAW_RANDOM_COLORS}
{-$DEFINE MICROTILES_DEBUGDRAW_UNOPTIMIZED}
{-$DEFINE MICROTILES_NO_ADAPTION}
{-$DEFINE MICROTILES_NO_ADAPTION_FORCE_WHOLETILES}
uses
{$IFDEF CLX}
Qt, {$IFDEF LINUX}Libc, {$ELSE}Windows, {$ENDIF}
{$ELSE}
Windows,
{$ENDIF}
{$IFDEF CODESITE}CSIntf, CSAux,{$ENDIF}
{$IFDEF COMPILER2005}Types, {$ENDIF}
SysUtils, Classes,
GR32, GR32_System, GR32_Containers, GR32_Layers, GR32_RepaintOpt;
const
MICROTILE_SHIFT = 5;
MICROTILE_SIZE = 1 shl MICROTILE_SHIFT;
MICROTILE_EMPTY = 0;
// MICROTILE_EMPTY -> Left: 0, Top: 0, Right: 0, Bottom: 0
MICROTILE_FULL = MICROTILE_SIZE shl 8 or MICROTILE_SIZE;
// MICROTILE_FULL -> Left: 0, Top: 0, Right: MICROTILE_SIZE, Bottom: MICROTILE_SIZE
{$IFDEF MICROTILES_DEBUGDRAW}
clDebugDrawFill = TColor32($30FF0000);
clDebugDrawFrame = TColor32($90FF0000);
{$ENDIF}
type
PMicroTile = ^TMicroTile;
TMicroTile = type Integer;
PMicroTileArray = ^TMicroTileArray;
TMicroTileArray = array[0..MaxListSize - 1] of TMicroTile;
PPMicroTiles = ^PMicroTiles;
PMicroTiles = ^TMicroTiles;
TMicroTiles = record
BoundsRect: TRect;
Columns, Rows: Integer;
BoundsUsedTiles: TRect;
Count: Integer;
Tiles: PMicroTileArray;
end;
// MicroTile auxiliary routines
function MakeMicroTile(const Left, Top, Right, Bottom: Integer): TMicroTile; {$IFDEF USEINLINING} inline; {$ENDIF}
function MicroTileHeight(const Tile: TMicroTile): Integer; {$IFDEF USEINLINING} inline; {$ENDIF}
function MicroTileWidth(const Tile: TMicroTile): Integer; {$IFDEF USEINLINING} inline; {$ENDIF}
var
MicroTileUnion: procedure(var DstTile: TMicroTile; const SrcTile: TMicroTile);
// MicroTiles auxiliary routines
function MakeEmptyMicroTiles: TMicroTiles; {$IFDEF USEINLINING} inline; {$ENDIF}
procedure MicroTilesCreate(var MicroTiles: TMicroTiles); {$IFDEF USEINLINING} inline; {$ENDIF}
procedure MicroTilesDestroy(var MicroTiles: TMicroTiles); {$IFDEF USEINLINING} inline; {$ENDIF}
procedure MicroTilesSetSize(var MicroTiles: TMicroTiles; const DstRect: TRect); {$IFDEF USEINLINING}
//SAARIXX
{$IFNDEF DELPHI2005}
inline;
{$ENDIF}
{$ENDIF}
procedure MicroTilesClear(var MicroTiles: TMicroTiles; const Value: TMicroTile = MICROTILE_EMPTY); {$IFDEF USEINLINING} inline; {$ENDIF}
procedure MicroTilesClearUsed(var MicroTiles: TMicroTiles; const Value: TMicroTile = MICROTILE_EMPTY);
procedure MicroTilesCopy(var DstTiles: TMicroTiles; SrcTiles: TMicroTiles);
procedure MicroTilesAddLine(var MicroTiles: TMicroTiles; X1, Y1, X2, Y2: Integer; LineWidth: Integer; RoundToWholeTiles: Boolean = False);
procedure MicroTilesAddRect(var MicroTiles: TMicroTiles; Rect: TRect; RoundToWholeTiles: Boolean = False);
procedure MicroTilesUnion(var DstTiles: TMicroTiles; const SrcTiles: TMicroTiles; RoundToWholeTiles: Boolean = False);
function MicroTilesCalcRects(const MicroTiles: TMicroTiles; DstRects: TRectList; CountOnly: Boolean = False; RoundToWholeTiles: Boolean = False): Integer; overload;
function MicroTilesCalcRects(const MicroTiles: TMicroTiles; DstRects: TRectList; const Clip: TRect; CountOnly: Boolean = False; RoundToWholeTiles: Boolean = False): Integer; overload;
function MicroTilesCountEmptyTiles(const MicroTiles: TMicroTiles): Integer;
type
{ TMicroTilesMap }
{ associative array that is used to map Layers to their MicroTiles }
TMicroTilesMap = class(TPointerMap)
private
function GetData(Item: Pointer): PMicroTiles;
procedure SetData(Item: Pointer; const Data: PMicroTiles);
protected
function Delete(BucketIndex: Integer; ItemIndex: Integer): Pointer; override;
public
function Add(Item: Pointer): PPMicroTiles;
property Data[Item: Pointer]: PMicroTiles read GetData write SetData; default;
end;
type
{ TMicroTilesRepaintOptimizer }
{ Repaint manager that optimizes the repaint process using MicroTiles }
TMicroTilesRepaintOptimizer = class(TCustomRepaintOptimizer)
private
// working tiles
FBufferBounds: TRect;
FWorkMicroTiles: PMicroTiles; // used by DrawLayerToMicroTiles
FTempTiles: TMicroTiles;
FInvalidTiles: TMicroTiles;
FForcedInvalidTiles: TMicroTiles;
// list of invalid layers
FInvalidLayers: TList;
// association that maps layers to their old invalid tiles
FOldInvalidTilesMap: TMicroTilesMap;
FWorkingTilesValid: Boolean;
FOldInvalidTilesValid: Boolean;
FUseInvalidTiles: Boolean;
// adaptive stuff...
FAdaptiveMode: Boolean;
FPerfTimer: TPerfTimer;
FPerformanceLevel: Integer;
FElapsedTimeForLastRepaint: Int64;
FElapsedTimeForFullSceneRepaint: Int64;
FAdaptionFailed: Boolean;
// vars for time based approach
FTimedCheck: Boolean;
FTimeDelta: Integer;
FNextCheck: Integer;
FElapsedTimeOnLastPenalty: Int64;
// vars for invalid rect difference approach
FOldInvalidRectsCount: Integer;
{$IFDEF MICROTILES_DEBUGDRAW}
FDebugWholeTiles: Boolean;
FDebugMicroTiles: TMicroTiles;
FDebugInvalidRects: TRectList;
{$ENDIF}
procedure DrawLayerToMicroTiles(var DstTiles: TMicroTiles; Layer: TCustomLayer);
procedure DrawMeasuringHandler(Sender: TObject; const Area: TRect; const Info: Cardinal);
procedure ValidateWorkingTiles;
procedure UpdateOldInvalidTiles;
procedure SetAdaptiveMode(const Value: Boolean);
procedure ResetAdaptiveMode;
procedure BeginAdaption;
procedure EndAdaption;
procedure AddArea(var Tiles: TMicroTiles; const Area: TRect; const Info: Cardinal);
protected
procedure SetEnabled(const Value: Boolean); override;
// LayerCollection handler
procedure LayerCollectionNotifyHandler(Sender: TLayerCollection;
Action: TLayerListNotification; Layer: TCustomLayer; Index: Integer); override;
public
constructor Create(Buffer: TBitmap32; InvalidRects: TRectList); override;
destructor Destroy; override;
procedure RegisterLayerCollection(Layers: TLayerCollection); override;
procedure UnregisterLayerCollection(Layers: TLayerCollection); override;
procedure Reset; override;
function UpdatesAvailable: Boolean; override;
procedure PerformOptimization; override;
procedure BeginPaintBuffer; override;
procedure EndPaintBuffer; override;
// handlers
procedure AreaUpdateHandler(Sender: TObject; const Area: TRect; const Info: Cardinal); override;
procedure LayerUpdateHandler(Sender: TObject; Layer: TCustomLayer); override;
procedure BufferResizedHandler(const NewWidth, NewHeight: Integer); override;
// custom settings:
property AdaptiveMode: Boolean read FAdaptiveMode write SetAdaptiveMode;
end;
{$IFDEF CODESITE}
TDebugMicroTilesRepaintOptimizer = class(TMicroTilesRepaintOptimizer)
public
procedure Reset; override;
function UpdatesAvailable: Boolean; override;
procedure PerformOptimization; override;
procedure BeginPaintBuffer; override;
procedure EndPaintBuffer; override;
procedure AreaUpdateHandler(Sender: TObject; const Area: TRect; const Info: Cardinal); override;
procedure LayerUpdateHandler(Sender: TObject; Layer: TCustomLayer); override;
procedure BufferResizedHandler(const NewWidth, NewHeight: Integer); override;
end;
{$ENDIF}
procedure _MicroTileUnion(var DstTile: TMicroTile; const SrcTile: TMicroTile);
procedure M_MicroTileUnion(var DstTile: TMicroTile; const SrcTile: TMicroTile);
procedure _MicroTilesUnion(var DstTiles: TMicroTiles; const SrcTiles: TMicroTiles);
procedure M_MicroTilesUnion(var DstTiles: TMicroTiles; const SrcTiles: TMicroTiles);
implementation
uses
GR32_LowLevel, GR32_Math, Math;
var
MicroTilesU: procedure(var DstTiles: TMicroTiles; const SrcTiles: TMicroTiles);
{ MicroTile auxiliary routines }
function MakeMicroTile(const Left, Top, Right, Bottom: Integer): TMicroTile;
begin
Result := Left shl 24 or Top shl 16 or Right shl 8 or Bottom;
end;
function MicroTileHeight(const Tile: TMicroTile): Integer;
begin
Result := (Tile and $FF) - (Tile shr 16 and $FF);
end;
function MicroTileWidth(const Tile: TMicroTile): Integer;
begin
Result := (Tile shr 8 and $FF) - (Tile shr 24);
end;
procedure _MicroTileUnion(var DstTile: TMicroTile; const SrcTile: TMicroTile);
var
SrcLeft, SrcTop, SrcRight, SrcBottom: Integer;
begin
SrcLeft := SrcTile shr 24;
SrcTop := (SrcTile and $FF0000) shr 16;
SrcRight := (SrcTile and $FF00) shr 8;
SrcBottom := SrcTile and $FF;
if (DstTile <> MICROTILE_FULL) and (SrcTile <> MICROTILE_EMPTY) and
(SrcRight - SrcLeft <> 0) and (SrcBottom - SrcTop <> 0) then
begin
if (DstTile = MICROTILE_EMPTY) or (SrcTile = MICROTILE_FULL) then
DstTile := SrcTile
else
begin
DstTile := Min(DstTile shr 24, SrcLeft) shl 24 or
Min(DstTile shr 16 and $FF, SrcTop) shl 16 or
Max(DstTile shr 8 and $FF, SrcRight) shl 8 or
Max(DstTile and $FF, SrcBottom);
end;
end;
end;
procedure M_MicroTileUnion(var DstTile: TMicroTile; const SrcTile: TMicroTile);
var
SrcLeft, SrcTop, SrcRight, SrcBottom: Integer;
begin
SrcLeft := SrcTile shr 24;
SrcTop := (SrcTile and $FF0000) shr 16;
SrcRight := (SrcTile and $FF00) shr 8;
SrcBottom := SrcTile and $FF;
if (DstTile <> MICROTILE_FULL) and (SrcTile <> MICROTILE_EMPTY) and
(SrcRight - SrcLeft <> 0) and (SrcBottom - SrcTop <> 0) then
begin
if (DstTile = MICROTILE_EMPTY) or (SrcTile = MICROTILE_FULL) then
DstTile := SrcTile
else
asm
{$IFDEF COMPILER6}
MOVD MM1,[SrcTile]
{$ELSE}
MOV EAX,[SrcTile]
db $0F,$6E,$C8 /// MOVD MM1,EAX
{$ENDIF}
MOV EAX,[DstTile]
db $0F,$6E,$10 /// MOVD MM2, [EAX]
db $0F,$6F,$D9 /// MOVQ MM3, MM1
MOV ECX,$FFFF0000 // Mask
db $0F,$6E,$C1 /// MOVD MM0, ECX
db $0F,$DA,$CA /// PMINUB MM1, MM2
db $0F,$DB,$C8 /// PAND MM1, MM0
db $0F,$72,$D0,$10 /// PSRLD MM0, 16 // shift mask right by 16 bits
db $0F,$DE,$D3 /// PMAXUB MM2, MM3
db $0F,$DB,$D0 /// PAND MM2, MM0
db $0F,$EB,$CA /// POR MM1, MM2
db $0F,$7E,$08 /// MOVD [EAX], MM1
db $0F,$77 /// EMMS
end;
end;
end;
{ MicroTiles auxiliary routines }
function MakeEmptyMicroTiles: TMicroTiles;
begin
FillChar(Result, SizeOf(TMicroTiles), 0);
ReallocMem(Result.Tiles, 0);
end;
procedure MicroTilesCreate(var MicroTiles: TMicroTiles);
begin
FillChar(MicroTiles, SizeOf(TMicroTiles), 0);
ReallocMem(MicroTiles.Tiles, 0);
end;
procedure MicroTilesDestroy(var MicroTiles: TMicroTiles);
begin
ReallocMem(MicroTiles.Tiles, 0);
end;
procedure MicroTilesSetSize(var MicroTiles: TMicroTiles; const DstRect: TRect);
begin
MicroTiles.BoundsRect := DstRect;
MicroTiles.Columns := ((DstRect.Right - DstRect.Left) shr MICROTILE_SHIFT) + 1;
MicroTiles.Rows := ((DstRect.Bottom - DstRect.Top) shr MICROTILE_SHIFT) + 1;
MicroTiles.Count := (MicroTiles.Columns + 1) * (MicroTiles.Rows + 1);
ReallocMem(MicroTiles.Tiles, MicroTiles.Count * SizeOf(TMicroTile));
MicroTilesClear(MicroTiles)
end;
procedure MicroTilesClear(var MicroTiles: TMicroTiles; const Value: TMicroTile);
begin
MicroTiles.BoundsUsedTiles := MakeRect(MicroTiles.Columns, MicroTiles.Rows, 0, 0);
FillLongword(MicroTiles.Tiles^[0], MicroTiles.Count, Value);
end;
procedure MicroTilesClearUsed(var MicroTiles: TMicroTiles; const Value: TMicroTile);
var
I: Integer;
begin
for I := MicroTiles.BoundsUsedTiles.Top to MicroTiles.BoundsUsedTiles.Bottom do
FillLongword(MicroTiles.Tiles^[I * MicroTiles.Columns + MicroTiles.BoundsUsedTiles.Left],
MicroTiles.BoundsUsedTiles.Right - MicroTiles.BoundsUsedTiles.Left + 1, Value);
MicroTiles.BoundsUsedTiles := MakeRect(MicroTiles.Columns, MicroTiles.Rows, 0, 0);
end;
procedure MicroTilesCopy(var DstTiles: TMicroTiles; SrcTiles: TMicroTiles);
var
CurRow, Width: Integer;
SrcTilePtr, DstTilePtr: PMicroTile;
begin
if Assigned(DstTiles.Tiles) and (DstTiles.Count > 0) then
MicroTilesClearUsed(DstTiles);
DstTiles.BoundsRect := SrcTiles.BoundsRect;
DstTiles.Columns := SrcTiles.Columns;
DstTiles.Rows := SrcTiles.Rows;
DstTiles.BoundsUsedTiles := SrcTiles.BoundsUsedTiles;
ReallocMem(DstTiles.Tiles, SrcTiles.Count * SizeOf(TMicroTile));
if DstTiles.Count < SrcTiles.Count then
FillLongword(DstTiles.Tiles^[DstTiles.Count], SrcTiles.Count - DstTiles.Count, MICROTILE_EMPTY);
DstTiles.Count := SrcTiles.Count;
SrcTilePtr := @SrcTiles.Tiles^[SrcTiles.BoundsUsedTiles.Top * SrcTiles.Columns + SrcTiles.BoundsUsedTiles.Left];
DstTilePtr := @DstTiles.Tiles^[SrcTiles.BoundsUsedTiles.Top * DstTiles.Columns + SrcTiles.BoundsUsedTiles.Left];
Width := SrcTiles.BoundsUsedTiles.Right - SrcTiles.BoundsUsedTiles.Left + 1;
for CurRow := SrcTiles.BoundsUsedTiles.Top to SrcTiles.BoundsUsedTiles.Bottom do
begin
MoveLongword(SrcTilePtr^, DstTilePtr^, Width);
Inc(DstTilePtr, DstTiles.Columns);
Inc(SrcTilePtr, SrcTiles.Columns);
end
end;
procedure MicroTilesAddLine(var MicroTiles: TMicroTiles; X1, Y1, X2, Y2: Integer; LineWidth: Integer; RoundToWholeTiles: Boolean = False);
var
I: Integer;
Dx, Dy: Integer;
Sx, Sy: Integer;
DeltaX, DeltaY: Integer;
Rects: Integer;
NewX, NewY: Integer;
TempRect: TRect;
Swapped: Boolean;
begin
Dx := X2 - X1;
Dy := Y2 - Y1;
LineWidth := LineWidth shl 1;
if Dx > 0 then
Sx := 1
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -