123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722 |
- unit GR32_MicroTiles;
- (* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1 or LGPL 2.1 with linking exception
- *
- * 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.
- *
- * Alternatively, the contents of this file may be used under the terms of the
- * Free Pascal modified version of the GNU Lesser General Public License
- * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
- * of this license are applicable instead of those above.
- * Please see the file LICENSE.txt for additional information concerning this
- * license.
- *
- * The Original Code is MicroTiles Repaint Optimizer Extension for Graphics32
- *
- * The Initial Developer of the Original Code is
- * Andre Beckedorf - metaException
- * [email protected]
- *
- * Portions created by the Initial Developer are Copyright (C) 2005-2009
- * 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 FPC}
- Types,
- {$IFDEF Windows}
- Windows,
- {$ENDIF}
- {$ELSE}
- Windows,
- {$ENDIF}
- {$IFDEF CODESITE}
- CSIntf, CSAux,
- {$ENDIF}
- {$IFDEF COMPILER2005_UP}
- 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
- MicroTileSize = MaxInt div 16;
- {$IFDEF MICROTILES_DEBUGDRAW}
- clDebugDrawFill = TColor32($30FF0000);
- clDebugDrawFrame = TColor32($90FF0000);
- {$ENDIF}
- type
- PMicroTile = ^TMicroTile;
- TMicroTile = type Integer;
- PMicroTileArray = ^TMicroTileArray;
- TMicroTileArray = array[0..MicroTileSize - 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);
- 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}
- implementation
- uses
- GR32_Bindings, 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_Pas(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;
- {$IFDEF TARGET_x86}
- procedure MicroTileUnion_EMMX(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
- MOVD MM1,[SrcTile]
- MOV EAX,[DstTile]
- MOVD MM2, [EAX]
- MOVQ MM3, MM1
- MOV ECX,$FFFF0000 // Mask
- MOVD MM0, ECX
- PMINUB MM1, MM2
- PAND MM1, MM0
- PSRLD MM0, 16 // shift mask right by 16 bits
- PMAXUB MM2, MM3
- PAND MM2, MM0
- POR MM1, MM2
- MOVD [EAX], MM1
- EMMS
- end;
- end;
- end;
- {$ENDIF}
- { 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 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 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 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;
- DeltaX, DeltaY: Integer;
- SignX, SignY: Integer;
- Rects: Integer;
- NewX, NewY: Integer;
- TempRect: TRect;
- begin
- LineWidth := (LineWidth + 1) shr 1; // Half line width rounded up
- DeltaX := X2 - X1;
- if DeltaX < 0 then
- begin
- // Make sure DeltaX*Sign is positive
- Swap(X1, X2);
- Swap(Y1, Y2);
- DeltaX := -DeltaX;
- SignX := 1
- end else
- if DeltaX > 0 then
- SignX := 1
- else // DeltaX = 0
- begin
- TempRect := MakeRect(X1, Y1, X2, Y2);
- InflateArea(TempRect, LineWidth, LineWidth);
- MicroTilesAddRect(MicroTiles, TempRect, RoundToWholeTiles);
- Exit;
- end;
- DeltaY := Y2 - Y1;
- if DeltaY > 0 then
- SignY := 1
- else
- if DeltaY < 0 then
- begin
- DeltaY := -DeltaY;
- SignY := -1;
- end else // DeltaY = 0
- begin
- TempRect := MakeRect(X1, Y1, X2, Y2);
- InflateArea(TempRect, LineWidth, LineWidth);
- MicroTilesAddRect(MicroTiles, TempRect, RoundToWholeTiles);
- Exit;
- end;
- X1 := X1 * FixedOne;
- Y1 := Y1 * FixedOne;
- DeltaX := DeltaX * FixedOne;
- DeltaY := DeltaY * FixedOne;
- if DeltaX >= DeltaY then
- begin
- Rects := DeltaX div MICROTILE_SIZE;
- DeltaX := SignX * MICROTILE_SIZE * FixedOne;
- DeltaY := SignY * FixedDiv(DeltaY, Rects);
- end else
- begin
- Rects := DeltaY div MICROTILE_SIZE;
- DeltaY := SignY * MICROTILE_SIZE * FixedOne;
- DeltaX := SignX * FixedDiv(DeltaX, Rects);
- end;
- for i := 1 to FixedCeil(Rects) do
- begin
- NewX := X1 + DeltaX;
- NewY := Y1 + DeltaY;
- // Make sure rect is positive or MakeRect will not round correctly
- if (SignY >= 0) then
- TempRect := MakeRect(FixedRect(X1, Y1, NewX, NewY), rrOutside)
- else
- TempRect := MakeRect(FixedRect(X1, NewY, NewX, Y1), rrOutside);
- InflateArea(TempRect, LineWidth, LineWidth);
- MicroTilesAddRect(MicroTiles, TempRect, RoundToWholeTiles);
- X1 := NewX;
- Y1 := NewY;
- end;
- end;
- procedure MicroTilesAddRect(var MicroTiles: TMicroTiles; Rect: TRect; RoundToWholeTiles: Boolean);
- var
- ModLeft, ModRight, ModTop, ModBottom, Temp: Integer;
- LeftTile, TopTile, RightTile, BottomTile, ColSpread, RowSpread: Integer;
- CurRow, CurCol: Integer;
- TilePtr, TilePtr2: PMicroTile;
- begin
- if MicroTiles.Count = 0 then Exit;
- with Rect do
- begin
- TestSwap(Left, Right);
- TestSwap(Top, Bottom);
- if Left < 0 then Left := 0;
- if Top < 0 then Top := 0;
- Temp := MicroTiles.Columns shl MICROTILE_SHIFT;
- if Right > Temp then Right := Temp;
- Temp := MicroTiles.Rows shl MICROTILE_SHIFT;
- if Bottom > Temp then Bottom := Temp;
- if (Left > Right) or (Top > Bottom) then Exit;
- end;
- LeftTile := Rect.Left shr MICROTILE_SHIFT;
- TopTile := Rect.Top shr MICROTILE_SHIFT;
- RightTile := Rect.Right shr MICROTILE_SHIFT;
- BottomTile := Rect.Bottom shr MICROTILE_SHIFT;
- TilePtr := @MicroTiles.Tiles^[TopTile * MicroTiles.Columns + LeftTile];
- if RoundToWholeTiles then
- begin
- for CurRow := TopTile to BottomTile do
- begin
- FillLongword(TilePtr^, RightTile - LeftTile + 1, MICROTILE_FULL);
- Inc(TilePtr, MicroTiles.Columns);
- end;
- end
- else
- begin
- // calculate number of tiles needed in columns and rows
- ColSpread := ((Rect.Right + MICROTILE_SIZE) shr MICROTILE_SHIFT) -
- (Rect.Left shr MICROTILE_SHIFT);
- RowSpread := ((Rect.Bottom + MICROTILE_SIZE) shr MICROTILE_SHIFT) -
- (Rect.Top shr MICROTILE_SHIFT);
- ModLeft := Rect.Left mod MICROTILE_SIZE;
- ModTop := Rect.Top mod MICROTILE_SIZE;
- ModRight := Rect.Right mod MICROTILE_SIZE;
- ModBottom := Rect.Bottom mod MICROTILE_SIZE;
- if (ColSpread = 1) and (RowSpread = 1) then
- MicroTileUnion(TilePtr^, MakeMicroTile(ModLeft, ModTop, ModRight, ModBottom))
- else if ColSpread = 1 then
- begin
- MicroTileUnion(TilePtr^, MakeMicroTile(ModLeft, ModTop, ModRight, MICROTILE_SIZE));
- Inc(TilePtr, MicroTiles.Columns);
- if RowSpread > 2 then
- for CurCol := TopTile + 1 to BottomTile - 1 do
- begin
- MicroTileUnion(TilePtr^, MakeMicroTile(ModLeft, 0, ModRight, MICROTILE_SIZE));
- Inc(TilePtr, MicroTiles.Columns);
- end;
- MicroTileUnion(TilePtr^, MakeMicroTile(ModLeft, 0, ModRight, ModBottom));
- end
- else if RowSpread = 1 then
- begin
- MicroTileUnion(TilePtr^, MakeMicroTile(ModLeft, ModTop, MICROTILE_SIZE, ModBottom));
- Inc(TilePtr);
- if ColSpread > 2 then
- for CurRow := LeftTile + 1 to RightTile - 1 do
- begin
- MicroTileUnion(TilePtr^, MakeMicroTile(0, ModTop, MICROTILE_SIZE, ModBottom));
- Inc(TilePtr);
- end;
- MicroTileUnion(TilePtr^, MakeMicroTile(0, ModTop, ModRight, ModBottom));
- end
- else
- begin
- TilePtr2 := TilePtr;
- // TOP:
- // render top-left corner
- MicroTileUnion(TilePtr2^, MakeMicroTile(ModLeft, ModTop, MICROTILE_SIZE, MICROTILE_SIZE));
- Inc(TilePtr2);
- // render top edge
- if ColSpread > 2 then
- for CurRow := LeftTile + 1 to RightTile - 1 do
- begin
- MicroTileUnion(TilePtr2^, MakeMicroTile(0, ModTop, MICROTILE_SIZE, MICROTILE_SIZE));
- Inc(TilePtr2);
- end;
- // render top-right corner
- MicroTileUnion(TilePtr2^, MakeMicroTile(0, ModTop, ModRight, MICROTILE_SIZE));
- Inc(TilePtr, MicroTiles.Columns);
- // INTERMEDIATE AREA:
- if RowSpread > 2 then
- for CurCol := TopTile + 1 to BottomTile - 1 do
- begin
- TilePtr2 := TilePtr;
- // render left edge
- MicroTileUnion(TilePtr2^, MakeMicroTile(ModLeft, 0, MICROTILE_SIZE, MICROTILE_SIZE));
- Inc(TilePtr2);
- // render content
- if ColSpread > 2 then
- begin
- FillLongword(TilePtr2^, RightTile - LeftTile - 1, MICROTILE_FULL);
- Inc(TilePtr2, RightTile - LeftTile - 1);
- end;
- // render right edge
- MicroTileUnion(TilePtr2^, MakeMicroTile(0, 0, ModRight, MICROTILE_SIZE));
- Inc(TilePtr, MicroTiles.Columns);
- end;
- TilePtr2 := TilePtr;
- // BOTTOM:
- // render bottom-left corner
- MicroTileUnion(TilePtr2^, MakeMicroTile(ModLeft, 0, MICROTILE_SIZE, ModBottom));
- Inc(TilePtr2);
- // render bottom edge
- if ColSpread > 2 then
- for CurRow := LeftTile + 1 to RightTile - 1 do
- begin
- MicroTileUnion(TilePtr2^, MakeMicroTile(0, 0, MICROTILE_SIZE, ModBottom));
- Inc(TilePtr2);
- end;
- // render bottom-right corner
- MicroTileUnion(TilePtr2^, MakeMicroTile(0, 0, ModRight, ModBottom));
- end;
- end;
- with MicroTiles.BoundsUsedTiles do
- begin
- if LeftTile < Left then Left := LeftTile;
- if TopTile < Top then Top := TopTile;
- if RightTile > Right then Right := RightTile;
- if BottomTile > Bottom then Bottom := BottomTile;
- end;
- end;
- procedure MicroTilesUnion_Pas(var DstTiles: TMicroTiles; const SrcTiles: TMicroTiles);
- var
- SrcTilePtr, DstTilePtr: PMicroTile;
- SrcTilePtr2, DstTilePtr2: PMicroTile;
- X, Y: Integer;
- SrcLeft, SrcTop, SrcRight, SrcBottom: Integer;
- SrcTile: TMicroTile;
- begin
- SrcTilePtr := @SrcTiles.Tiles^[SrcTiles.BoundsUsedTiles.Top * SrcTiles.Columns + SrcTiles.BoundsUsedTiles.Left];
- DstTilePtr := @DstTiles.Tiles^[SrcTiles.BoundsUsedTiles.Top * DstTiles.Columns + SrcTiles.BoundsUsedTiles.Left];
- for Y := SrcTiles.BoundsUsedTiles.Top to SrcTiles.BoundsUsedTiles.Bottom do
- begin
- SrcTilePtr2 := SrcTilePtr;
- DstTilePtr2 := DstTilePtr;
- for X := SrcTiles.BoundsUsedTiles.Left to SrcTiles.BoundsUsedTiles.Right do
- begin
- SrcTile := SrcTilePtr2^;
- SrcLeft := SrcTile shr 24;
- SrcTop := (SrcTile and $FF0000) shr 16;
- SrcRight := (SrcTile and $FF00) shr 8;
- SrcBottom := SrcTile and $FF;
- if (DstTilePtr2^ <> MICROTILE_FULL) and (SrcTilePtr2^ <> MICROTILE_EMPTY) and
- (SrcRight - SrcLeft <> 0) and (SrcBottom - SrcTop <> 0) then
- begin
- if (DstTilePtr2^ = MICROTILE_EMPTY) or (SrcTilePtr2^ = MICROTILE_FULL) then
- DstTilePtr2^ := SrcTilePtr2^
- else
- DstTilePtr2^ := Min(DstTilePtr2^ shr 24, SrcLeft) shl 24 or
- Min(DstTilePtr2^ shr 16 and $FF, SrcTop) shl 16 or
- Max(DstTilePtr2^ shr 8 and $FF, SrcRight) shl 8 or
- Max(DstTilePtr2^ and $FF, SrcBottom);
- end;
- Inc(DstTilePtr2);
- Inc(SrcTilePtr2);
- end;
- Inc(DstTilePtr, DstTiles.Columns);
- Inc(SrcTilePtr, SrcTiles.Columns);
- end;
- end;
- {$IFDEF TARGET_x86}
- procedure MicroTilesUnion_EMMX(var DstTiles: TMicroTiles; const SrcTiles: TMicroTiles);
- var
- SrcTilePtr, DstTilePtr: PMicroTile;
- SrcTilePtr2, DstTilePtr2: PMicroTile;
- X, Y: Integer;
- SrcLeft, SrcTop, SrcRight, SrcBottom: Integer;
- begin
- SrcTilePtr := @SrcTiles.Tiles^[SrcTiles.BoundsUsedTiles.Top * SrcTiles.Columns + SrcTiles.BoundsUsedTiles.Left];
- DstTilePtr := @DstTiles.Tiles^[SrcTiles.BoundsUsedTiles.Top * DstTiles.Columns + SrcTiles.BoundsUsedTiles.Left];
- asm
- MOV ECX, $FFFF // Mask
- MOVD MM0, ECX
- MOVQ MM4, MM0
- PSLLD MM4, 16 // shift mask left by 16 bits
- end;
- for Y := SrcTiles.BoundsUsedTiles.Top to SrcTiles.BoundsUsedTiles.Bottom do
- begin
- SrcTilePtr2 := SrcTilePtr;
- DstTilePtr2 := DstTilePtr;
- for X := SrcTiles.BoundsUsedTiles.Left to SrcTiles.BoundsUsedTiles.Right do
- begin
- SrcLeft := SrcTilePtr2^ shr 24;
- SrcTop := (SrcTilePtr2^ and $FF0000) shr 16;
- SrcRight := (SrcTilePtr2^ and $FF00) shr 8;
- SrcBottom := SrcTilePtr2^ and $FF;
- if (DstTilePtr2^ <> MICROTILE_FULL) and (SrcTilePtr2^ <> MICROTILE_EMPTY) and
- (SrcRight - SrcLeft <> 0) and (SrcBottom - SrcTop <> 0) then
- begin
- if (DstTilePtr2^ = MICROTILE_EMPTY) or (SrcTilePtr2^ = MICROTILE_FULL) then
- DstTilePtr2^ := SrcTilePtr2^
- else
- asm
- MOV EAX, [DstTilePtr2]
- MOVD MM2, [EAX]
- MOV ECX, [SrcTilePtr2]
- MOVD MM1, [ECX]
- MOVQ MM3, MM1
- PMINUB MM1, MM2
- PAND MM1, MM4
- PMAXUB MM2, MM3
- PAND MM2, MM0
- POR MM1, MM2
- MOVD [EAX], MM1
- end;
- end;
- Inc(DstTilePtr2);
- Inc(SrcTilePtr2);
- end;
- Inc(DstTilePtr, DstTiles.Columns);
- Inc(SrcTilePtr, SrcTiles.Columns);
- end;
- asm
- db $0F,$77 /// EMMS
- end;
- end;
- {$ENDIF}
- procedure MicroTilesUnion(var DstTiles: TMicroTiles; const SrcTiles: TMicroTiles; RoundToWholeTiles: Boolean);
- var
- SrcTilePtr, DstTilePtr: PMicroTile;
- SrcTilePtr2, DstTilePtr2: PMicroTile;
- X, Y: Integer;
- SrcLeft, SrcTop, SrcRight, SrcBottom: Integer;
- begin
- if SrcTiles.Count = 0 then Exit;
- if RoundToWholeTiles then
- begin
- SrcTilePtr := @SrcTiles.Tiles^[SrcTiles.BoundsUsedTiles.Top * SrcTiles.Columns + SrcTiles.BoundsUsedTiles.Left];
- DstTilePtr := @DstTiles.Tiles^[SrcTiles.BoundsUsedTiles.Top * DstTiles.Columns + SrcTiles.BoundsUsedTiles.Left];
- for Y := SrcTiles.BoundsUsedTiles.Top to SrcTiles.BoundsUsedTiles.Bottom do
- begin
- SrcTilePtr2 := SrcTilePtr;
- DstTilePtr2 := DstTilePtr;
- for X := SrcTiles.BoundsUsedTiles.Left to SrcTiles.BoundsUsedTiles.Right do
- begin
- SrcLeft := SrcTilePtr2^ shr 24;
- SrcTop := (SrcTilePtr2^ and $FF0000) shr 16;
- SrcRight := (SrcTilePtr2^ and $FF00) shr 8;
- SrcBottom := SrcTilePtr2^ and $FF;
- if (DstTilePtr2^ <> MICROTILE_FULL) and (SrcTilePtr2^ <> MICROTILE_EMPTY) and
- (SrcRight - SrcLeft <> 0) and (SrcBottom - SrcTop <> 0) then
- DstTilePtr2^ := MICROTILE_FULL;
- Inc(DstTilePtr2);
- Inc(SrcTilePtr2);
- end;
- Inc(DstTilePtr, DstTiles.Columns);
- Inc(SrcTilePtr, SrcTiles.Columns);
- end
- end
- else
- MicroTilesU(DstTiles, SrcTiles);
- with DstTiles.BoundsUsedTiles do
- begin
- if SrcTiles.BoundsUsedTiles.Left < Left then Left := SrcTiles.BoundsUsedTiles.Left;
- if SrcTiles.BoundsUsedTiles.Top < Top then Top := SrcTiles.BoundsUsedTiles.Top;
- if SrcTiles.BoundsUsedTiles.Right > Right then Right := SrcTiles.BoundsUsedTiles.Right;
- if SrcTiles.BoundsUsedTiles.Bottom > Bottom then Bottom := SrcTiles.BoundsUsedTiles.Bottom;
- end;
- end;
- function MicroTilesCalcRects(const MicroTiles: TMicroTiles; DstRects: TRectList;
- CountOnly, RoundToWholeTiles: Boolean): Integer;
- begin
- Result := MicroTilesCalcRects(MicroTiles, DstRects, MicroTiles.BoundsRect, CountOnly);
- end;
- function MicroTilesCalcRects(const MicroTiles: TMicroTiles; DstRects: TRectList;
- const Clip: TRect; CountOnly, RoundToWholeTiles: Boolean): Integer;
- var
- Rects: Array Of TRect;
- Rect: PRect;
- CombLUT: Array Of Integer;
- StartIndex: Integer;
- CurTile, TempTile: TMicroTile;
- Temp: Integer;
- NewLeft, NewTop, NewRight, NewBottom: Integer;
- CurCol, CurRow, I, RectsCount: Integer;
- begin
- Result := 0;
- if (MicroTiles.Count = 0) or
- (MicroTiles.BoundsUsedTiles.Right - MicroTiles.BoundsUsedTiles.Left < 0) or
- (MicroTiles.BoundsUsedTiles.Bottom - MicroTiles.BoundsUsedTiles.Top < 0) then Exit;
- SetLength(Rects, MicroTiles.Columns * MicroTiles.Rows);
- SetLength(CombLUT, MicroTiles.Columns * MicroTiles.Rows);
- FillLongword(CombLUT[0], Length(CombLUT), Cardinal(-1));
- I := 0;
- RectsCount := 0;
- if not RoundToWholeTiles then
- for CurRow := 0 to MicroTiles.Rows - 1 do
- begin
- CurCol := 0;
- while CurCol < MicroTiles.Columns do
- begin
- CurTile := MicroTiles.Tiles[I];
- if CurTile <> MICROTILE_EMPTY then
- begin
- Temp := CurRow shl MICROTILE_SHIFT;
- NewTop := Constrain(Temp + CurTile shr 16 and $FF, Clip.Top, Clip.Bottom);
- NewBottom := Constrain(Temp + CurTile and $FF, Clip.Top, Clip.Bottom);
- NewLeft := Constrain(CurCol shl MICROTILE_SHIFT + CurTile shr 24, Clip.Left, Clip.Right);
- StartIndex := I;
- if (CurTile shr 8 and $FF = MICROTILE_SIZE) and (CurCol <> MicroTiles.Columns - 1) then
- begin
- while True do
- begin
- Inc(CurCol);
- Inc(I);
- TempTile := MicroTiles.Tiles[I];
- if (CurCol = MicroTiles.Columns) or
- (TempTile shr 16 and $FF <> CurTile shr 16 and $FF) or
- (TempTile and $FF <> CurTile and $FF) or
- (TempTile shr 24 <> 0) then
- begin
- Dec(CurCol);
- Dec(I);
- Break;
- end;
- end;
- end;
- NewRight := Constrain(CurCol shl MICROTILE_SHIFT + MicroTiles.Tiles[I] shr 8 and $FF, Clip.Left, Clip.Right);
- Temp := CombLUT[StartIndex];
- Rect := nil;
- if Temp <> -1 then Rect := @Rects[Temp];
- if Assigned(Rect) and
- (Rect.Left = NewLeft) and
- (Rect.Right = NewRight) and
- (Rect.Bottom = NewTop) then
- begin
- Rect.Bottom := NewBottom;
- if CurRow <> MicroTiles.Rows - 1 then
- CombLUT[StartIndex + MicroTiles.Columns] := Temp;
- end
- else
- with Rects[RectsCount] do
- begin
- Left := NewLeft; Top := NewTop;
- Right := NewRight; Bottom := NewBottom;
- if CurRow <> MicroTiles.Rows - 1 then
- CombLUT[StartIndex + MicroTiles.Columns] := RectsCount;
- Inc(RectsCount);
- end;
- end;
- Inc(I);
- Inc(CurCol);
- end;
- end
- else
- for CurRow := 0 to MicroTiles.Rows - 1 do
- begin
- CurCol := 0;
- while CurCol < MicroTiles.Columns do
- begin
- CurTile := MicroTiles.Tiles[I];
- if CurTile <> MICROTILE_EMPTY then
- begin
- Temp := CurRow shl MICROTILE_SHIFT;
- NewTop := Constrain(Temp, Clip.Top, Clip.Bottom);
- NewBottom := Constrain(Temp + MICROTILE_SIZE, Clip.Top, Clip.Bottom);
- NewLeft := Constrain(CurCol shl MICROTILE_SHIFT, Clip.Left, Clip.Right);
- StartIndex := I;
- if CurCol <> MicroTiles.Columns - 1 then
- begin
- while True do
- begin
- Inc(CurCol);
- Inc(I);
- TempTile := MicroTiles.Tiles[I];
- if (CurCol = MicroTiles.Columns) or (TempTile = MICROTILE_EMPTY) then
- begin
- Dec(CurCol);
- Dec(I);
- Break;
- end;
- end;
- end;
- NewRight := Constrain(CurCol shl MICROTILE_SHIFT + MICROTILE_SIZE, Clip.Left, Clip.Right);
- Temp := CombLUT[StartIndex];
- Rect := nil;
- if Temp <> -1 then Rect := @Rects[Temp];
- if Assigned(Rect) and
- (Rect.Left = NewLeft) and
- (Rect.Right = NewRight) and
- (Rect.Bottom = NewTop) then
- begin
- Rect.Bottom := NewBottom;
- if CurRow <> MicroTiles.Rows - 1 then
- CombLUT[StartIndex + MicroTiles.Columns] := Temp;
- end
- else
- with Rects[RectsCount] do
- begin
- Left := NewLeft; Top := NewTop;
- Right := NewRight; Bottom := NewBottom;
- if CurRow <> MicroTiles.Rows - 1 then
- CombLUT[StartIndex + MicroTiles.Columns] := RectsCount;
- Inc(RectsCount);
- end;
- end;
- Inc(I);
- Inc(CurCol);
- end;
- end;
- Result := RectsCount;
- if not CountOnly then
- for I := 0 to RectsCount - 1 do DstRects.Add(Rects[I]);
- end;
- function MicroTilesCountEmptyTiles(const MicroTiles: TMicroTiles): Integer;
- var
- CurRow, CurCol: Integer;
- TilePtr: PMicroTile;
- begin
- Result := 0;
- if MicroTiles.Count > 0 then
- begin
- TilePtr := @MicroTiles.Tiles^[0];
- for CurRow := 0 to MicroTiles.Rows - 1 do
- for CurCol := 0 to MicroTiles.Columns - 1 do
- begin
- if TilePtr^ = MICROTILE_EMPTY then Inc(Result);
- Inc(TilePtr);
- end;
- end;
- end;
- {$IFDEF MICROTILES_DEBUGDRAW}
- procedure MicroTilesDebugDraw(const MicroTiles: TMicroTiles; DstBitmap: TBitmap32; DrawOptimized, RoundToWholeTiles: Boolean);
- var
- I: Integer;
- TempRect: TRect;
- Rects: TRectList;
- C1, C2: TColor32;
- begin
- {$IFDEF MICROTILES_DEBUGDRAW_RANDOM_COLORS}
- C1 := Random(MaxInt) AND $00FFFFFF;
- C2 := C1 OR $90000000;
- C1 := C1 OR $30000000;
- {$ELSE}
- C1 := clDebugDrawFill;
- C2 := clDebugDrawFrame;
- {$ENDIF}
- if DrawOptimized then
- begin
- Rects := TRectList.Create;
- MicroTilesCalcRects(MicroTiles, Rects, False, RoundToWholeTiles);
- try
- if Rects.Count > 0 then
- begin
- for I := 0 to Rects.Count - 1 do
- begin
- DstBitmap.FillRectTS(Rects[I]^, C1);
- DstBitmap.FrameRectTS(Rects[I]^, C2);
- end;
- end
- finally
- Rects.Free;
- end;
- end
- else
- for I := 0 to MicroTiles.Count - 1 do
- begin
- if MicroTiles.Tiles^[i] <> MICROTILE_EMPTY then
- begin
- TempRect.Left := ((I mod MicroTiles.Columns) shl MICROTILE_SHIFT) + (MicroTiles.Tiles[i] shr 24);
- TempRect.Top := ((I div MicroTiles.Columns) shl MICROTILE_SHIFT) + (MicroTiles.Tiles[i] shr 16 and $FF);
- TempRect.Right := ((I mod MicroTiles.Columns) shl MICROTILE_SHIFT) + (MicroTiles.Tiles[i] shr 8 and $FF);
- TempRect.Bottom := ((I div MicroTiles.Columns) shl MICROTILE_SHIFT) + (MicroTiles.Tiles[i] and $FF);
- DstBitmap.FillRectTS(TempRect, C1);
- DstBitmap.FrameRectTS(TempRect, C2);
- end;
- end;
- end;
- {$ENDIF}
- { TMicroTilesMap }
- function TMicroTilesMap.Add(Item: Pointer): PPMicroTiles;
- var
- TilesPtr: PMicroTiles;
- IsNew: Boolean;
- begin
- Result := PPMicroTiles(inherited Add(Item, IsNew));
- if IsNew then
- begin
- New(TilesPtr);
- MicroTilesCreate(TilesPtr^);
- Result^ := TilesPtr;
- end;
- end;
- function TMicroTilesMap.Delete(BucketIndex, ItemIndex: Integer): Pointer;
- var
- TilesPtr: PMicroTiles;
- begin
- TilesPtr := inherited Delete(BucketIndex, ItemIndex);
- MicroTilesDestroy(TilesPtr^);
- Dispose(TilesPtr);
- Result := nil;
- end;
- procedure TMicroTilesMap.SetData(Item: Pointer; const Data: PMicroTiles);
- begin
- inherited SetData(Item, Data);
- end;
- function TMicroTilesMap.GetData(Item: Pointer): PMicroTiles;
- begin
- Result := inherited GetData(Item);
- end;
- { TMicroTilesRepaintManager }
- type
- TLayerCollectionAccess = class(TLayerCollection);
- TCustomLayerAccess = class(TCustomLayer);
- const
- PL_MICROTILES = 0;
- PL_WHOLETILES = 1;
- PL_FULLSCENE = 2;
- TIMER_PENALTY = 250;
- TIMER_LOWLIMIT = 1000;
- TIMER_HIGHLIMIT = 5000;
- INVALIDRECTS_DELTA = 10;
- constructor TMicroTilesRepaintOptimizer.Create(Buffer: TBitmap32; InvalidRects: TRectList);
- begin
- inherited;
- FOldInvalidTilesMap := TMicroTilesMap.Create;
- FInvalidLayers := TList.Create;
- FPerfTimer := TPerfTimer.Create;
- {$IFNDEF MICROTILES_DEBUGDRAW}
- {$IFNDEF MICROTILES_NO_ADAPTION}
- FAdaptiveMode := True;
- {$ENDIF}
- {$ENDIF}
- MicroTilesCreate(FInvalidTiles);
- MicroTilesCreate(FTempTiles);
- MicroTilesCreate(FForcedInvalidTiles);
- {$IFDEF MICROTILES_DEBUGDRAW}
- MicroTilesCreate(FDebugMicroTiles);
- FDebugInvalidRects := TRectList.Create;
- {$ENDIF}
- end;
- destructor TMicroTilesRepaintOptimizer.Destroy;
- begin
- MicroTilesDestroy(FForcedInvalidTiles);
- MicroTilesDestroy(FTempTiles);
- MicroTilesDestroy(FInvalidTiles);
- FPerfTimer.Free;
- FInvalidLayers.Free;
- FOldInvalidTilesMap.Free;
- {$IFDEF MICROTILES_DEBUGDRAW}
- FDebugInvalidRects.Free;
- MicroTilesDestroy(FDebugMicroTiles);
- {$ENDIF}
- inherited;
- end;
- procedure TMicroTilesRepaintOptimizer.AreaUpdateHandler(Sender: TObject; const Area: TRect;
- const Info: Cardinal);
- begin
- ValidateWorkingTiles;
- AddArea(FForcedInvalidTiles, Area, Info);
- FUseInvalidTiles := True;
- end;
- procedure TMicroTilesRepaintOptimizer.AddArea(var Tiles: TMicroTiles; const Area: TRect;
- const Info: Cardinal);
- var
- LineWidth: Integer;
- begin
- if Info and AREAINFO_LINE <> 0 then
- begin
- LineWidth := Info and $00FFFFFF;
- with Area do
- MicroTilesAddLine(Tiles, Left, Top, Right, Bottom, LineWidth, FPerformanceLevel > PL_MICROTILES);
- end
- else
- MicroTilesAddRect(Tiles, Area, FPerformanceLevel > PL_MICROTILES);
- end;
- procedure TMicroTilesRepaintOptimizer.LayerUpdateHandler(Sender: TObject; Layer: TCustomLayer);
- begin
- if FOldInvalidTilesValid and not TCustomLayerAccess(Layer).Invalid then
- begin
- FInvalidLayers.Add(Layer);
- TCustomLayerAccess(Layer).Invalid := True;
- FUseInvalidTiles := True;
- end;
- end;
- procedure TMicroTilesRepaintOptimizer.LayerCollectionNotifyHandler(Sender: TLayerCollection;
- Action: TLayerListNotification; Layer: TCustomLayer; Index: Integer);
- var
- TilesPtr: PMicroTiles;
- begin
- case Action of
- lnLayerAdded, lnLayerInserted:
- begin
- TilesPtr := FOldInvalidTilesMap.Add(Layer)^;
- MicroTilesSetSize(TilesPtr^, Buffer.BoundsRect);
- FOldInvalidTilesValid := True;
- end;
- lnLayerDeleted:
- begin
- if FOldInvalidTilesValid then
- begin
- // force repaint of tiles that the layer did previously allocate
- MicroTilesUnion(FInvalidTiles, FOldInvalidTilesMap[Layer]^);
- FUseInvalidTiles := True;
- end;
- FInvalidLayers.Remove(Layer);
- FOldInvalidTilesMap.Remove(Layer);
- end;
- lnCleared:
- begin
- if FOldInvalidTilesValid then
- begin
- with TPointerMapIterator.Create(FOldInvalidTilesMap) do
- try
- while Next do
- MicroTilesUnion(FInvalidTiles, PMicroTiles(Data)^);
- finally
- Free;
- end;
- FUseInvalidTiles := True;
- ResetAdaptiveMode;
- end;
- FOldInvalidTilesMap.Clear;
- FOldInvalidTilesValid := True;
- end;
- end;
- end;
- procedure TMicroTilesRepaintOptimizer.ValidateWorkingTiles;
- begin
- if not FWorkingTilesValid then // check if working microtiles need resize...
- begin
- MicroTilesSetSize(FTempTiles, FBufferBounds);
- MicroTilesSetSize(FInvalidTiles, FBufferBounds);
- MicroTilesSetSize(FForcedInvalidTiles, FBufferBounds);
- FWorkingTilesValid := True;
- end;
- end;
- procedure TMicroTilesRepaintOptimizer.BufferResizedHandler(const NewWidth, NewHeight: Integer);
- begin
- FBufferBounds := MakeRect(0, 0, NewWidth, NewHeight);
- Reset;
- end;
- procedure TMicroTilesRepaintOptimizer.Reset;
- begin
- FWorkingTilesValid := False; // force resizing of working microtiles
- FOldInvalidTilesValid := False; // force resizing and rerendering of invalid tiles
- UpdateOldInvalidTiles;
- // mark whole buffer area invalid...
- MicroTilesClear(FForcedInvalidTiles, MICROTILE_FULL);
- FForcedInvalidTiles.BoundsUsedTiles := MakeRect(0, 0, FForcedInvalidTiles.Columns, FForcedInvalidTiles.Rows);
- FUseInvalidTiles := True;
- end;
- function TMicroTilesRepaintOptimizer.UpdatesAvailable: Boolean;
- begin
- UpdateOldInvalidTiles;
- Result := FUseInvalidTiles;
- end;
- procedure TMicroTilesRepaintOptimizer.UpdateOldInvalidTiles;
- var
- I, J: Integer;
- TilesPtr: PMicroTiles;
- Layer: TCustomLayer;
- begin
- if not FOldInvalidTilesValid then // check if old Invalid tiles need resize and rerendering...
- begin
- ValidateWorkingTiles;
- for I := 0 to LayerCollections.Count - 1 do
- with TLayerCollection(LayerCollections[I]) do
- for J := 0 to Count - 1 do
- begin
- Layer := Items[J];
- TilesPtr := FOldInvalidTilesMap.Add(Layer)^;
- MicroTilesSetSize(TilesPtr^, FBufferBounds);
- DrawLayerToMicroTiles(TilesPtr^, Layer);
- TCustomLayerAccess(Layer).Invalid := False;
- end;
- FInvalidLayers.Clear;
- FOldInvalidTilesValid := True;
- FUseInvalidTiles := False;
- end;
- end;
- procedure TMicroTilesRepaintOptimizer.RegisterLayerCollection(Layers: TLayerCollection);
- begin
- inherited;
- if Enabled then
- with TLayerCollectionAccess(Layers) do
- begin
- OnLayerUpdated := LayerUpdateHandler;
- OnAreaUpdated := AreaUpdateHandler;
- OnListNotify := LayerCollectionNotifyHandler;
- end;
- end;
- procedure TMicroTilesRepaintOptimizer.UnregisterLayerCollection(Layers: TLayerCollection);
- begin
- with TLayerCollectionAccess(Layers) do
- begin
- OnLayerUpdated := nil;
- OnAreaUpdated := nil;
- OnListNotify := nil;
- end;
- inherited;
- end;
- procedure TMicroTilesRepaintOptimizer.SetEnabled(const Value: Boolean);
- var
- I: Integer;
- begin
- if Value <> Enabled then
- begin
- if Value then
- begin
- // initialize:
- for I := 0 to LayerCollections.Count - 1 do
- with TLayerCollectionAccess(LayerCollections[I]) do
- begin
- OnLayerUpdated := LayerUpdateHandler;
- OnAreaUpdated := AreaUpdateHandler;
- OnListNotify := LayerCollectionNotifyHandler;
- end;
- BufferResizedHandler(Buffer.Width, Buffer.Height);
- end
- else
- begin
- // clean up:
- for I := 0 to LayerCollections.Count - 1 do
- with TLayerCollectionAccess(LayerCollections[I]) do
- begin
- OnLayerUpdated := nil;
- OnAreaUpdated := nil;
- OnListNotify := nil;
- end;
- MicroTilesDestroy(FInvalidTiles);
- MicroTilesDestroy(FTempTiles);
- MicroTilesDestroy(FForcedInvalidTiles);
- FUseInvalidTiles := False;
- FOldInvalidTilesValid := False;
- FOldInvalidTilesMap.Clear;
- FInvalidLayers.Clear;
- end;
- inherited;
- end;
- end;
- procedure TMicroTilesRepaintOptimizer.SetAdaptiveMode(const Value: Boolean);
- begin
- if FAdaptiveMode <> Value then
- begin
- FAdaptiveMode := Value;
- ResetAdaptiveMode;
- end;
- end;
- procedure TMicroTilesRepaintOptimizer.ResetAdaptiveMode;
- begin
- FTimeDelta := TIMER_LOWLIMIT;
- FAdaptionFailed := False;
- FPerformanceLevel := PL_MICROTILES;
- end;
- procedure TMicroTilesRepaintOptimizer.BeginPaintBuffer;
- begin
- if AdaptiveMode then FPerfTimer.Start;
- end;
- procedure TMicroTilesRepaintOptimizer.EndPaintBuffer;
- begin
- FUseInvalidTiles := False;
- {$IFDEF MICROTILES_DEBUGDRAW}
- {$IFDEF MICROTILES_DEBUGDRAW_UNOPTIMIZED}
- MicroTilesDebugDraw(FDebugMicroTiles, Buffer, False, FDebugWholeTiles);
- {$ELSE}
- MicroTilesDebugDraw(FDebugMicroTiles, Buffer, True, FDebugWholeTiles);
- {$ENDIF}
- MicroTilesClear(FDebugMicroTiles);
- {$ENDIF}
- {$IFNDEF MICROTILES_NO_ADAPTION}
- EndAdaption;
- {$ENDIF}
- end;
- procedure TMicroTilesRepaintOptimizer.DrawLayerToMicroTiles(var DstTiles: TMicroTiles; Layer: TCustomLayer);
- begin
- Buffer.BeginMeasuring(DrawMeasuringHandler);
- FWorkMicroTiles := @DstTiles;
- TCustomLayerAccess(Layer).DoPaint(Buffer);
- Buffer.EndMeasuring;
- end;
- procedure TMicroTilesRepaintOptimizer.DrawMeasuringHandler(Sender: TObject; const Area: TRect;
- const Info: Cardinal);
- begin
- AddArea(FWorkMicroTiles^, Area, Info);
- end;
- procedure TMicroTilesRepaintOptimizer.PerformOptimization;
- var
- I: Integer;
- Layer: TCustomLayer;
- UseWholeTiles: Boolean;
- LayerTilesPtr: PMicroTiles;
- begin
- if FUseInvalidTiles then
- begin
- ValidateWorkingTiles;
- // Determine if the use of whole tiles is better for current performance level
- {$IFNDEF MICROTILES_NO_ADAPTION}
- UseWholeTiles := FPerformanceLevel > PL_MICROTILES;
- {$ELSE}
- {$IFDEF MICROTILES_NO_ADAPTION_FORCE_WHOLETILES}
- UseWholeTiles := True;
- {$ELSE}
- UseWholeTiles := False;
- {$ENDIF}
- {$ENDIF}
- if FInvalidLayers.Count > 0 then
- begin
- for I := 0 to FInvalidLayers.Count - 1 do
- begin
- Layer := FInvalidLayers[I];
- // Clear temporary tiles
- MicroTilesClearUsed(FTempTiles);
- // Draw layer to temporary tiles
- DrawLayerToMicroTiles(FTempTiles, Layer);
- // Combine temporary tiles with the global invalid tiles
- MicroTilesUnion(FInvalidTiles, FTempTiles, UseWholeTiles);
- // Retrieve old invalid tiles for the current layer
- LayerTilesPtr := FOldInvalidTilesMap[Layer];
- // Combine old invalid tiles with the global invalid tiles
- MicroTilesUnion(FInvalidTiles, LayerTilesPtr^, UseWholeTiles);
- // Copy temporary (current) invalid tiles to the layer
- MicroTilesCopy(LayerTilesPtr^, FTempTiles);
- // Unmark layer as invalid
- TCustomLayerAccess(Layer).Invalid := False;
- end;
- FInvalidLayers.Clear;
- end;
- {$IFDEF MICROTILES_DEBUGDRAW}
- MicroTilesCalcRects(FInvalidTiles, InvalidRects, False, UseWholeTiles);
- MicroTilesCalcRects(FForcedInvalidTiles, InvalidRects, False, UseWholeTiles);
- MicroTilesCopy(FDebugMicroTiles, FInvalidTiles);
- MicroTilesUnion(FDebugMicroTiles, FForcedInvalidTiles);
- FDebugWholeTiles := UseWholeTiles;
- {$ELSE}
- // Calculate optimized rectangles from global invalid tiles
- MicroTilesCalcRects(FInvalidTiles, InvalidRects, False, UseWholeTiles);
- // Calculate optimized rectangles from forced invalid tiles
- MicroTilesCalcRects(FForcedInvalidTiles, InvalidRects, False, UseWholeTiles);
- {$ENDIF}
- end;
- {$IFNDEF MICROTILES_NO_ADAPTION}
- BeginAdaption;
- {$ENDIF}
- {$IFDEF MICROTILES_DEBUGDRAW}
- if InvalidRects.Count > 0 then
- begin
- FDebugInvalidRects.Count := InvalidRects.Count;
- Move(InvalidRects[0]^, FDebugInvalidRects[0]^, InvalidRects.Count * SizeOf(TRect));
- InvalidRects.Clear;
- end;
- {$ENDIF}
- // Rects have been created, so we don't need the tiles any longer, clear them.
- MicroTilesClearUsed(FInvalidTiles);
- MicroTilesClearUsed(FForcedInvalidTiles);
- end;
- procedure TMicroTilesRepaintOptimizer.BeginAdaption;
- begin
- if AdaptiveMode and (FPerformanceLevel > PL_MICROTILES) then
- begin
- if Integer(GetTickCount) > FNextCheck then
- begin
- FPerformanceLevel := Constrain(FPerformanceLevel - 1, PL_MICROTILES, PL_FULLSCENE);
- {$IFDEF CODESITE}
- CodeSite.SendInteger('PrepareInvalidRects(Timed): FPerformanceLevel', FPerformanceLevel);
- {$ENDIF}
- FTimedCheck := True;
- end
- else if not FAdaptionFailed and (InvalidRects.Count < FOldInvalidRectsCount - INVALIDRECTS_DELTA) then
- begin
- FPerformanceLevel := Constrain(FPerformanceLevel - 1, PL_MICROTILES, PL_FULLSCENE);
- {$IFDEF CODESITE}
- CodeSite.SendInteger('PrepareInvalidRects: FPerformanceLevel', FPerformanceLevel);
- {$ENDIF}
- end
- else if FPerformanceLevel = PL_FULLSCENE then
- // we need a full scene rendition, so clear the invalid rects
- InvalidRects.Clear;
- end;
- end;
- procedure TMicroTilesRepaintOptimizer.EndAdaption;
- var
- TimeElapsed: Int64;
- Level: Integer;
- begin
- // our KISS(TM) repaint mode balancing starts here...
- TimeElapsed := FPerfTimer.ReadValue;
- {$IFDEF MICROTILES_DEBUGDRAW}
- if FDebugInvalidRects.Count = 0 then
- {$ELSE}
- if InvalidRects.Count = 0 then
- {$ENDIF}
- FElapsedTimeForFullSceneRepaint := TimeElapsed
- else if AdaptiveMode then
- begin
- if TimeElapsed > FElapsedTimeForFullSceneRepaint then
- begin
- Level := Constrain(FPerformanceLevel + 1, PL_MICROTILES, PL_FULLSCENE);
- // did performance level change from previous level?
- if Level <> FPerformanceLevel then
- begin
- {$IFDEF MICROTILES_DEBUGDRAW}
- FOldInvalidRectsCount := FDebugInvalidRects.Count;
- {$ELSE}
- // save count of old invalid rects so we can use it in PrepareInvalidRects
- // the next time...
- FOldInvalidRectsCount := InvalidRects.Count;
- {$ENDIF}
- FPerformanceLevel := Level;
- {$IFDEF CODESITE}
- CodeSite.SendInteger('EndPaintBuffer: FPerformanceLevel', FPerformanceLevel);
- {$ENDIF}
- // was this a timed check?
- if FTimedCheck then
- begin
- // time based approach failed, so add penalty
- FTimeDelta := Constrain(Integer(FTimeDelta + TIMER_PENALTY), TIMER_LOWLIMIT, TIMER_HIGHLIMIT);
- // schedule next check
- FNextCheck := Integer(GetTickCount) + FTimeDelta;
- FElapsedTimeOnLastPenalty := TimeElapsed;
- FTimedCheck := False;
- {$IFDEF CODESITE}
- CodeSite.SendInteger('timed check failed, new delta', FTimeDelta);
- {$ENDIF}
- end;
- {$IFDEF CODESITE}
- CodeSite.AddSeparator;
- {$ENDIF}
- FAdaptionFailed := True;
- end;
- end
- else if TimeElapsed < FElapsedTimeForFullSceneRepaint then
- begin
- if FTimedCheck then
- begin
- // time based approach had success!!
- // reset time delta back to lower limit, ie. remove penalties
- FTimeDelta := TIMER_LOWLIMIT;
- // schedule next check
- FNextCheck := Integer(GetTickCount) + FTimeDelta;
- FTimedCheck := False;
- {$IFDEF CODESITE}
- CodeSite.SendInteger('timed check succeeded, new delta', FTimeDelta);
- CodeSite.AddSeparator;
- {$ENDIF}
- FAdaptionFailed := False;
- end
- else
- begin
- // invalid rect count approach had success!!
- // shorten time for next check to benefit nonetheless in case we have a fallback...
- if FTimeDelta > TIMER_LOWLIMIT then
- begin
- // remove the penalty value 4 times from the current time delta
- FTimeDelta := Constrain(FTimeDelta - 4 * TIMER_PENALTY, TIMER_LOWLIMIT, TIMER_HIGHLIMIT);
- // schedule next check
- FNextCheck := Integer(GetTickCount) + FTimeDelta;
- {$IFDEF CODESITE}
- CodeSite.SendInteger('invalid rect count approach succeeded, new timer delta', FTimeDelta);
- CodeSite.AddSeparator;
- {$ENDIF}
- end;
- FAdaptionFailed := False;
- end;
- end
- else if (TimeElapsed < FElapsedTimeOnLastPenalty) and FTimedCheck then
- begin
- // time approach had success optimizing the situation, so shorten time until next check
- FTimeDelta := Constrain(FTimeDelta - TIMER_PENALTY, TIMER_LOWLIMIT, TIMER_HIGHLIMIT);
- // schedule next check
- FNextCheck := Integer(GetTickCount) + FTimeDelta;
- FTimedCheck := False;
- {$IFDEF CODESITE}
- CodeSite.SendInteger('timed check succeeded, new delta', FTimeDelta);
- CodeSite.AddSeparator;
- {$ENDIF}
- end;
- end;
- FElapsedTimeForLastRepaint := TimeElapsed;
- end;
- {$IFDEF CODESITE}
- { TDebugMicroTilesRepaintOptimizer }
- procedure TDebugMicroTilesRepaintOptimizer.AreaUpdateHandler(Sender: TObject;
- const Area: TRect; const Info: Cardinal);
- begin
- DumpCallStack('TDebugMicroTilesRepaintOptimizer.AreaUpdateHandler');
- inherited;
- end;
- procedure TDebugMicroTilesRepaintOptimizer.BeginPaintBuffer;
- begin
- DumpCallStack('TDebugMicroTilesRepaintOptimizer.BeginPaintBuffer');
- inherited;
- end;
- procedure TDebugMicroTilesRepaintOptimizer.BufferResizedHandler(const NewWidth,
- NewHeight: Integer);
- begin
- DumpCallStack('TDebugMicroTilesRepaintOptimizer.BufferResizedHandler');
- inherited;
- end;
- procedure TDebugMicroTilesRepaintOptimizer.EndPaintBuffer;
- begin
- DumpCallStack('TDebugMicroTilesRepaintOptimizer.EndPaintBuffer');
- inherited;
- CodeSite.AddSeparator;
- end;
- procedure TDebugMicroTilesRepaintOptimizer.LayerUpdateHandler(Sender: TObject;
- Layer: TCustomLayer);
- begin
- DumpCallStack('TDebugMicroTilesRepaintOptimizer.LayerUpdateHandler');
- inherited;
- end;
- procedure TDebugMicroTilesRepaintOptimizer.PerformOptimization;
- begin
- DumpCallStack('TDebugMicroTilesRepaintOptimizer.PerformOptimization');
- inherited;
- end;
- procedure TDebugMicroTilesRepaintOptimizer.Reset;
- begin
- DumpCallStack('TDebugMicroTilesRepaintOptimizer.Reset');
- inherited;
- CodeSite.AddSeparator;
- end;
- function TDebugMicroTilesRepaintOptimizer.UpdatesAvailable: Boolean;
- begin
- DumpCallStack('TDebugMicroTilesRepaintOptimizer.UpdatesAvailable');
- Result := inherited UpdatesAvailable;
- end;
- {$ENDIF}
- const
- FID_MICROTILEUNION = 0;
- FID_MICROTILESUNION = 1;
- var
- Registry: TFunctionRegistry;
- procedure RegisterBindings;
- begin
- Registry := NewRegistry('GR32_MicroTiles bindings');
- Registry.RegisterBinding(FID_MICROTILEUNION, @@MicroTileUnion);
- Registry.RegisterBinding(FID_MICROTILESUNION, @@MicroTilesU);
- Registry.Add(FID_MICROTILEUNION, @MicroTileUnion_Pas);
- Registry.Add(FID_MICROTILESUNION, @MicroTilesUnion_Pas);
- {$IFNDEF PUREPASCAL}
- {$IFDEF TARGET_x86}
- Registry.Add(FID_MICROTILEUNION, @MicroTileUnion_EMMX, [ciEMMX]);
- Registry.Add(FID_MICROTILESUNION, @MicroTilesUnion_EMMX, [ciEMMX]);
- {$ENDIF}
- {$ENDIF}
- Registry.RebindAll;
- end;
- initialization
- RegisterBindings;
- end.
|