1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354 |
- unit GR32_LowLevel;
- (* ***** 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 Graphics32
- *
- * The Initial Developer of the Original Code is
- * Alex A. Denisov
- *
- * Portions created by the Initial Developer are Copyright (C) 2000-2009
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Michael Hansen <[email protected]>
- * Andre Beckedorf <[email protected]>
- * Mattias Andersson <[email protected]>
- *
- * ***** END LICENSE BLOCK ***** *)
- interface
- {$I GR32.inc}
- {$IFDEF PUREPASCAL}
- {$DEFINE USENATIVECODE}
- {$DEFINE USEMOVE}
- {$ENDIF}
- {$IFDEF USEINLINING}
- {$DEFINE USENATIVECODE}
- {$ENDIF}
- uses
- System.UITypes,
- FMX.Graphics, GR32, GR32_Math;
- { Clamp function restricts value to [0..255] range }
- function Clamp(const Value: Integer): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- { An analogue of FillChar for 32 bit values }
- var
- FillLongword: procedure(var X; Count: Cardinal; Value: Longword);
- procedure FillWord(var X; Count: Cardinal; Value: Longword);
- { An analogue of Move for 32 bit values }
- {$IFDEF USEMOVE}
- procedure MoveLongword(const Source; var Dest; Count: Integer); {$IFDEF USEINLINING} inline; {$ENDIF}
- {$ELSE}
- procedure MoveLongword(const Source; var Dest; Count: Integer);
- {$ENDIF}
- procedure MoveWord(const Source; var Dest; Count: Integer);
- {$IFDEF USESTACKALLOC}
- { Allocates a 'small' block of memory on the stack }
- function StackAlloc(Size: Integer): Pointer; register;
- { Pops memory allocated by StackAlloc }
- procedure StackFree(P: Pointer); register;
- {$ENDIF}
- { Exchange two 32-bit values }
- procedure Swap(var A, B: Pointer); overload;{$IFDEF USEINLINING} inline; {$ENDIF}
- procedure Swap(var A, B: Integer); overload;{$IFDEF USEINLINING} inline; {$ENDIF}
- procedure Swap(var A, B: TFixed); overload;{$IFDEF USEINLINING} inline; {$ENDIF}
- procedure Swap(var A, B: TColor32); overload;{$IFDEF USEINLINING} inline; {$ENDIF}
- procedure Swap32(var A, B); overload;{$IFDEF USEINLINING} inline; {$ENDIF}
- { Exchange A <-> B only if B < A }
- procedure TestSwap(var A, B: Integer); overload;{$IFDEF USEINLINING} inline; {$ENDIF}
- procedure TestSwap(var A, B: TFixed); overload;{$IFDEF USEINLINING} inline; {$ENDIF}
- { Exchange A <-> B only if B < A then restrict both to [0..Size-1] range }
- { returns true if resulting range has common points with [0..Size-1] range }
- function TestClip(var A, B: Integer; const Size: Integer): Boolean; overload;
- function TestClip(var A, B: Integer; const Start, Stop: Integer): Boolean; overload;
- { Returns value constrained to [Lo..Hi] range}
- function Constrain(const Value, Lo, Hi: Integer): Integer; {$IFDEF USEINLINING} inline; {$ENDIF} overload;
- function Constrain(const Value, Lo, Hi: Single): Single; {$IFDEF USEINLINING} inline; {$ENDIF} overload;
- { Returns value constrained to [min(Constrain1, Constrain2)..max(Constrain1, Constrain2] range}
- function SwapConstrain(const Value: Integer; Constrain1, Constrain2: Integer): Integer;
- { Returns min./max. value of A, B and C }
- function Min(const A, B, C: Integer): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- function Max(const A, B, C: Integer): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- { Clamp integer value to [0..Max] range }
- function Clamp(Value, Max: Integer): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- { Same but [Min..Max] range }
- function Clamp(Value, Min, Max: Integer): Integer; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- { Wrap integer value to [0..Max] range }
- function Wrap(Value, Max: Integer): Integer; overload;
- { Same but [Min..Max] range }
- function Wrap(Value, Min, Max: Integer): Integer; overload;
- { Wrap single value to [0..Max] range }
- function Wrap(Value, Max: Single): Single; overload; {$IFDEF USEINLINING} inline; {$ENDIF} overload;
- { Fast Wrap alternatives for cases where range + 1 is a power of two }
- function WrapPow2(Value, Max: Integer): Integer; {$IFDEF USEINLINING} inline; {$ENDIF} overload;
- function WrapPow2(Value, Min, Max: Integer): Integer; {$IFDEF USEINLINING} inline; {$ENDIF} overload;
- { Mirror integer value in [0..Max] range }
- function Mirror(Value, Max: Integer): Integer; overload;
- { Same but [Min..Max] range }
- function Mirror(Value, Min, Max: Integer): Integer; overload;
- { Fast Mirror alternatives for cases where range + 1 is a power of two }
- function MirrorPow2(Value, Max: Integer): Integer; {$IFDEF USEINLINING} inline; {$ENDIF} overload;
- function MirrorPow2(Value, Min, Max: Integer): Integer; {$IFDEF USEINLINING} inline; {$ENDIF} overload;
- { Functions to determine appropiate wrap procs (normal or power of 2 optimized)}
- function GetOptimalWrap(Max: Integer): TWrapProc; {$IFDEF USEINLINING} inline; {$ENDIF} overload;
- function GetOptimalWrap(Min, Max: Integer): TWrapProcEx; {$IFDEF USEINLINING} inline; {$ENDIF} overload;
- function GetOptimalMirror(Max: Integer): TWrapProc; {$IFDEF USEINLINING} inline; {$ENDIF} overload;
- function GetOptimalMirror(Min, Max: Integer): TWrapProcEx; {$IFDEF USEINLINING} inline; {$ENDIF} overload;
- { Functions to retrieve correct WrapProc given WrapMode (and range) }
- function GetWrapProc(WrapMode: TWrapMode): TWrapProc; overload;
- function GetWrapProc(WrapMode: TWrapMode; Max: Integer): TWrapProc; overload;
- function GetWrapProcEx(WrapMode: TWrapMode): TWrapProcEx; overload;
- function GetWrapProcEx(WrapMode: TWrapMode; Min, Max: Integer): TWrapProcEx; overload;
- const
- WRAP_PROCS: array[TWrapMode] of TWrapProc = (Clamp, Wrap, Mirror);
- WRAP_PROCS_EX: array[TWrapMode] of TWrapProcEx = (Clamp, Wrap, Mirror);
- { Fast Value div 255, correct result with Value in [0..66298] range }
- function Div255(Value: Cardinal): Cardinal; {$IFDEF USEINLINING} inline; {$ENDIF}
- { shift right with sign conservation }
- function SAR_3(Value: Integer): Integer;
- function SAR_4(Value: Integer): Integer;
- function SAR_6(Value: Integer): Integer;
- function SAR_8(Value: Integer): Integer;
- function SAR_9(Value: Integer): Integer;
- function SAR_11(Value: Integer): Integer;
- function SAR_12(Value: Integer): Integer;
- function SAR_13(Value: Integer): Integer;
- function SAR_14(Value: Integer): Integer;
- function SAR_15(Value: Integer): Integer;
- function SAR_16(Value: Integer): Integer;
- { ColorSwap exchanges ARGB <-> ABGR and fills A with $FF }
- function ColorSwap(WinColor: TColor): TColor32;
- implementation
- uses
- {$IFDEF FPC}
- SysUtils,
- {$ENDIF}
- GR32_System, GR32_Bindings;
- {$R-}{$Q-} // switch off overflow and range checking
- function Clamp(const Value: Integer): Integer;
- {$IFDEF USENATIVECODE}
- begin
- if Value > 255 then
- Result := 255
- else
- if Value < 0 then
- Result := 0
- else
- Result := Value;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- // in x64 calling convention parameters are passed in ECX, EDX, R8 & R9
- MOV EAX,ECX
- {$ENDIF}
- TEST EAX,$FFFFFF00
- JNZ @1
- RET
- @1: JS @2
- MOV EAX,$FF
- RET
- @2: XOR EAX,EAX
- {$ENDIF}
- end;
- procedure FillLongword_Pas(var X; Count: Cardinal; Value: Longword);
- var
- I: Integer;
- P: PIntegerArray;
- begin
- P := PIntegerArray(@X);
- for I := Count - 1 downto 0 do
- P[I] := Integer(Value);
- end;
- {$IFNDEF PUREPASCAL}
- procedure FillLongword_ASM(var X; Count: Cardinal; Value: Longword); {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x86}
- // EAX = X; EDX = Count; ECX = Value
- PUSH EDI
- MOV EDI,EAX // Point EDI to destination
- MOV EAX,ECX
- MOV ECX,EDX
- REP STOSD // Fill count dwords
- @Exit:
- POP EDI
- {$ENDIF}
- {$IFDEF TARGET_x64}
- // ECX = X; EDX = Count; R8 = Value
- PUSH RDI
- MOV RDI,RCX // Point EDI to destination
- MOV RAX,R8 // copy value from R8 to RAX (EAX)
- MOV ECX,EDX // copy count to ECX
- TEST ECX,ECX
- JS @Exit
- REP STOSD // Fill count dwords
- @Exit:
- POP RDI
- {$ENDIF}
- end;
- procedure FillLongword_MMX(var X; Count: Cardinal; Value: Longword); {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x86}
- // EAX = X; EDX = Count; ECX = Value
- TEST EDX, EDX // if Count = 0 then
- JZ @Exit // Exit
- PUSH EDI
- MOV EDI, EAX
- MOV EAX, EDX
- SHR EAX, 1
- SHL EAX, 1
- SUB EAX, EDX
- JE @QLoopIni
- MOV [EDI], ECX
- ADD EDI, 4
- DEC EDX
- JZ @ExitPOP
- @QLoopIni:
- MOVD MM1, ECX
- PUNPCKLDQ MM1, MM1
- SHR EDX, 1
- @QLoop:
- MOVQ [EDI], MM1
- ADD EDI, 8
- DEC EDX
- JNZ @QLoop
- EMMS
- @ExitPOP:
- POP EDI
- @Exit:
- {$ENDIF}
- {$IFDEF TARGET_x64}
- // RCX = X; RDX = Count; R8 = Value
- TEST RDX, RDX // if Count = 0 then
- JZ @Exit // Exit
- MOV RAX, RCX // RAX = X
- PUSH RDI // store RDI on stack
- MOV R9, RDX // R9 = Count
- MOV RDI, RDX // RDI = Count
- SHR RDI, 1 // RDI = RDI SHR 1
- SHL RDI, 1 // RDI = RDI SHL 1
- SUB R9, RDI // check if extra fill is necessary
- JE @QLoopIni
- MOV [RAX], R8D // eventually perform extra fill
- ADD RAX, 4 // Inc(X, 4)
- DEC RDX // Dec(Count)
- JZ @ExitPOP // if (Count = 0) then Exit
- @QLoopIni:
- MOVD MM0, R8D // MM0 = R8D
- PUNPCKLDQ MM0, MM0 // unpack MM0 register
- SHR RDX, 1 // RDX = RDX div 2
- @QLoop:
- MOVQ QWORD PTR [RAX], MM0 // perform fill
- ADD RAX, 8 // Inc(X, 8)
- DEC RDX // Dec(X);
- JNZ @QLoop
- EMMS
- @ExitPOP:
- POP RDI
- @Exit:
- {$ENDIF}
- end;
- procedure FillLongword_SSE2(var X; Count: Integer; Value: Longword); {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x86}
- // EAX = X; EDX = Count; ECX = Value
- TEST EDX, EDX // if Count = 0 then
- JZ @Exit // Exit
- PUSH EDI // push EDI on stack
- MOV EDI, EAX // Point EDI to destination
- CMP EDX, 32
- JL @SmallLoop
- AND EAX, 3 // get aligned count
- TEST EAX, EAX // check if X is not dividable by 4
- JNZ @SmallLoop // otherwise perform slow small loop
- MOV EAX, EDI
- SHR EAX, 2 // bytes to count
- AND EAX, 3 // get aligned count
- ADD EAX,-4
- NEG EAX // get count to advance
- JZ @SetupMain
- SUB EDX, EAX // subtract aligning start from total count
- @AligningLoop:
- MOV [EDI], ECX
- ADD EDI, 4
- DEC EAX
- JNZ @AligningLoop
- @SetupMain:
- MOV EAX, EDX // EAX = remaining count
- SHR EAX, 2
- SHL EAX, 2
- SUB EDX, EAX // EDX = remaining count
- SHR EAX, 2
- MOVD XMM0, ECX
- PUNPCKLDQ XMM0, XMM0
- PUNPCKLDQ XMM0, XMM0
- @SSE2Loop:
- MOVDQA [EDI], XMM0
- ADD EDI, 16
- DEC EAX
- JNZ @SSE2Loop
- @SmallLoop:
- MOV EAX,ECX
- MOV ECX,EDX
- REP STOSD // Fill count dwords
- @ExitPOP:
- POP EDI
- @Exit:
- {$ENDIF}
- {$IFDEF TARGET_x64}
- // RCX = X; RDX = Count; R8 = Value
- TEST RDX, RDX // if Count = 0 then
- JZ @Exit // Exit
- MOV R9, RCX // Point R9 to destination
- CMP RDX, 32
- JL @SmallLoop
- AND RCX, 3 // get aligned count
- TEST RCX, RCX // check if X is not dividable by 4
- JNZ @SmallLoop // otherwise perform slow small loop
- MOV RCX, R9
- SHR RCX, 2 // bytes to count
- AND RCX, 3 // get aligned count
- ADD RCX,-4
- NEG RCX // get count to advance
- JZ @SetupMain
- SUB RDX, RCX // subtract aligning start from total count
- @AligningLoop:
- MOV [R9], R8D
- ADD R9, 4
- DEC RCX
- JNZ @AligningLoop
- @SetupMain:
- MOV RCX, RDX // RCX = remaining count
- SHR RCX, 2
- SHL RCX, 2
- SUB RDX, RCX // RDX = remaining count
- SHR RCX, 2
- MOVD XMM0, R8D
- PUNPCKLDQ XMM0, XMM0
- PUNPCKLDQ XMM0, XMM0
- @SSE2Loop:
- MOVDQA [R9], XMM0
- ADD R9, 16
- DEC RCX
- JNZ @SSE2Loop
- TEST RDX, RDX
- JZ @Exit
- @SmallLoop:
- MOV [R9], R8D
- ADD R9, 4
- DEC RDX
- JNZ @SmallLoop
- @Exit:
- {$ENDIF}
- end;
- {$ENDIF}
- procedure FillWord(var X; Count: Cardinal; Value: LongWord);
- {$IFDEF USENATIVECODE}
- var
- I: Integer;
- P: PWordArray;
- begin
- P := PWordArray(@X);
- for I := Count - 1 downto 0 do
- P[I] := Value;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x86}
- // EAX = X; EDX = Count; ECX = Value
- PUSH EDI
- MOV EDI,EAX // Point EDI to destination
- MOV EAX,ECX
- MOV ECX,EDX
- TEST ECX,ECX
- JZ @exit
- REP STOSW // Fill count words
- @exit:
- POP EDI
- {$ENDIF}
- {$IFDEF TARGET_x64}
- // ECX = X; EDX = Count; R8D = Value
- PUSH RDI
- MOV RDI,RCX // Point EDI to destination
- MOV EAX,R8D
- MOV ECX,EDX
- TEST ECX,ECX
- JZ @exit
- REP STOSW // Fill count words
- @exit:
- POP RDI
- {$ENDIF}
- {$ENDIF}
- end;
- procedure MoveLongword(const Source; var Dest; Count: Integer);
- {$IFDEF USEMOVE}
- begin
- Move(Source, Dest, Count shl 2);
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x86}
- // EAX = Source; EDX = Dest; ECX = Count
- PUSH ESI
- PUSH EDI
- MOV ESI,EAX
- MOV EDI,EDX
- CMP EDI,ESI
- JE @exit
- REP MOVSD
- @exit:
- POP EDI
- POP ESI
- {$ENDIF}
- {$IFDEF TARGET_x64}
- // RCX = Source; RDX = Dest; R8 = Count
- PUSH RSI
- PUSH RDI
- MOV RSI,RCX
- MOV RDI,RDX
- MOV RCX,R8
- CMP RDI,RSI
- JE @exit
- REP MOVSD
- @exit:
- POP RDI
- POP RSI
- {$ENDIF}
- {$ENDIF}
- end;
- procedure MoveWord(const Source; var Dest; Count: Integer);
- {$IFDEF USEMOVE}
- begin
- Move(Source, Dest, Count shl 1);
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x86}
- // EAX = X; EDX = Count; ECX = Value
- PUSH ESI
- PUSH EDI
- MOV ESI,EAX
- MOV EDI,EDX
- MOV EAX,ECX
- CMP EDI,ESI
- JE @exit
- REP MOVSW
- @exit:
- POP EDI
- POP ESI
- {$ENDIF}
- {$IFDEF TARGET_x64}
- // ECX = X; EDX = Count; R8 = Value
- PUSH RSI
- PUSH RDI
- MOV RSI,RCX
- MOV RDI,RDX
- MOV RAX,R8
- CMP RDI,RSI
- JE @exit
- REP MOVSW
- @exit:
- POP RDI
- POP RSI
- {$ENDIF}
- {$ENDIF}
- end;
- procedure Swap(var A, B: Pointer);
- var
- T: Pointer;
- begin
- T := A;
- A := B;
- B := T;
- end;
- procedure Swap(var A, B: Integer);
- var
- T: Integer;
- begin
- T := A;
- A := B;
- B := T;
- end;
- procedure Swap(var A, B: TFixed);
- var
- T: TFixed;
- begin
- T := A;
- A := B;
- B := T;
- end;
- procedure Swap(var A, B: TColor32);
- var
- T: TColor32;
- begin
- T := A;
- A := B;
- B := T;
- end;
- procedure Swap32(var A, B);
- var
- T: Integer;
- begin
- T := Integer(A);
- Integer(A) := Integer(B);
- Integer(B) := T;
- end;
- procedure TestSwap(var A, B: Integer);
- var
- T: Integer;
- begin
- if B < A then
- begin
- T := A;
- A := B;
- B := T;
- end;
- end;
- procedure TestSwap(var A, B: TFixed);
- var
- T: TFixed;
- begin
- if B < A then
- begin
- T := A;
- A := B;
- B := T;
- end;
- end;
- function TestClip(var A, B: Integer; const Size: Integer): Boolean;
- begin
- TestSwap(A, B); // now A = min(A,B) and B = max(A, B)
- if A < 0 then
- A := 0;
- if B >= Size then
- B := Size - 1;
- Result := B >= A;
- end;
- function TestClip(var A, B: Integer; const Start, Stop: Integer): Boolean;
- begin
- TestSwap(A, B); // now A = min(A,B) and B = max(A, B)
- if A < Start then
- A := Start;
- if B >= Stop then
- B := Stop - 1;
- Result := B >= A;
- end;
- function Constrain(const Value, Lo, Hi: Integer): Integer;
- {$IFDEF USENATIVECODE}
- begin
- if Value < Lo then
- Result := Lo
- else if Value > Hi then
- Result := Hi
- else
- Result := Value;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- MOV ECX,R8D
- {$ENDIF}
- CMP EDX,EAX
- CMOVG EAX,EDX
- CMP ECX,EAX
- CMOVL EAX,ECX
- {$ENDIF}
- end;
- function Constrain(const Value, Lo, Hi: Single): Single; overload;
- begin
- if Value < Lo then Result := Lo
- else if Value > Hi then Result := Hi
- else Result := Value;
- end;
- function SwapConstrain(const Value: Integer; Constrain1, Constrain2: Integer): Integer;
- begin
- TestSwap(Constrain1, Constrain2);
- if Value < Constrain1 then Result := Constrain1
- else if Value > Constrain2 then Result := Constrain2
- else Result := Value;
- end;
- function Max(const A, B, C: Integer): Integer;
- {$IFDEF USENATIVECODE}
- begin
- if A > B then
- Result := A
- else
- Result := B;
- if C > Result then
- Result := C;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV RAX,RCX
- MOV RCX,R8
- {$ENDIF}
- CMP EDX,EAX
- CMOVG EAX,EDX
- CMP ECX,EAX
- CMOVG EAX,ECX
- {$ENDIF}
- end;
- function Min(const A, B, C: Integer): Integer;
- {$IFDEF USENATIVECODE}
- begin
- if A < B then
- Result := A
- else
- Result := B;
- if C < Result then
- Result := C;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV RAX,RCX
- MOV RCX,R8
- {$ENDIF}
- CMP EDX,EAX
- CMOVL EAX,EDX
- CMP ECX,EAX
- CMOVL EAX,ECX
- {$ENDIF}
- end;
- function Clamp(Value, Max: Integer): Integer;
- {$IFDEF USENATIVECODE}
- begin
- if Value > Max then
- Result := Max
- else if Value < 0 then
- Result := 0
- else
- Result := Value;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- MOV ECX,R8D
- {$ENDIF}
- CMP EAX,EDX
- JG @Above
- TEST EAX,EAX
- JL @Below
- RET
- @Above:
- MOV EAX,EDX
- RET
- @Below:
- MOV EAX,0
- RET
- {$ENDIF}
- end;
- function Clamp(Value, Min, Max: Integer): Integer;
- {$IFDEF USENATIVECODE}
- begin
- if Value > Max then
- Result := Max
- else if Value < Min then
- Result := Min
- else
- Result := Value;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- MOV ECX,R8D
- {$ENDIF}
- CMP EDX,EAX
- CMOVG EAX,EDX
- CMP ECX,EAX
- CMOVL EAX,ECX
- {$ENDIF}
- end;
- function Wrap(Value, Max: Integer): Integer;
- {$IFDEF USENATIVECODE}
- begin
- if Value < 0 then
- Result := Max + (Value - Max) mod (Max + 1)
- else
- Result := Value mod (Max + 1);
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- MOV ECX,R8D
- LEA ECX,[RDX+1]
- {$ELSE}
- LEA ECX,[EDX+1]
- {$ENDIF}
- CDQ
- IDIV ECX
- MOV EAX,EDX
- TEST EAX,EAX
- JNL @Exit
- ADD EAX,ECX
- @Exit:
- {$ENDIF}
- end;
- function Wrap(Value, Min, Max: Integer): Integer;
- begin
- if Value < Min then
- Result := Max + (Value - Max) mod (Max - Min + 1)
- else
- Result := Min + (Value - Min) mod (Max - Min + 1);
- end;
- function Wrap(Value, Max: Single): Single;
- begin
- {$IFDEF USEFLOATMOD}
- Result := FloatMod(Value, Max);
- {$ELSE}
- Result := Value;
- while Result >= Max do Result := Result - Max;
- while Result < 0 do Result := Result + Max;
- {$ENDIF}
- end;
- function DivMod(Dividend, Divisor: Integer; out Remainder: Integer): Integer;
- {$IFDEF USENATIVECODE}
- begin
- Remainder := Dividend mod Divisor;
- Result := Dividend div Divisor;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x86}
- PUSH EBX
- MOV EBX,EDX
- CDQ
- IDIV EBX
- MOV [ECX],EDX
- POP EBX
- {$ENDIF}
- {$IFDEF TARGET_x64}
- PUSH RBX
- MOV EAX,ECX
- MOV ECX,R8D
- MOV EBX,EDX
- CDQ
- IDIV EBX
- MOV [RCX],EDX
- POP RBX
- {$ENDIF}
- {$ENDIF}
- end;
- function Mirror(Value, Max: Integer): Integer;
- {$IFDEF USENATIVECODE}
- var
- DivResult: Integer;
- begin
- if Value < 0 then
- begin
- DivResult := DivMod(Value - Max, Max + 1, Result);
- Inc(Result, Max);
- end
- else
- DivResult := DivMod(Value, Max + 1, Result);
- if Odd(DivResult) then
- Result := Max - Result;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- MOV ECX,R8D
- {$ENDIF}
- TEST EAX,EAX
- JNL @@1
- NEG EAX
- @@1:
- MOV ECX,EDX
- CDQ
- IDIV ECX
- TEST EAX,1
- MOV EAX,EDX
- JZ @Exit
- NEG EAX
- ADD EAX,ECX
- @Exit:
- {$ENDIF}
- end;
- function Mirror(Value, Min, Max: Integer): Integer;
- var
- DivResult: Integer;
- begin
- if Value < Min then
- begin
- DivResult := DivMod(Value - Max, Max - Min + 1, Result);
- Inc(Result, Max);
- end
- else
- begin
- DivResult := DivMod(Value - Min, Max - Min + 1, Result);
- Inc(Result, Min);
- end;
- if Odd(DivResult) then Result := Max + Min - Result;
- end;
- function WrapPow2(Value, Max: Integer): Integer; overload;
- begin
- Result := Value and Max;
- end;
- function WrapPow2(Value, Min, Max: Integer): Integer; overload;
- begin
- Result := (Value - Min) and (Max - Min) + Min;
- end;
- function MirrorPow2(Value, Max: Integer): Integer; overload;
- begin
- if Value and (Max + 1) = 0 then
- Result := Value and Max
- else
- Result := Max - Value and Max;
- end;
- function MirrorPow2(Value, Min, Max: Integer): Integer; overload;
- begin
- Value := Value - Min;
- Result := Max - Min;
- if Value and (Result + 1) = 0 then
- Result := Min + Value and Result
- else
- Result := Max - Value and Result;
- end;
- function GetOptimalWrap(Max: Integer): TWrapProc; overload;
- begin
- if (Max >= 0) and IsPowerOf2(Max + 1) then
- Result := WrapPow2
- else
- Result := Wrap;
- end;
- function GetOptimalWrap(Min, Max: Integer): TWrapProcEx; overload;
- begin
- if (Min >= 0) and (Max >= Min) and IsPowerOf2(Max - Min + 1) then
- Result := WrapPow2
- else
- Result := Wrap;
- end;
- function GetOptimalMirror(Max: Integer): TWrapProc; overload;
- begin
- if (Max >= 0) and IsPowerOf2(Max + 1) then
- Result := MirrorPow2
- else
- Result := Mirror;
- end;
- function GetOptimalMirror(Min, Max: Integer): TWrapProcEx; overload;
- begin
- if (Min >= 0) and (Max >= Min) and IsPowerOf2(Max - Min + 1) then
- Result := MirrorPow2
- else
- Result := Mirror;
- end;
- function GetWrapProc(WrapMode: TWrapMode): TWrapProc; overload;
- begin
- case WrapMode of
- wmRepeat:
- Result := Wrap;
- wmMirror:
- Result := Mirror;
- else //wmClamp:
- Result := Clamp;
- end;
- end;
- function GetWrapProc(WrapMode: TWrapMode; Max: Integer): TWrapProc; overload;
- begin
- case WrapMode of
- wmRepeat:
- Result := GetOptimalWrap(Max);
- wmMirror:
- Result := GetOptimalMirror(Max);
- else //wmClamp:
- Result := Clamp;
- end;
- end;
- function GetWrapProcEx(WrapMode: TWrapMode): TWrapProcEx; overload;
- begin
- case WrapMode of
- wmRepeat:
- Result := Wrap;
- wmMirror:
- Result := Mirror;
- else //wmClamp:
- Result := Clamp;
- end;
- end;
- function GetWrapProcEx(WrapMode: TWrapMode; Min, Max: Integer): TWrapProcEx; overload;
- begin
- case WrapMode of
- wmRepeat:
- Result := GetOptimalWrap(Min, Max);
- wmMirror:
- Result := GetOptimalMirror(Min, Max);
- else //wmClamp:
- Result := Clamp;
- end;
- end;
- function Div255(Value: Cardinal): Cardinal;
- begin
- Result := (Value * $8081) shr 23;
- end;
- { shift right with sign conservation }
- function SAR_3(Value: Integer): Integer;
- {$IFDEF PUREPASCAL}
- begin
- Result := Value div 8;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- {$ENDIF}
- SAR EAX,3
- {$ENDIF}
- end;
- function SAR_4(Value: Integer): Integer;
- {$IFDEF PUREPASCAL}
- begin
- Result := Value div 16;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- {$ENDIF}
- SAR EAX,4
- {$ENDIF}
- end;
- function SAR_6(Value: Integer): Integer;
- {$IFDEF PUREPASCAL}
- begin
- Result := Value div 64;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- {$ENDIF}
- SAR EAX,6
- {$ENDIF}
- end;
- function SAR_8(Value: Integer): Integer;
- {$IFDEF PUREPASCAL}
- begin
- Result := Value div 256;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- {$ENDIF}
- SAR EAX,8
- {$ENDIF}
- end;
- function SAR_9(Value: Integer): Integer;
- {$IFDEF PUREPASCAL}
- begin
- Result := Value div 512;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- {$ENDIF}
- SAR EAX,9
- {$ENDIF}
- end;
- function SAR_11(Value: Integer): Integer;
- {$IFDEF PUREPASCAL}
- begin
- Result := Value div 2048;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- {$ENDIF}
- SAR EAX,11
- {$ENDIF}
- end;
- function SAR_12(Value: Integer): Integer;
- {$IFDEF PUREPASCAL}
- begin
- Result := Value div 4096;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- {$ENDIF}
- SAR EAX,12
- {$ENDIF}
- end;
- function SAR_13(Value: Integer): Integer;
- {$IFDEF PUREPASCAL}
- begin
- Result := Value div 8192;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- {$ENDIF}
- SAR EAX,13
- {$ENDIF}
- end;
- function SAR_14(Value: Integer): Integer;
- {$IFDEF PUREPASCAL}
- begin
- Result := Value div 16384;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- {$ENDIF}
- SAR EAX,14
- {$ENDIF}
- end;
- function SAR_15(Value: Integer): Integer;
- {$IFDEF PUREPASCAL}
- begin
- Result := Value div 32768;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- {$ENDIF}
- SAR EAX,15
- {$ENDIF}
- end;
- function SAR_16(Value: Integer): Integer;
- {$IFDEF PUREPASCAL}
- begin
- Result := Value div 65536;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- {$ENDIF}
- SAR EAX,16
- {$ENDIF}
- end;
- { Colorswap exchanges ARGB <-> ABGR and fill A with $FF }
- function ColorSwap(WinColor: TColor): TColor32;
- {$IFDEF USENATIVECODE}
- var
- WCEn: TColor32Entry absolute WinColor;
- REn : TColor32Entry absolute Result;
- begin
- Result := WCEn.ARGB;
- REn.A := $FF;
- REn.R := WCEn.B;
- REn.B := WCEn.R;
- {$ELSE}
- {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- // EAX = WinColor
- // this function swaps R and B bytes in ABGR
- // and writes $FF into A component
- {$IFDEF TARGET_x64}
- MOV EAX,ECX
- {$ENDIF}
- BSWAP EAX
- MOV AL, $FF
- ROR EAX,8
- {$ENDIF}
- end;
- {$IFDEF USESTACKALLOC}
- {$IFDEF PUREPASCAL}
- function StackAlloc(Size: Integer): Pointer;
- begin
- GetMem(Result, Size);
- end;
- procedure StackFree(P: Pointer);
- begin
- FreeMem(P);
- end;
- {$ELSE}
- { StackAlloc allocates a 'small' block of memory from the stack by
- decrementing SP. This provides the allocation speed of a local variable,
- but the runtime size flexibility of heap allocated memory.
- x64 implementation by Jameel Halabi
- }
- function StackAlloc(Size: Integer): Pointer; register; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x86}
- POP ECX // return address
- MOV EDX, ESP
- ADD EAX, 3
- AND EAX, not 3 // round up to keep ESP dword aligned
- CMP EAX, 4092
- JLE @@2
- @@1:
- SUB ESP, 4092
- PUSH EAX // make sure we touch guard page, to grow stack
- SUB EAX, 4096
- JNS @@1
- ADD EAX, 4096
- @@2:
- SUB ESP, EAX
- MOV EAX, ESP // function result = low memory address of block
- PUSH EDX // save original SP, for cleanup
- MOV EDX, ESP
- SUB EDX, 4
- PUSH EDX // save current SP, for sanity check (sp = [sp])
- PUSH ECX // return to caller
- {$ENDIF}
- {$IFDEF TARGET_x64}
- {$IFNDEF FPC}
- .NOFRAME
- {$ENDIF}
- POP R8 // return address
- MOV RDX, RSP // original SP
- ADD ECX, 15
- AND ECX, NOT 15 // round up to keep SP dqword aligned
- CMP ECX, 4088
- JLE @@2
- @@1:
- SUB RSP, 4088
- PUSH RCX // make sure we touch guard page, to grow stack
- SUB ECX, 4096
- JNS @@1
- ADD ECX, 4096
- @@2:
- SUB RSP, RCX
- MOV RAX, RSP // function result = low memory address of block
- PUSH RDX // save original SP, for cleanup
- MOV RDX, RSP
- SUB RDX, 8
- PUSH RDX // save current SP, for sanity check (sp = [sp])
- PUSH R8 // return to caller
- {$ENDIF}
- end;
- { StackFree pops the memory allocated by StackAlloc off the stack.
- - Calling StackFree is optional - SP will be restored when the calling routine
- exits, but it's a good idea to free the stack allocated memory ASAP anyway.
- - StackFree must be called in the same stack context as StackAlloc - not in
- a subroutine or finally block.
- - Multiple StackFree calls must occur in reverse order of their corresponding
- StackAlloc calls.
- - Built-in sanity checks guarantee that an improper call to StackFree will not
- corrupt the stack. Worst case is that the stack block is not released until
- the calling routine exits. }
- procedure StackFree(P: Pointer); register; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$IFDEF TARGET_x86}
- POP ECX // return address
- MOV EDX, DWORD PTR [ESP]
- SUB EAX, 8
- CMP EDX, ESP // sanity check #1 (SP = [SP])
- JNE @Exit
- CMP EDX, EAX // sanity check #2 (P = this stack block)
- JNE @Exit
- MOV ESP, DWORD PTR [ESP+4] // restore previous SP
- @Exit:
- PUSH ECX // return to caller
- {$ENDIF}
- {$IFDEF TARGET_x64}
- {$IFNDEF FPC}
- .NOFRAME
- {$ENDIF}
- POP R8 // return address
- MOV RDX, QWORD PTR [RSP]
- SUB RCX, 16
- CMP RDX, RSP // sanity check #1 (SP = [SP])
- JNE @Exit
- CMP RDX, RCX // sanity check #2 (P = this stack block)
- JNE @Exit
- MOV RSP, QWORD PTR [RSP + 8] // restore previous SP
- @Exit:
- PUSH R8 // return to caller
- {$ENDIF}
- end;
- {$ENDIF}
- {$ENDIF}
- {CPU target and feature Function templates}
- const
- FID_FILLLONGWORD = 0;
- {Complete collection of unit templates}
- var
- Registry: TFunctionRegistry;
- procedure RegisterBindings;
- begin
- Registry := NewRegistry('GR32_LowLevel bindings');
- Registry.RegisterBinding(FID_FILLLONGWORD, @@FillLongWord);
- Registry.Add(FID_FILLLONGWORD, @FillLongWord_Pas, []);
- {$IFNDEF PUREPASCAL}
- Registry.Add(FID_FILLLONGWORD, @FillLongWord_ASM, []);
- Registry.Add(FID_FILLLONGWORD, @FillLongWord_MMX, [ciMMX]);
- Registry.Add(FID_FILLLONGWORD, @FillLongword_SSE2, [ciSSE2]);
- {$ENDIF}
- Registry.RebindAll;
- end;
- initialization
- RegisterBindings;
- end.
|