root / Imaging / ImagingUtility.pas @ 0:95bd93c28625
History | View | Annotate | Download (41.4 kB)
| 1 | {
|
|---|---|
| 2 | $Id: ImagingUtility.pas 86 2007-06-12 22:39:08Z galfar $ |
| 3 | Vampyre Imaging Library |
| 4 | by Marek Mauder |
| 5 | http://imaginglib.sourceforge.net |
| 6 | |
| 7 | The contents of this file are used with permission, subject to the Mozilla |
| 8 | Public License Version 1.1 (the "License"); you may not use this file except |
| 9 | in compliance with the License. You may obtain a copy of the License at |
| 10 | http://www.mozilla.org/MPL/MPL-1.1.html |
| 11 | |
| 12 | Software distributed under the License is distributed on an "AS IS" basis, |
| 13 | WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
| 14 | the specific language governing rights and limitations under the License. |
| 15 | |
| 16 | Alternatively, the contents of this file may be used under the terms of the |
| 17 | GNU Lesser General Public License (the "LGPL License"), in which case the |
| 18 | provisions of the LGPL License are applicable instead of those above. |
| 19 | If you wish to allow use of your version of this file only under the terms |
| 20 | of the LGPL License and not to allow others to use your version of this file |
| 21 | under the MPL, indicate your decision by deleting the provisions above and |
| 22 | replace them with the notice and other provisions required by the LGPL |
| 23 | License. If you do not delete the provisions above, a recipient may use |
| 24 | your version of this file under either the MPL or the LGPL License. |
| 25 | |
| 26 | For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
| 27 | } |
| 28 | |
| 29 | { This unit contains utility functions and types for Imaging library.}
|
| 30 | unit ImagingUtility;
|
| 31 | |
| 32 | {$I ImagingOptions.inc}
|
| 33 | |
| 34 | interface
|
| 35 | |
| 36 | uses
|
| 37 | SysUtils, Classes, Types; |
| 38 | |
| 39 | const
|
| 40 | STrue = 'True';
|
| 41 | SFalse = 'False';
|
| 42 | |
| 43 | type
|
| 44 | TByteArray = array[0..MaxInt - 1] of Byte; |
| 45 | PByteArray = ^TByteArray; |
| 46 | TWordArray = array[0..MaxInt div 2 - 1] of Word; |
| 47 | PWordArray = ^TWordArray; |
| 48 | TLongIntArray = array[0..MaxInt div 4 - 1] of LongInt; |
| 49 | PLongIntArray = ^TLongIntArray; |
| 50 | TLongWordArray = array[0..MaxInt div 4 - 1] of LongWord; |
| 51 | PLongWordArray = ^TLongWordArray; |
| 52 | TInt64Array = array[0..MaxInt div 8 - 1] of Int64; |
| 53 | PInt64Array = ^TInt64Array; |
| 54 | TSingleArray = array[0..MaxInt div 4 - 1] of Single; |
| 55 | PSingleArray = ^TSingleArray; |
| 56 | TBooleanArray = array[0..MaxInt - 1] of Boolean; |
| 57 | PBooleanArray = ^TBooleanArray; |
| 58 | |
| 59 | TWordRec = packed record |
| 60 | case Integer of |
| 61 | 0: (WordValue: Word);
|
| 62 | 1: (Low, High: Byte);
|
| 63 | end;
|
| 64 | PWordRec = ^TWordRec; |
| 65 | TWordRecArray = array[0..MaxInt div 2 - 1] of TWordRec; |
| 66 | PWordRecArray = ^TWordRecArray; |
| 67 | |
| 68 | TLongWordRec = packed record |
| 69 | case Integer of |
| 70 | 0: (LongWordValue: LongWord);
|
| 71 | 1: (Low, High: Word);
|
| 72 | { Array variants - Index 0 means lowest significant byte (word, ...).}
|
| 73 | 2: (Words: array[0..1] of Word); |
| 74 | 3: (Bytes: array[0..3] of Byte); |
| 75 | end;
|
| 76 | PLongWordRec = ^TLongWordRec; |
| 77 | TLongWordRecArray = array[0..MaxInt div 4 - 1] of TLongWordRec; |
| 78 | PLongWordRecArray = ^TLongWordRecArray; |
| 79 | |
| 80 | TInt64Rec = packed record |
| 81 | case Integer of |
| 82 | 0: (Int64Value: Int64);
|
| 83 | 1: (Low, High: LongWord);
|
| 84 | { Array variants - Index 0 means lowest significant byte (word, ...).}
|
| 85 | 2: (Words: array[0..3] of Word); |
| 86 | 3: (Bytes: array[0..7] of Byte); |
| 87 | end;
|
| 88 | PInt64Rec = ^TInt64Rec; |
| 89 | TInt64RecArray = array[0..MaxInt div 8 - 1] of TInt64Rec; |
| 90 | PInt64RecArray = ^TInt64RecArray; |
| 91 | |
| 92 | TFloatHelper = record
|
| 93 | Data1: Int64; |
| 94 | Data2: Int64; |
| 95 | end;
|
| 96 | PFloatHelper = ^TFloatHelper; |
| 97 | |
| 98 | TChar2 = array[0..1] of Char; |
| 99 | TChar3 = array[0..2] of Char; |
| 100 | TChar4 = array[0..3] of Char; |
| 101 | TChar8 = array[0..7] of Char; |
| 102 | |
| 103 | { Options for BuildFileList function:
|
| 104 | flFullNames - file names in result will have full path names |
| 105 | (ExtractFileDir(Path) + FileName) |
| 106 | flRelNames - file names in result will have names relative to |
| 107 | ExtractFileDir(Path) dir |
| 108 | flRecursive - adds files in subdirectories found in Path.} |
| 109 | TFileListOption = (flFullNames, flRelNames, flRecursive); |
| 110 | TFileListOptions = set of TFileListOption; |
| 111 | |
| 112 | |
| 113 | { Frees class instance and sets its reference to nil.}
|
| 114 | procedure FreeAndNil(var Obj); |
| 115 | { Frees pointer and sets it to nil.}
|
| 116 | procedure FreeMemNil(var P); {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 117 | { Replacement of standard System.FreeMem procedure which checks if P is nil
|
| 118 | (this is only needed for Free Pascal, Delphi makes checks in its FreeMem).} |
| 119 | procedure FreeMem(P: Pointer); {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 120 | { Returns current exception object. Do not call outside exception handler.}
|
| 121 | function GetExceptObject: Exception; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 122 | { Returns time value with microsecond resolution. Use for some time counters.}
|
| 123 | function GetTimeMicroseconds: Int64;
|
| 124 | |
| 125 | { Returns file extension (without "." dot)}
|
| 126 | function GetFileExt(const FileName: string): string; |
| 127 | { Returns file name of application's executable.}
|
| 128 | function GetAppExe: string; |
| 129 | { Returns directory where application's exceutable is located without
|
| 130 | path delimiter at the end.} |
| 131 | function GetAppDir:string; |
| 132 | { Returns True if FileName matches given Mask with optional case sensitivity.
|
| 133 | Mask can contain ? and * special characters: ? matches |
| 134 | one character, * matches zero or more characters.} |
| 135 | function MatchFileNameMask(const FileName, Mask: string; CaseSensitive: Boolean = False): Boolean; |
| 136 | { This function fills Files string list with names of files found
|
| 137 | with FindFirst/FindNext functions (See details on Path/Atrr here). |
| 138 | - BuildFileList('c:\*.*', faAnyFile, List, [flRecursive]) returns
|
| 139 | list of all files (only name.ext - no path) on C drive |
| 140 | - BuildFileList('d:\*.*', faDirectory, List, [flFullNames]) returns
|
| 141 | list of all directories (d:\dirxxx) in root of D drive.} |
| 142 | function BuildFileList(Path: string; Attr: LongInt; Files: TStrings; |
| 143 | Options: TFileListOptions = []): Boolean; |
| 144 | { Similar to RTL's Pos function but with optional Offset where search will start.
|
| 145 | This function is in the RTL StrUtils unit but } |
| 146 | function PosEx(const SubStr, S: string; Offset: LongInt = 1): LongInt; |
| 147 | { Same as PosEx but without case sensitivity.}
|
| 148 | function PosNoCase(const SubStr, S: string; Offset: LongInt = 1): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 149 | { Returns a sub-string from S which is followed by
|
| 150 | Sep separator and deletes the sub-string from S including the separator.} |
| 151 | function StrToken(var S: string; Sep: Char): string; |
| 152 | { Same as StrToken but searches from the end of S string.}
|
| 153 | function StrTokenEnd(var S: string; Sep: Char): string; |
| 154 | |
| 155 | { Clamps integer value to range <Min, Max>}
|
| 156 | function ClampInt(Number: LongInt; Min, Max: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 157 | { Clamps float value to range <Min, Max>}
|
| 158 | function ClampFloat(Number: Single; Min, Max: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 159 | { Clamps integer value to Byte boundaries.}
|
| 160 | function ClampToByte(Value: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 161 | { Clamps integer value to Word boundaries.}
|
| 162 | function ClampToWord(Value: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 163 | { Returns True if Num is power of 2.}
|
| 164 | function IsPow2(Num: LongInt): Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 165 | { Returns next power of 2 greater than or equal to Num
|
| 166 | (if Num itself is power of 2 then it retuns Num).} |
| 167 | function NextPow2(Num: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 168 | { Raises 2 to the given integer power (in range [0, 30]).}
|
| 169 | function Pow2Int(Exponent: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 170 | { Raises Base to any power.}
|
| 171 | function Power(const Base, Exponent: Single): Single; |
| 172 | { Returns log base 2 of integer X (max 2^30) or -1 if X is not power of 2.}
|
| 173 | function Log2Int(X: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 174 | { Returns log base 2 of X.}
|
| 175 | function Log2(X: Single): Single;
|
| 176 | { Returns largest integer <= Val (for 5.9 returns 5).}
|
| 177 | function Floor(Value: Single): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 178 | { Returns smallest integer >= Val (for 5.1 returns 6).}
|
| 179 | function Ceil(Value: Single): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 180 | { Returns lesser of two integer numbers.}
|
| 181 | function Min(A, B: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 182 | { Returns lesser of two float numbers.}
|
| 183 | function MinFloat(A, B: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 184 | { Returns greater of two integer numbers.}
|
| 185 | function Max(A, B: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 186 | { Returns greater of two float numbers.}
|
| 187 | function MaxFloat(A, B: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 188 | { Returns result from multiplying Number by Numerator and then dividing by Denominator.
|
| 189 | Denominator must be greater than 0.} |
| 190 | function MulDiv(Number, Numerator, Denominator: Word): Word; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 191 | |
| 192 | { Switches Boolean value.}
|
| 193 | procedure Switch(var Value: Boolean); {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 194 | { If Condition is True then TruePart is retured, otherwise
|
| 195 | FalsePart is returned.} |
| 196 | function Iff(Condition: Boolean; TruePart, FalsePart: LongInt): LongInt; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 197 | { If Condition is True then TruePart is retured, otherwise
|
| 198 | FalsePart is returned.} |
| 199 | function IffUnsigned(Condition: Boolean; TruePart, FalsePart: LongWord): LongWord; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 200 | { If Condition is True then TruePart is retured, otherwise
|
| 201 | FalsePart is returned.} |
| 202 | function Iff(Condition, TruePart, FalsePart: Boolean): Boolean; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 203 | { If Condition is True then TruePart is retured, otherwise
|
| 204 | FalsePart is returned.} |
| 205 | function Iff(Condition: Boolean; const TruePart, FalsePart: string): string; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 206 | { If Condition is True then TruePart is retured, otherwise
|
| 207 | FalsePart is returned.} |
| 208 | function Iff(Condition: Boolean; TruePart, FalsePart: Char): Char; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 209 | { If Condition is True then TruePart is retured, otherwise
|
| 210 | FalsePart is returned.} |
| 211 | function Iff(Condition: Boolean; TruePart, FalsePart: Pointer): Pointer; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 212 | { If Condition is True then TruePart is retured, otherwise
|
| 213 | FalsePart is returned.} |
| 214 | function Iff(Condition: Boolean; const TruePart, FalsePart: Int64): Int64; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 215 | { If Condition is True then TruePart is retured, otherwise
|
| 216 | FalsePart is returned.} |
| 217 | function IffFloat(Condition: Boolean; TruePart, FalsePart: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 218 | { Swaps two Byte values}
|
| 219 | procedure SwapValues(var A, B: Byte); overload; |
| 220 | { Swaps two Word values}
|
| 221 | procedure SwapValues(var A, B: Word); overload; |
| 222 | { Swaps two LongInt values}
|
| 223 | procedure SwapValues(var A, B: LongInt); overload; |
| 224 | { Swaps two Single values}
|
| 225 | procedure SwapValues(var A, B: Single); overload; |
| 226 | { Swaps two LongInt values if necessary to ensure that Min <= Max.}
|
| 227 | procedure SwapMin(var Min, Max: LongInt); {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 228 | { This function returns True if running on little endian machine.}
|
| 229 | function IsLittleEndian: Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 230 | { Swaps byte order of Word value.}
|
| 231 | function SwapEndianWord(Value: Word): Word; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 232 | { Swaps byte order of multiple Word values.}
|
| 233 | procedure SwapEndianWord(P: PWordArray; Count: LongInt); overload; |
| 234 | { Swaps byte order of LongWord value.}
|
| 235 | function SwapEndianLongWord(Value: LongWord): LongWord; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 236 | { Swaps byte order of multiple LongWord values.}
|
| 237 | procedure SwapEndianLongWord(P: PLongWord; Count: LongInt); overload; |
| 238 | { Calculates CRC32 for the given data.}
|
| 239 | procedure CalcCrc32(var Crc: LongWord; Data: Pointer; Size: LongInt); |
| 240 | { Fills given memory with given Byte value. Size is size of buffer in bytes.}
|
| 241 | procedure FillMemoryByte(Data: Pointer; Size: LongInt; Value: Byte);
|
| 242 | { Fills given memory with given Word value. Size is size of buffer in bytes.}
|
| 243 | procedure FillMemoryWord(Data: Pointer; Size: LongInt; Value: Word);
|
| 244 | { Fills given memory with given LongWord value. Size is size of buffer in bytes.}
|
| 245 | procedure FillMemoryLongWord(Data: Pointer; Size: LongInt; Value: LongWord);
|
| 246 | |
| 247 | { Returns how many mipmap levels can be created for image of given size.}
|
| 248 | function GetNumMipMapLevels(Width, Height: LongInt): LongInt;
|
| 249 | { Returns total number of levels of volume texture with given depth and
|
| 250 | mipmap count (this is not depth * mipmaps!).} |
| 251 | function GetVolumeLevelCount(Depth, MipMaps: LongInt): LongInt;
|
| 252 | { Returns rectangle (X, Y, X + Width, Y + Height).}
|
| 253 | function BoundsToRect(X, Y, Width, Height: LongInt): TRect; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 254 | { Returns rectangle (R.Left, R.Top, R.Left + R.Right, R.Top + R.Bottom).}
|
| 255 | function BoundsToRect(const R: TRect): TRect; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 256 | { Returns rectangle (R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top).}
|
| 257 | function RectToBounds(const R: TRect): TRect; overload; {$IFDEF USE_INLINE}inline;{$ENDIF} |
| 258 | { Clips given bounds to Clip rectangle.}
|
| 259 | procedure ClipRectBounds(var X, Y, Width, Height: LongInt; const Clip: TRect); |
| 260 | { Clips given source bounds and dest position. It is used by various CopyRect
|
| 261 | functions that copy rect from one image to another. It handles clipping the same way |
| 262 | as Win32 BitBlt function. } |
| 263 | procedure ClipCopyBounds(var SrcX, SrcY, Width, Height, DstX, DstY: LongInt; |
| 264 | SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect);
|
| 265 | { Clips given source bounds and dest bounds. It is used by various StretchRect
|
| 266 | functions that stretch rectangle of pixels from one image to another. |
| 267 | It handles clipping the same way as Win32 StretchBlt function. } |
| 268 | procedure ClipStretchBounds(var SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY, |
| 269 | DstWidth, DstHeight: LongInt; SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect);
|
| 270 | { Scales one rectangle to fit into another. Proportions are preserved so
|
| 271 | it could be used for 'Stretch To Fit Window' image drawing for instance.} |
| 272 | function ScaleRectToRect(const SourceRect, TargetRect: TRect): TRect; |
| 273 | { Returns True if R1 fits into R2.}
|
| 274 | function RectInRect(const R1, R2: TRect): Boolean; |
| 275 | { Returns True if R1 and R2 intersects.}
|
| 276 | function RectIntersects(const R1, R2: TRect): Boolean; |
| 277 | |
| 278 | { Formats given message for usage in Exception.Create(..). Use only
|
| 279 | in except block - returned message contains message of last raised exception.} |
| 280 | function FormatExceptMsg(const Msg: string; const Args: array of const): string; |
| 281 | { Outputs debug message - shows message dialog in Windows and writes to console
|
| 282 | in Linux/Unix.} |
| 283 | procedure DebugMsg(const Msg: string; const Args: array of const); |
| 284 | |
| 285 | implementation
|
| 286 | |
| 287 | uses
|
| 288 | {$IFDEF MSWINDOWS}
|
| 289 | Windows; |
| 290 | {$ENDIF}
|
| 291 | {$IFDEF UNIX}
|
| 292 | {$IFDEF KYLIX}
|
| 293 | Libc; |
| 294 | {$ELSE}
|
| 295 | Dos, BaseUnix, Unix; |
| 296 | {$ENDIF}
|
| 297 | {$ENDIF}
|
| 298 | |
| 299 | procedure FreeAndNil(var Obj); |
| 300 | var
|
| 301 | Temp: TObject; |
| 302 | begin
|
| 303 | Temp := TObject(Obj); |
| 304 | Pointer(Obj) := nil;
|
| 305 | Temp.Free; |
| 306 | end;
|
| 307 | |
| 308 | procedure FreeMemNil(var P); |
| 309 | begin
|
| 310 | FreeMem(Pointer(P)); |
| 311 | Pointer(P) := nil;
|
| 312 | end;
|
| 313 | |
| 314 | procedure FreeMem(P: Pointer);
|
| 315 | begin
|
| 316 | if P <> nil then |
| 317 | System.FreeMem(P); |
| 318 | end;
|
| 319 | |
| 320 | function GetExceptObject: Exception;
|
| 321 | begin
|
| 322 | Result := Exception(ExceptObject); |
| 323 | end;
|
| 324 | |
| 325 | {$IFDEF MSWINDOWS}
|
| 326 | var
|
| 327 | PerfFrequency: Int64; |
| 328 | InvPerfFrequency: Single; |
| 329 | |
| 330 | function GetTimeMicroseconds: Int64;
|
| 331 | var
|
| 332 | Time: Int64; |
| 333 | begin
|
| 334 | QueryPerformanceCounter(Time); |
| 335 | Result := Round(1000000 * InvPerfFrequency * Time);
|
| 336 | end;
|
| 337 | {$ENDIF}
|
| 338 | |
| 339 | {$IFDEF UNIX}
|
| 340 | function GetTimeMicroseconds: Int64;
|
| 341 | var
|
| 342 | TimeVal: TTimeVal; |
| 343 | begin
|
| 344 | {$IFDEF KYLIX}
|
| 345 | GetTimeOfDay(TimeVal, nil);
|
| 346 | {$ELSE}
|
| 347 | fpGetTimeOfDay(@TimeVal, nil);
|
| 348 | {$ENDIF}
|
| 349 | Result := Int64(TimeVal.tv_sec) * 1000000 + TimeVal.tv_usec;
|
| 350 | end;
|
| 351 | {$ENDIF}
|
| 352 | |
| 353 | {$IFDEF MSDOS}
|
| 354 | function GetTimeMicroseconds: Int64;
|
| 355 | asm
|
| 356 | XOR EAX, EAX
|
| 357 | CLI |
| 358 | OUT $43, AL |
| 359 | MOV EDX, FS:[$46C]
|
| 360 | IN AL, $40 |
| 361 | DB $EB, 0, $EB, 0, $EB, 0 |
| 362 | MOV AH, AL |
| 363 | IN AL, $40 |
| 364 | DB $EB, 0, $EB, 0, $EB, 0 |
| 365 | XCHG AL, AH |
| 366 | NEG AX |
| 367 | MOVZX EDI, AX |
| 368 | STI |
| 369 | MOV EBX, $10000
|
| 370 | MOV EAX, EDX |
| 371 | XOR EDX, EDX
|
| 372 | MUL EBX |
| 373 | ADD EAX, EDI |
| 374 | ADC EDX, 0
|
| 375 | PUSH EDX |
| 376 | PUSH EAX |
| 377 | MOV ECX, $82BF1000
|
| 378 | MOVZX EAX, WORD PTR FS:[$470]
|
| 379 | MUL ECX |
| 380 | MOV ECX, EAX |
| 381 | POP EAX |
| 382 | POP EDX |
| 383 | ADD EAX, ECX |
| 384 | ADC EDX, 0
|
| 385 | end;
|
| 386 | {$ENDIF}
|
| 387 | |
| 388 | function GetFileExt(const FileName: string): string; |
| 389 | begin
|
| 390 | Result := ExtractFileExt(FileName); |
| 391 | if Length(Result) > 1 then |
| 392 | Delete(Result, 1, 1); |
| 393 | end;
|
| 394 | |
| 395 | function GetAppExe: string; |
| 396 | {$IFDEF MSWINDOWS}
|
| 397 | var
|
| 398 | FileName: array[0..MAX_PATH] of Char; |
| 399 | begin
|
| 400 | SetString(Result, FileName, |
| 401 | Windows.GetModuleFileName(MainInstance, FileName, SizeOf(FileName))); |
| 402 | {$ENDIF}
|
| 403 | {$IFDEF UNIX}
|
| 404 | {$IFDEF KYLIX}
|
| 405 | var
|
| 406 | FileName: array[0..FILENAME_MAX] of Char; |
| 407 | begin
|
| 408 | SetString(Result, FileName, |
| 409 | System.GetModuleFileName(MainInstance, FileName, SizeOf(FileName))); |
| 410 | {$ELSE}
|
| 411 | begin
|
| 412 | Result := FExpand(ParamStr(0));
|
| 413 | {$ENDIF}
|
| 414 | {$ENDIF}
|
| 415 | {$IFDEF MSDOS}
|
| 416 | begin
|
| 417 | Result := ParamStr(0);
|
| 418 | {$ENDIF}
|
| 419 | end;
|
| 420 | |
| 421 | function GetAppDir:string; |
| 422 | begin
|
| 423 | Result := ExtractFileDir(GetAppExe); |
| 424 | end;
|
| 425 | |
| 426 | function MatchFileNameMask(const FileName, Mask: string; CaseSensitive: Boolean): Boolean; |
| 427 | var
|
| 428 | MaskLen, KeyLen : LongInt; |
| 429 | |
| 430 | function CharMatch(A, B: Char): Boolean;
|
| 431 | begin
|
| 432 | if CaseSensitive then |
| 433 | Result := A = B |
| 434 | else
|
| 435 | Result := UpCase(A) = UpCase(B); |
| 436 | end;
|
| 437 | |
| 438 | function MatchAt(MaskPos, KeyPos: LongInt): Boolean;
|
| 439 | begin
|
| 440 | while (MaskPos <= MaskLen) and (KeyPos <= KeyLen) do |
| 441 | begin
|
| 442 | case Mask[MaskPos] of |
| 443 | '?' :
|
| 444 | begin
|
| 445 | Inc(MaskPos); |
| 446 | Inc(KeyPos); |
| 447 | end;
|
| 448 | '*' :
|
| 449 | begin
|
| 450 | while (MaskPos <= MaskLen) and (Mask[MaskPos] = '*') do |
| 451 | Inc(MaskPos); |
| 452 | if MaskPos > MaskLen then |
| 453 | begin
|
| 454 | Result := True; |
| 455 | Exit; |
| 456 | end;
|
| 457 | repeat
|
| 458 | if MatchAt(MaskPos, KeyPos) then |
| 459 | begin
|
| 460 | Result := True; |
| 461 | Exit; |
| 462 | end;
|
| 463 | Inc(KeyPos); |
| 464 | until KeyPos > KeyLen;
|
| 465 | Result := False; |
| 466 | Exit; |
| 467 | end;
|
| 468 | else
|
| 469 | if not CharMatch(Mask[MaskPos], FileName[KeyPos]) then |
| 470 | begin
|
| 471 | Result := False; |
| 472 | Exit; |
| 473 | end
|
| 474 | else
|
| 475 | begin
|
| 476 | Inc(MaskPos); |
| 477 | Inc(KeyPos); |
| 478 | end;
|
| 479 | end;
|
| 480 | end;
|
| 481 | |
| 482 | while (MaskPos <= MaskLen) and (Mask[MaskPos] in ['?', '*']) do |
| 483 | Inc(MaskPos); |
| 484 | if (MaskPos <= MaskLen) or (KeyPos <= KeyLen) then |
| 485 | begin
|
| 486 | Result := False; |
| 487 | Exit; |
| 488 | end;
|
| 489 | |
| 490 | Result := True; |
| 491 | end;
|
| 492 | |
| 493 | begin
|
| 494 | MaskLen := Length(Mask); |
| 495 | KeyLen := Length(FileName); |
| 496 | if MaskLen = 0 then |
| 497 | begin
|
| 498 | Result := True; |
| 499 | Exit; |
| 500 | end;
|
| 501 | Result := MatchAt(1, 1); |
| 502 | end;
|
| 503 | |
| 504 | function BuildFileList(Path: string; Attr: LongInt; |
| 505 | Files: TStrings; Options: TFileListOptions): Boolean; |
| 506 | var
|
| 507 | FileMask: string;
|
| 508 | RootDir: string;
|
| 509 | Folders: TStringList; |
| 510 | CurrentItem: LongInt; |
| 511 | Counter: LongInt; |
| 512 | LocAttr: LongInt; |
| 513 | |
| 514 | procedure BuildFolderList;
|
| 515 | var
|
| 516 | FindInfo: TSearchRec; |
| 517 | Rslt: LongInt; |
| 518 | begin
|
| 519 | Counter := Folders.Count - 1;
|
| 520 | CurrentItem := 0;
|
| 521 | while CurrentItem <= Counter do |
| 522 | begin
|
| 523 | // Searching for subfolders
|
| 524 | Rslt := SysUtils.FindFirst(Folders[CurrentItem] + '*', faDirectory, FindInfo);
|
| 525 | try
|
| 526 | while Rslt = 0 do |
| 527 | begin
|
| 528 | if (FindInfo.Name <> '.') and (FindInfo.Name <> '..') and |
| 529 | (FindInfo.Attr and faDirectory = faDirectory) then |
| 530 | Folders.Add(Folders[CurrentItem] + FindInfo.Name + PathDelim); |
| 531 | Rslt := SysUtils.FindNext(FindInfo); |
| 532 | end;
|
| 533 | finally
|
| 534 | SysUtils.FindClose(FindInfo); |
| 535 | end;
|
| 536 | Counter := Folders.Count - 1;
|
| 537 | Inc(CurrentItem); |
| 538 | end;
|
| 539 | end;
|
| 540 | |
| 541 | procedure FillFileList(CurrentCounter: LongInt);
|
| 542 | var
|
| 543 | FindInfo: TSearchRec; |
| 544 | Res: LongInt; |
| 545 | CurrentFolder: string;
|
| 546 | begin
|
| 547 | CurrentFolder := Folders[CurrentCounter]; |
| 548 | Res := SysUtils.FindFirst(CurrentFolder + FileMask, LocAttr, FindInfo); |
| 549 | if flRelNames in Options then |
| 550 | CurrentFolder := ExtractRelativePath(RootDir, CurrentFolder); |
| 551 | try
|
| 552 | while Res = 0 do |
| 553 | begin
|
| 554 | if (FindInfo.Name <> '.') and (FindInfo.Name <> '..') then |
| 555 | begin
|
| 556 | if (flFullNames in Options) or (flRelNames in Options) then |
| 557 | Files.Add(CurrentFolder + FindInfo.Name) |
| 558 | else
|
| 559 | Files.Add(FindInfo.Name); |
| 560 | end;
|
| 561 | Res := SysUtils.FindNext(FindInfo); |
| 562 | end;
|
| 563 | finally
|
| 564 | SysUtils.FindClose(FindInfo); |
| 565 | end;
|
| 566 | end;
|
| 567 | |
| 568 | begin
|
| 569 | FileMask := ExtractFileName(Path); |
| 570 | RootDir := ExtractFilePath(Path); |
| 571 | Folders := TStringList.Create; |
| 572 | Folders.Add(RootDir); |
| 573 | Files.Clear; |
| 574 | {$IFDEF DCC}
|
| 575 | {$WARN SYMBOL_PLATFORM OFF}
|
| 576 | {$ENDIF}
|
| 577 | if Attr = faAnyFile then |
| 578 | LocAttr := faSysFile or faHidden or faArchive or faReadOnly |
| 579 | else
|
| 580 | LocAttr := Attr; |
| 581 | {$IFDEF DCC}
|
| 582 | {$WARN SYMBOL_PLATFORM ON}
|
| 583 | {$ENDIF}
|
| 584 | // Here's the recursive search for nested folders
|
| 585 | if flRecursive in Options then |
| 586 | BuildFolderList; |
| 587 | if Attr <> faDirectory then |
| 588 | for Counter := 0 to Folders.Count - 1 do |
| 589 | FillFileList(Counter) |
| 590 | else
|
| 591 | Files.AddStrings(Folders); |
| 592 | Folders.Free; |
| 593 | Result := True; |
| 594 | end;
|
| 595 | |
| 596 | function PosEx(const SubStr, S: string; Offset: LongInt = 1): LongInt; |
| 597 | {$IFDEF USE_ASM}
|
| 598 | asm
|
| 599 | // The Original ASM Code is (C) Fastcode project.
|
| 600 | test eax, eax |
| 601 | jz @Nil
|
| 602 | test edx, edx |
| 603 | jz @Nil
|
| 604 | dec ecx |
| 605 | jl @Nil
|
| 606 | |
| 607 | push esi |
| 608 | push ebx |
| 609 | |
| 610 | mov esi, [edx-4] //Length(Str) |
| 611 | mov ebx, [eax-4] //Length(Substr) |
| 612 | sub esi, ecx //effective length of Str
|
| 613 | add edx, ecx //addr of the first char at starting position
|
| 614 | cmp esi, ebx |
| 615 | jl @Past //jump if EffectiveLength(Str)<Length(Substr)
|
| 616 | test ebx, ebx |
| 617 | jle @Past //jump if Length(Substr)<=0
|
| 618 | |
| 619 | add esp, -12
|
| 620 | add ebx, -1 //Length(Substr)-1 |
| 621 | add esi, edx //addr of the terminator
|
| 622 | add edx, ebx //addr of the last char at starting position
|
| 623 | mov [esp+8], esi //save addr of the terminator |
| 624 | add eax, ebx //addr of the last char of Substr
|
| 625 | sub ecx, edx //-@Str[Length(Substr)]
|
| 626 | neg ebx //-(Length(Substr)-1)
|
| 627 | mov [esp+4], ecx //save -@Str[Length(Substr)] |
| 628 | mov [esp], ebx //save -(Length(Substr)-1)
|
| 629 | movzx ecx, byte ptr [eax] //the last char of Substr
|
| 630 | |
| 631 | @Loop: |
| 632 | cmp cl, [edx] |
| 633 | jz @Test0 |
| 634 | @AfterTest0: |
| 635 | cmp cl, [edx+1]
|
| 636 | jz @TestT |
| 637 | @AfterTestT: |
| 638 | add edx, 4
|
| 639 | cmp edx, [esp+8]
|
| 640 | jb @Continue |
| 641 | @EndLoop: |
| 642 | add edx, -2
|
| 643 | cmp edx, [esp+8]
|
| 644 | jb @Loop |
| 645 | @Exit: |
| 646 | add esp, 12
|
| 647 | @Past: |
| 648 | pop ebx |
| 649 | pop esi |
| 650 | @Nil:
|
| 651 | xor eax, eax
|
| 652 | ret |
| 653 | @Continue: |
| 654 | cmp cl, [edx-2]
|
| 655 | jz @Test2 |
| 656 | cmp cl, [edx-1]
|
| 657 | jnz @Loop |
| 658 | @Test1: |
| 659 | add edx, 1
|
| 660 | @Test2: |
| 661 | add edx, -2
|
| 662 | @Test0: |
| 663 | add edx, -1
|
| 664 | @TestT: |
| 665 | mov esi, [esp] |
| 666 | test esi, esi |
| 667 | jz @Found |
| 668 | @String:
|
| 669 | movzx ebx, word ptr [esi+eax] |
| 670 | cmp bx, word ptr [esi+edx+1]
|
| 671 | jnz @AfterTestT |
| 672 | cmp esi, -2
|
| 673 | jge @Found |
| 674 | movzx ebx, word ptr [esi+eax+2]
|
| 675 | cmp bx, word ptr [esi+edx+3]
|
| 676 | jnz @AfterTestT |
| 677 | add esi, 4
|
| 678 | jl @String
|
| 679 | @Found: |
| 680 | mov eax, [esp+4]
|
| 681 | add edx, 2
|
| 682 | |
| 683 | cmp edx, [esp+8]
|
| 684 | ja @Exit |
| 685 | |
| 686 | add esp, 12
|
| 687 | add eax, edx |
| 688 | pop ebx |
| 689 | pop esi |
| 690 | end;
|
| 691 | {$ELSE}
|
| 692 | var
|
| 693 | I, X: LongInt; |
| 694 | Len, LenSubStr: LongInt; |
| 695 | begin
|
| 696 | I := Offset; |
| 697 | LenSubStr := Length(SubStr); |
| 698 | Len := Length(S) - LenSubStr + 1;
|
| 699 | while I <= Len do |
| 700 | begin
|
| 701 | if S[I] = SubStr[1] then |
| 702 | begin
|
| 703 | X := 1;
|
| 704 | while (X < LenSubStr) and (S[I + X] = SubStr[X + 1]) do |
| 705 | Inc(X); |
| 706 | if (X = LenSubStr) then |
| 707 | begin
|
| 708 | Result := I; |
| 709 | Exit; |
| 710 | end;
|
| 711 | end;
|
| 712 | Inc(I); |
| 713 | end;
|
| 714 | Result := 0;
|
| 715 | end;
|
| 716 | {$ENDIF}
|
| 717 | |
| 718 | function PosNoCase(const SubStr, S: string; Offset: LongInt): LongInt; |
| 719 | begin
|
| 720 | Result := PosEx(LowerCase(SubStr), LowerCase(S), Offset); |
| 721 | end;
|
| 722 | |
| 723 | function StrToken(var S: string; Sep: Char): string; |
| 724 | var
|
| 725 | I: LongInt; |
| 726 | begin
|
| 727 | I := Pos(Sep, S); |
| 728 | if I <> 0 then |
| 729 | begin
|
| 730 | Result := Copy(S, 1, I - 1); |
| 731 | Delete(S, 1, I);
|
| 732 | end
|
| 733 | else
|
| 734 | begin
|
| 735 | Result := S; |
| 736 | S := '';
|
| 737 | end;
|
| 738 | end;
|
| 739 | |
| 740 | function StrTokenEnd(var S: string; Sep: Char): string; |
| 741 | var
|
| 742 | I, J: LongInt; |
| 743 | begin
|
| 744 | J := 0;
|
| 745 | I := Pos(Sep, S); |
| 746 | while I <> 0 do |
| 747 | begin
|
| 748 | J := I; |
| 749 | I := PosEx(Sep, S, J + 1);
|
| 750 | end;
|
| 751 | if J <> 0 then |
| 752 | begin
|
| 753 | Result := Copy(S, J + 1, MaxInt);
|
| 754 | Delete(S, J, MaxInt); |
| 755 | end
|
| 756 | else
|
| 757 | begin
|
| 758 | Result := S; |
| 759 | S := '';
|
| 760 | end;
|
| 761 | end;
|
| 762 | |
| 763 | function ClampInt(Number: LongInt; Min, Max: LongInt): LongInt;
|
| 764 | begin
|
| 765 | Result := Number; |
| 766 | if Result < Min then |
| 767 | Result := Min |
| 768 | else
|
| 769 | if Result > Max then |
| 770 | Result := Max; |
| 771 | end;
|
| 772 | |
| 773 | function ClampFloat(Number: Single; Min, Max: Single): Single;
|
| 774 | begin
|
| 775 | Result := Number; |
| 776 | if Result < Min then |
| 777 | Result := Min |
| 778 | else
|
| 779 | if Result > Max then |
| 780 | Result := Max; |
| 781 | end;
|
| 782 | |
| 783 | function ClampToByte(Value: LongInt): LongInt;
|
| 784 | begin
|
| 785 | Result := Value; |
| 786 | if Result > 255 then |
| 787 | Result := 255
|
| 788 | else if Result < 0 then |
| 789 | Result := 0;
|
| 790 | end;
|
| 791 | |
| 792 | function ClampToWord(Value: LongInt): LongInt;
|
| 793 | begin
|
| 794 | Result := Value; |
| 795 | if Result > 65535 then |
| 796 | Result := 65535
|
| 797 | else if Result < 0 then |
| 798 | Result := 0;
|
| 799 | end;
|
| 800 | |
| 801 | function IsPow2(Num: LongInt): Boolean;
|
| 802 | begin
|
| 803 | Result := (Num and -Num) = Num;
|
| 804 | end;
|
| 805 | |
| 806 | function NextPow2(Num: LongInt): LongInt;
|
| 807 | begin
|
| 808 | Result := Num and -Num;
|
| 809 | while (Result < Num) do |
| 810 | Result := Result shl 1; |
| 811 | end;
|
| 812 | |
| 813 | function Pow2Int(Exponent: LongInt): LongInt;
|
| 814 | begin
|
| 815 | Result := 1 shl Exponent; |
| 816 | end;
|
| 817 | |
| 818 | function Power(const Base, Exponent: Single): Single; |
| 819 | begin
|
| 820 | if Exponent = 0.0 then |
| 821 | Result := 1.0
|
| 822 | else if (Base = 0.0) and (Exponent > 0.0) then |
| 823 | Result := 0.0
|
| 824 | else
|
| 825 | Result := Exp(Exponent * Ln(Base)); |
| 826 | end;
|
| 827 | |
| 828 | function Log2Int(X: LongInt): LongInt;
|
| 829 | begin
|
| 830 | case X of |
| 831 | 1: Result := 0; |
| 832 | 2: Result := 1; |
| 833 | 4: Result := 2; |
| 834 | 8: Result := 3; |
| 835 | 16: Result := 4; |
| 836 | 32: Result := 5; |
| 837 | 64: Result := 6; |
| 838 | 128: Result := 7; |
| 839 | 256: Result := 8; |
| 840 | 512: Result := 9; |
| 841 | 1024: Result := 10; |
| 842 | 2048: Result := 11; |
| 843 | 4096: Result := 12; |
| 844 | 8192: Result := 13; |
| 845 | 16384: Result := 14; |
| 846 | 32768: Result := 15; |
| 847 | 65536: Result := 16; |
| 848 | 131072: Result := 17; |
| 849 | 262144: Result := 18; |
| 850 | 524288: Result := 19; |
| 851 | 1048576: Result := 20; |
| 852 | 2097152: Result := 21; |
| 853 | 4194304: Result := 22; |
| 854 | 8388608: Result := 23; |
| 855 | 16777216: Result := 24; |
| 856 | 33554432: Result := 25; |
| 857 | 67108864: Result := 26; |
| 858 | 134217728: Result := 27; |
| 859 | 268435456: Result := 28; |
| 860 | 536870912: Result := 29; |
| 861 | 1073741824: Result := 30; |
| 862 | else
|
| 863 | Result := -1;
|
| 864 | end;
|
| 865 | end;
|
| 866 | |
| 867 | function Log2(X: Single): Single;
|
| 868 | const
|
| 869 | Ln2: Single = 0.6931471;
|
| 870 | begin
|
| 871 | Result := Ln(X) / Ln2; |
| 872 | end;
|
| 873 | |
| 874 | function Floor(Value: Single): LongInt;
|
| 875 | begin
|
| 876 | Result := Trunc(Value); |
| 877 | if Frac(Value) < 0.0 then |
| 878 | Dec(Result); |
| 879 | end;
|
| 880 | |
| 881 | function Ceil(Value: Single): LongInt;
|
| 882 | begin
|
| 883 | Result := Trunc(Value); |
| 884 | if Frac(Value) > 0.0 then |
| 885 | Inc(Result); |
| 886 | end;
|
| 887 | |
| 888 | procedure Switch(var Value: Boolean); |
| 889 | begin
|
| 890 | Value := not Value;
|
| 891 | end;
|
| 892 | |
| 893 | function Iff(Condition: Boolean; TruePart, FalsePart: LongInt): LongInt;
|
| 894 | begin
|
| 895 | if Condition then |
| 896 | Result := TruePart |
| 897 | else
|
| 898 | Result := FalsePart; |
| 899 | end;
|
| 900 | |
| 901 | function IffUnsigned(Condition: Boolean; TruePart, FalsePart: LongWord): LongWord;
|
| 902 | begin
|
| 903 | if Condition then |
| 904 | Result := TruePart |
| 905 | else
|
| 906 | Result := FalsePart; |
| 907 | end;
|
| 908 | |
| 909 | function Iff(Condition, TruePart, FalsePart: Boolean): Boolean;
|
| 910 | begin
|
| 911 | if Condition then |
| 912 | Result := TruePart |
| 913 | else
|
| 914 | Result := FalsePart; |
| 915 | end;
|
| 916 | |
| 917 | function Iff(Condition: Boolean; const TruePart, FalsePart: string): string; |
| 918 | begin
|
| 919 | if Condition then |
| 920 | Result := TruePart |
| 921 | else
|
| 922 | Result := FalsePart; |
| 923 | end;
|
| 924 | |
| 925 | function Iff(Condition: Boolean; TruePart, FalsePart: Char): Char;
|
| 926 | begin
|
| 927 | if Condition then |
| 928 | Result := TruePart |
| 929 | else
|
| 930 | Result := FalsePart; |
| 931 | end;
|
| 932 | |
| 933 | function Iff(Condition: Boolean; TruePart, FalsePart: Pointer): Pointer;
|
| 934 | begin
|
| 935 | if Condition then |
| 936 | Result := TruePart |
| 937 | else
|
| 938 | Result := FalsePart; |
| 939 | end;
|
| 940 | |
| 941 | function Iff(Condition: Boolean; const TruePart, FalsePart: Int64): Int64; |
| 942 | begin
|
| 943 | if Condition then |
| 944 | Result := TruePart |
| 945 | else
|
| 946 | Result := FalsePart; |
| 947 | end;
|
| 948 | |
| 949 | function IffFloat(Condition: Boolean; TruePart, FalsePart: Single): Single;
|
| 950 | begin
|
| 951 | if Condition then |
| 952 | Result := TruePart |
| 953 | else
|
| 954 | Result := FalsePart; |
| 955 | end;
|
| 956 | |
| 957 | procedure SwapValues(var A, B: Byte); |
| 958 | var
|
| 959 | Tmp: Byte; |
| 960 | begin
|
| 961 | Tmp := A; |
| 962 | A := B; |
| 963 | B := Tmp; |
| 964 | end;
|
| 965 | |
| 966 | procedure SwapValues(var A, B: Word); |
| 967 | var
|
| 968 | Tmp: Word; |
| 969 | begin
|
| 970 | Tmp := A; |
| 971 | A := B; |
| 972 | B := Tmp; |
| 973 | end;
|
| 974 | |
| 975 | procedure SwapValues(var A, B: LongInt); |
| 976 | var
|
| 977 | Tmp: LongInt; |
| 978 | begin
|
| 979 | Tmp := A; |
| 980 | A := B; |
| 981 | B := Tmp; |
| 982 | end;
|
| 983 | |
| 984 | procedure SwapValues(var A, B: Single); |
| 985 | var
|
| 986 | Tmp: Single; |
| 987 | begin
|
| 988 | Tmp := A; |
| 989 | A := B; |
| 990 | B := Tmp; |
| 991 | end;
|
| 992 | |
| 993 | procedure SwapMin(var Min, Max: LongInt); |
| 994 | var
|
| 995 | Tmp: LongInt; |
| 996 | begin
|
| 997 | if Min > Max then |
| 998 | begin
|
| 999 | Tmp := Min; |
| 1000 | Min := Max; |
| 1001 | Max := Tmp; |
| 1002 | end;
|
| 1003 | end;
|
| 1004 | |
| 1005 | function Min(A, B: LongInt): LongInt;
|
| 1006 | begin
|
| 1007 | if A < B then |
| 1008 | Result := A |
| 1009 | else
|
| 1010 | Result := B; |
| 1011 | end;
|
| 1012 | |
| 1013 | function MinFloat(A, B: Single): Single;
|
| 1014 | begin
|
| 1015 | if A < B then |
| 1016 | Result := A |
| 1017 | else
|
| 1018 | Result := B; |
| 1019 | end;
|
| 1020 | |
| 1021 | function Max(A, B: LongInt): LongInt;
|
| 1022 | begin
|
| 1023 | if A > B then |
| 1024 | Result := A |
| 1025 | else
|
| 1026 | Result := B; |
| 1027 | end;
|
| 1028 | |
| 1029 | function MaxFloat(A, B: Single): Single;
|
| 1030 | begin
|
| 1031 | if A > B then |
| 1032 | Result := A |
| 1033 | else
|
| 1034 | Result := B; |
| 1035 | end;
|
| 1036 | |
| 1037 | function MulDiv(Number, Numerator, Denominator: Word): Word;
|
| 1038 | {$IF Defined(USE_ASM) and (not Defined(USE_INLINE))}
|
| 1039 | asm
|
| 1040 | MUL DX |
| 1041 | DIV CX
|
| 1042 | end;
|
| 1043 | {$ELSE}
|
| 1044 | begin
|
| 1045 | Result := Number * Numerator div Denominator;
|
| 1046 | end;
|
| 1047 | {$IFEND}
|
| 1048 | |
| 1049 | function IsLittleEndian: Boolean;
|
| 1050 | var
|
| 1051 | W: Word; |
| 1052 | begin
|
| 1053 | W := $00FF;
|
| 1054 | Result := PByte(@W)^ = $FF;
|
| 1055 | end;
|
| 1056 | |
| 1057 | function SwapEndianWord(Value: Word): Word;
|
| 1058 | {$IF Defined(USE_ASM) and (not Defined(USE_INLINE))}
|
| 1059 | asm
|
| 1060 | XCHG AH, AL |
| 1061 | end;
|
| 1062 | {$ELSE}
|
| 1063 | begin
|
| 1064 | TWordRec(Result).Low := TWordRec(Value).High; |
| 1065 | TWordRec(Result).High := TWordRec(Value).Low; |
| 1066 | end;
|
| 1067 | {$IFEND}
|
| 1068 | |
| 1069 | procedure SwapEndianWord(P: PWordArray; Count: LongInt);
|
| 1070 | {$IFDEF USE_ASM}
|
| 1071 | asm
|
| 1072 | @Loop: |
| 1073 | MOV CX, [EAX] |
| 1074 | XCHG CH, CL |
| 1075 | MOV [EAX], CX |
| 1076 | ADD EAX, 2
|
| 1077 | DEC EDX |
| 1078 | JNZ @Loop |
| 1079 | end;
|
| 1080 | {$ELSE}
|
| 1081 | var
|
| 1082 | I: LongInt; |
| 1083 | Temp: Word; |
| 1084 | begin
|
| 1085 | for I := 0 to Count - 1 do |
| 1086 | begin
|
| 1087 | Temp := P[I]; |
| 1088 | TWordRec(P[I]).Low := TWordRec(Temp).High; |
| 1089 | TWordRec(P[I]).High := TWordRec(Temp).Low; |
| 1090 | end;
|
| 1091 | end;
|
| 1092 | {$ENDIF}
|
| 1093 | |
| 1094 | function SwapEndianLongWord(Value: LongWord): LongWord;
|
| 1095 | {$IF Defined(USE_ASM) and (not Defined(USE_INLINE))}
|
| 1096 | asm
|
| 1097 | BSWAP EAX |
| 1098 | end;
|
| 1099 | {$ELSE}
|
| 1100 | begin
|
| 1101 | TLongWordRec(Result).Bytes[0] := TLongWordRec(Value).Bytes[3]; |
| 1102 | TLongWordRec(Result).Bytes[1] := TLongWordRec(Value).Bytes[2]; |
| 1103 | TLongWordRec(Result).Bytes[2] := TLongWordRec(Value).Bytes[1]; |
| 1104 | TLongWordRec(Result).Bytes[3] := TLongWordRec(Value).Bytes[0]; |
| 1105 | end;
|
| 1106 | {$IFEND}
|
| 1107 | |
| 1108 | procedure SwapEndianLongWord(P: PLongWord; Count: LongInt);
|
| 1109 | {$IFDEF USE_ASM}
|
| 1110 | asm
|
| 1111 | @Loop: |
| 1112 | MOV ECX, [EAX] |
| 1113 | BSWAP ECX |
| 1114 | MOV [EAX], ECX |
| 1115 | ADD EAX, 4
|
| 1116 | DEC EDX |
| 1117 | JNZ @Loop |
| 1118 | end;
|
| 1119 | {$ELSE}
|
| 1120 | var
|
| 1121 | I: LongInt; |
| 1122 | Temp: LongWord; |
| 1123 | begin
|
| 1124 | for I := 0 to Count - 1 do |
| 1125 | begin
|
| 1126 | Temp := PLongWordArray(P)[I]; |
| 1127 | TLongWordRec(PLongWordArray(P)[I]).Bytes[0] := TLongWordRec(Temp).Bytes[3]; |
| 1128 | TLongWordRec(PLongWordArray(P)[I]).Bytes[1] := TLongWordRec(Temp).Bytes[2]; |
| 1129 | TLongWordRec(PLongWordArray(P)[I]).Bytes[2] := TLongWordRec(Temp).Bytes[1]; |
| 1130 | TLongWordRec(PLongWordArray(P)[I]).Bytes[3] := TLongWordRec(Temp).Bytes[0]; |
| 1131 | end;
|
| 1132 | end;
|
| 1133 | {$ENDIF}
|
| 1134 | |
| 1135 | type
|
| 1136 | TCrcTable = array[Byte] of LongWord; |
| 1137 | var
|
| 1138 | CrcTable: TCrcTable; |
| 1139 | |
| 1140 | procedure InitCrcTable;
|
| 1141 | const
|
| 1142 | Polynom = $EDB88320;
|
| 1143 | var
|
| 1144 | I, J: LongInt; |
| 1145 | C: LongWord; |
| 1146 | begin
|
| 1147 | for I := 0 to 255 do |
| 1148 | begin
|
| 1149 | C := I; |
| 1150 | for J := 0 to 7 do |
| 1151 | begin
|
| 1152 | if (C and $01) <> 0 then |
| 1153 | C := Polynom xor (C shr 1) |
| 1154 | else
|
| 1155 | C := C shr 1; |
| 1156 | end;
|
| 1157 | CrcTable[I] := C; |
| 1158 | end;
|
| 1159 | end;
|
| 1160 | |
| 1161 | procedure CalcCrc32(var Crc: LongWord; Data: Pointer; Size: LongInt); |
| 1162 | var
|
| 1163 | I: LongInt; |
| 1164 | B: PByte; |
| 1165 | begin
|
| 1166 | B := Data; |
| 1167 | for I := 0 to Size - 1 do |
| 1168 | begin
|
| 1169 | Crc := (Crc shr 8) xor CrcTable[B^ xor Byte(Crc)]; |
| 1170 | Inc(B); |
| 1171 | end
|
| 1172 | end;
|
| 1173 | |
| 1174 | procedure FillMemoryByte(Data: Pointer; Size: LongInt; Value: Byte);
|
| 1175 | {$IFDEF USE_ASM}
|
| 1176 | asm
|
| 1177 | PUSH EDI |
| 1178 | MOV EDI, EAX |
| 1179 | MOV EAX, ECX |
| 1180 | MOV AH, AL |
| 1181 | MOV CX, AX |
| 1182 | SHL EAX, 16 |
| 1183 | MOV AX, CX |
| 1184 | MOV ECX, EDX |
| 1185 | SAR ECX, 2
|
| 1186 | JS @Exit |
| 1187 | REP STOSD |
| 1188 | MOV ECX, EDX |
| 1189 | AND ECX, 3 |
| 1190 | REP STOSB |
| 1191 | POP EDI |
| 1192 | @Exit: |
| 1193 | end;
|
| 1194 | {$ELSE}
|
| 1195 | begin
|
| 1196 | FillChar(Data^, Size, Value); |
| 1197 | end;
|
| 1198 | {$ENDIF}
|
| 1199 | |
| 1200 | procedure FillMemoryWord(Data: Pointer; Size: LongInt; Value: Word);
|
| 1201 | {$IFDEF USE_ASM}
|
| 1202 | asm
|
| 1203 | PUSH EDI |
| 1204 | PUSH EBX |
| 1205 | MOV EBX, EDX |
| 1206 | MOV EDI, EAX |
| 1207 | MOV EAX, ECX |
| 1208 | MOV CX, AX |
| 1209 | SHL EAX, 16 |
| 1210 | MOV AX, CX |
| 1211 | MOV ECX, EDX |
| 1212 | SHR ECX, 2 |
| 1213 | JZ @Word |
| 1214 | REP STOSD |
| 1215 | @Word: |
| 1216 | MOV ECX, EBX |
| 1217 | AND ECX, 2 |
| 1218 | JZ @Byte |
| 1219 | MOV [EDI], AX |
| 1220 | ADD EDI, 2
|
| 1221 | @Byte: |
| 1222 | MOV ECX, EBX |
| 1223 | AND ECX, 1 |
| 1224 | JZ @Exit |
| 1225 | MOV [EDI], AL |
| 1226 | @Exit: |
| 1227 | POP EBX |
| 1228 | POP EDI |
| 1229 | end;
|
| 1230 | {$ELSE}
|
| 1231 | var
|
| 1232 | I, V: LongWord; |
| 1233 | begin
|
| 1234 | V := Value * $10000 + Value;
|
| 1235 | for I := 0 to Size div 4 - 1 do |
| 1236 | PLongWordArray(Data)[I] := V; |
| 1237 | case Size mod 4 of |
| 1238 | 1: PByteArray(Data)[Size - 1] := Lo(Value); |
| 1239 | 2: PWordArray(Data)[Size div 2] := Value; |
| 1240 | 3:
|
| 1241 | begin
|
| 1242 | PWordArray(Data)[Size div 2 - 1] := Value; |
| 1243 | PByteArray(Data)[Size - 1] := Lo(Value);
|
| 1244 | end;
|
| 1245 | end;
|
| 1246 | end;
|
| 1247 | {$ENDIF}
|
| 1248 | |
| 1249 | procedure FillMemoryLongWord(Data: Pointer; Size: LongInt; Value: LongWord);
|
| 1250 | {$IFDEF USE_ASM}
|
| 1251 | asm
|
| 1252 | PUSH EDI |
| 1253 | PUSH EBX |
| 1254 | MOV EBX, EDX |
| 1255 | MOV EDI, EAX |
| 1256 | MOV EAX, ECX |
| 1257 | MOV ECX, EDX |
| 1258 | SHR ECX, 2 |
| 1259 | JZ @Word |
| 1260 | REP STOSD |
| 1261 | @Word: |
| 1262 | MOV ECX, EBX |
| 1263 | AND ECX, 2 |
| 1264 | JZ @Byte |
| 1265 | MOV [EDI], AX |
| 1266 | ADD EDI, 2
|
| 1267 | @Byte: |
| 1268 | MOV ECX, EBX |
| 1269 | AND ECX, 1 |
| 1270 | JZ @Exit |
| 1271 | MOV [EDI], AL |
| 1272 | @Exit: |
| 1273 | POP EBX |
| 1274 | POP EDI |
| 1275 | end;
|
| 1276 | {$ELSE}
|
| 1277 | var
|
| 1278 | I: LongInt; |
| 1279 | begin
|
| 1280 | for I := 0 to Size div 4 - 1 do |
| 1281 | PLongWordArray(Data)[I] := Value; |
| 1282 | case Size mod 4 of |
| 1283 | 1: PByteArray(Data)[Size - 1] := TLongWordRec(Value).Bytes[0]; |
| 1284 | 2: PWordArray(Data)[Size div 2] := TLongWordRec(Value).Words[0]; |
| 1285 | 3:
|
| 1286 | begin
|
| 1287 | PWordArray(Data)[Size div 2 - 1] := TLongWordRec(Value).Words[0]; |
| 1288 | PByteArray(Data)[Size - 1] := TLongWordRec(Value).Bytes[0]; |
| 1289 | end;
|
| 1290 | end;
|
| 1291 | end;
|
| 1292 | {$ENDIF}
|
| 1293 | |
| 1294 | function GetNumMipMapLevels(Width, Height: LongInt): LongInt;
|
| 1295 | begin
|
| 1296 | Result := 0;
|
| 1297 | if (Width > 0) and (Height > 0) then |
| 1298 | begin
|
| 1299 | Result := 1;
|
| 1300 | while (Width <> 1) or (Height <> 1) do |
| 1301 | begin
|
| 1302 | Width := Width div 2; |
| 1303 | Height := Height div 2; |
| 1304 | if Width < 1 then Width := 1; |
| 1305 | if Height < 1 then Height := 1; |
| 1306 | Inc(Result); |
| 1307 | end;
|
| 1308 | end;
|
| 1309 | end;
|
| 1310 | |
| 1311 | function GetVolumeLevelCount(Depth, MipMaps: LongInt): LongInt;
|
| 1312 | var
|
| 1313 | I: LongInt; |
| 1314 | begin
|
| 1315 | Result := Depth; |
| 1316 | for I := 1 to MipMaps - 1 do |
| 1317 | Inc(Result, ClampInt(Depth shr I, 1, Depth)); |
| 1318 | end;
|
| 1319 | |
| 1320 | function BoundsToRect(X, Y, Width, Height: LongInt): TRect;
|
| 1321 | begin
|
| 1322 | Result.Left := X; |
| 1323 | Result.Top := Y; |
| 1324 | Result.Right := X + Width; |
| 1325 | Result.Bottom := Y + Height; |
| 1326 | end;
|
| 1327 | |
| 1328 | function BoundsToRect(const R: TRect): TRect; |
| 1329 | begin
|
| 1330 | Result.Left := R.Left; |
| 1331 | Result.Top := R.Top; |
| 1332 | Result.Right := R.Left + R.Right; |
| 1333 | Result.Bottom := R.Top + R.Bottom; |
| 1334 | end;
|
| 1335 | |
| 1336 | function RectToBounds(const R: TRect): TRect; |
| 1337 | begin
|
| 1338 | Result.Left := R.Left; |
| 1339 | Result.Top := R.Top; |
| 1340 | Result.Right := R.Right - R.Left; |
| 1341 | Result.Bottom := R.Bottom - R.Top; |
| 1342 | end;
|
| 1343 | |
| 1344 | procedure ClipRectBounds(var X, Y, Width, Height: LongInt; const Clip: TRect); |
| 1345 | |
| 1346 | procedure ClipDim(var AStart, ALength: LongInt; ClipMin, ClipMax: LongInt); |
| 1347 | begin
|
| 1348 | if AStart < ClipMin then |
| 1349 | begin
|
| 1350 | ALength := ALength - (ClipMin - AStart); |
| 1351 | AStart := ClipMin; |
| 1352 | end;
|
| 1353 | if AStart + ALength > ClipMax then ALength := Max(0, ClipMax - AStart); |
| 1354 | end;
|
| 1355 | |
| 1356 | begin
|
| 1357 | ClipDim(X, Width, Clip.Left, Clip.Right); |
| 1358 | ClipDim(Y, Height, Clip.Top, Clip.Bottom); |
| 1359 | end;
|
| 1360 | |
| 1361 | procedure ClipCopyBounds(var SrcX, SrcY, Width, Height, DstX, DstY: LongInt; SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect); |
| 1362 | |
| 1363 | procedure ClipDim(var SrcPos, DstPos, Size: LongInt; SrcClipMax, |
| 1364 | DstClipMin, DstClipMax: LongInt); |
| 1365 | var
|
| 1366 | OldDstPos: LongInt; |
| 1367 | Diff: LongInt; |
| 1368 | begin
|
| 1369 | OldDstPos := Iff(DstPos < 0, DstPos, 0); |
| 1370 | if DstPos < DstClipMin then |
| 1371 | begin
|
| 1372 | Diff := DstClipMin - DstPos; |
| 1373 | Size := Size - Diff; |
| 1374 | if DstPos < SrcPos then |
| 1375 | SrcPos := SrcPos + Diff; |
| 1376 | DstPos := DstClipMin; |
| 1377 | end;
|
| 1378 | if SrcPos < 0 then |
| 1379 | begin
|
| 1380 | Size := Size + SrcPos - OldDstPos; |
| 1381 | DstPos := DstPos - SrcPos + OldDstPos; |
| 1382 | SrcPos := 0;
|
| 1383 | end;
|
| 1384 | if SrcPos + Size > SrcClipMax then Size := SrcClipMax - SrcPos; |
| 1385 | if DstPos + Size > DstClipMax then Size := DstClipMax - DstPos; |
| 1386 | end;
|
| 1387 | |
| 1388 | begin
|
| 1389 | ClipDim(SrcX, DstX, Width, SrcImageWidth, DstClip.Left, DstClip.Right); |
| 1390 | ClipDim(SrcY, DstY, Height, SrcImageHeight, DstClip.Top, DstClip.Bottom); |
| 1391 | end;
|
| 1392 | |
| 1393 | procedure ClipStretchBounds(var SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY, |
| 1394 | DstWidth, DstHeight: LongInt; SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect);
|
| 1395 | |
| 1396 | procedure ClipDim(var SrcPos, DstPos, SrcSize, DstSize: LongInt; SrcClipMax, |
| 1397 | DstClipMin, DstClipMax: LongInt); |
| 1398 | var
|
| 1399 | OldSize: LongInt; |
| 1400 | Diff: LongInt; |
| 1401 | Scale: Single; |
| 1402 | begin
|
| 1403 | Scale := DstSize / SrcSize; |
| 1404 | if DstPos < DstClipMin then |
| 1405 | begin
|
| 1406 | Diff := DstClipMin - DstPos; |
| 1407 | DstSize := DstSize - Diff; |
| 1408 | SrcPos := SrcPos + Round(Diff / Scale); |
| 1409 | SrcSize := SrcSize - Round(Diff / Scale); |
| 1410 | DstPos := DstClipMin; |
| 1411 | end;
|
| 1412 | if SrcPos < 0 then |
| 1413 | begin
|
| 1414 | SrcSize := SrcSize + SrcPos; |
| 1415 | DstPos := DstPos - Round(SrcPos * Scale); |
| 1416 | DstSize := DstSize + Round(SrcPos * Scale); |
| 1417 | SrcPos := 0;
|
| 1418 | end;
|
| 1419 | if SrcPos + SrcSize > SrcClipMax then |
| 1420 | begin
|
| 1421 | OldSize := SrcSize; |
| 1422 | SrcSize := SrcClipMax - SrcPos; |
| 1423 | DstSize := Round(DstSize * (SrcSize / OldSize)); |
| 1424 | end;
|
| 1425 | if DstPos + DstSize > DstClipMax then |
| 1426 | begin
|
| 1427 | OldSize := DstSize; |
| 1428 | DstSize := DstClipMax - DstPos; |
| 1429 | SrcSize := Round(SrcSize * (DstSize / OldSize)); |
| 1430 | end;
|
| 1431 | end;
|
| 1432 | |
| 1433 | begin
|
| 1434 | ClipDim(SrcX, DstX, SrcWidth, DstWidth, SrcImageWidth, DstClip.Left, DstClip.Right); |
| 1435 | ClipDim(SrcY, DstY, SrcHeight, DstHeight, SrcImageHeight, DstClip.Top, DstClip.Bottom); |
| 1436 | end;
|
| 1437 | |
| 1438 | function ScaleRectToRect(const SourceRect, TargetRect: TRect): TRect; |
| 1439 | var
|
| 1440 | SourceWidth: LongInt; |
| 1441 | SourceHeight: LongInt; |
| 1442 | TargetWidth: LongInt; |
| 1443 | TargetHeight: LongInt; |
| 1444 | ScaledWidth: LongInt; |
| 1445 | ScaledHeight: LongInt; |
| 1446 | begin
|
| 1447 | SourceWidth := SourceRect.Right - SourceRect.Left; |
| 1448 | SourceHeight := SourceRect.Bottom - SourceRect.Top; |
| 1449 | TargetWidth := TargetRect.Right - TargetRect.Left; |
| 1450 | TargetHeight := TargetRect.Bottom - TargetRect.Top; |
| 1451 | |
| 1452 | if SourceWidth * TargetHeight < SourceHeight * TargetWidth then |
| 1453 | begin
|
| 1454 | ScaledWidth := (SourceWidth * TargetHeight) div SourceHeight;
|
| 1455 | Result := BoundsToRect(TargetRect.Left + ((TargetWidth - ScaledWidth) div 2), |
| 1456 | TargetRect.Top, ScaledWidth, TargetHeight); |
| 1457 | end
|
| 1458 | else
|
| 1459 | begin
|
| 1460 | ScaledHeight := (SourceHeight * TargetWidth) div SourceWidth;
|
| 1461 | Result := BoundsToRect(TargetRect.Left, TargetRect.Top + ((TargetHeight - ScaledHeight) div 2), |
| 1462 | TargetWidth, ScaledHeight); |
| 1463 | end;
|
| 1464 | end;
|
| 1465 | |
| 1466 | function RectInRect(const R1, R2: TRect): Boolean; |
| 1467 | begin
|
| 1468 | Result:= |
| 1469 | (R1.Left >= R2.Left) and
|
| 1470 | (R1.Top >= R2.Top) and
|
| 1471 | (R1.Right <= R2.Right) and
|
| 1472 | (R1.Bottom <= R2.Bottom); |
| 1473 | end;
|
| 1474 | |
| 1475 | function RectIntersects(const R1, R2: TRect): Boolean; |
| 1476 | begin
|
| 1477 | Result := |
| 1478 | not (R1.Left > R2.Right) and |
| 1479 | not (R1.Top > R2.Bottom) and |
| 1480 | not (R1.Right < R2.Left) and |
| 1481 | not (R1.Bottom < R2.Top);
|
| 1482 | end;
|
| 1483 | |
| 1484 | function FormatExceptMsg(const Msg: string; const Args: array of const): string; |
| 1485 | begin
|
| 1486 | Result := Format(Msg + SLineBreak + 'Message: ' + GetExceptObject.Message, Args);
|
| 1487 | end;
|
| 1488 | |
| 1489 | procedure DebugMsg(const Msg: string; const Args: array of const); |
| 1490 | var
|
| 1491 | FmtMsg: string;
|
| 1492 | begin
|
| 1493 | FmtMsg := Format(Msg, Args); |
| 1494 | {$IFDEF MSWINDOWS}
|
| 1495 | if IsConsole then |
| 1496 | WriteLn('DebugMsg: ' + FmtMsg)
|
| 1497 | else
|
| 1498 | MessageBox(GetActiveWindow, PChar(FmtMsg), 'DebugMsg', MB_OK);
|
| 1499 | {$ENDIF}
|
| 1500 | {$IFDEF UNIX}
|
| 1501 | WriteLn('DebugMsg: ' + FmtMsg);
|
| 1502 | {$ENDIF}
|
| 1503 | {$IFDEF MSDOS}
|
| 1504 | WriteLn('DebugMsg: ' + FmtMsg);
|
| 1505 | {$ENDIF}
|
| 1506 | end;
|
| 1507 | |
| 1508 | initialization
|
| 1509 | InitCrcTable; |
| 1510 | {$IFDEF MSWINDOWS}
|
| 1511 | QueryPerformanceFrequency(PerfFrequency); |
| 1512 | InvPerfFrequency := 1.0 / PerfFrequency;
|
| 1513 | {$ENDIF}
|
| 1514 | {$IFDEF MSDOS}
|
| 1515 | // reset PIT
|
| 1516 | asm
|
| 1517 | MOV EAX, $34
|
| 1518 | OUT $43, AL |
| 1519 | XOR EAX, EAX
|
| 1520 | OUT $40, AL |
| 1521 | OUT $40, AL |
| 1522 | end;
|
| 1523 | {$ENDIF}
|
| 1524 | |
| 1525 | {
|
| 1526 | File Notes: |
| 1527 | |
| 1528 | -- TODOS ---------------------------------------------------- |
| 1529 | - nothing now |
| 1530 | |
| 1531 | -- 0.23 Changes/Bug Fixes ----------------------------------- |
| 1532 | - Added RectInRect and RectIntersects functions |
| 1533 | - Added some string utils: StrToken, StrTokenEnd, PosEx, PosNoCase. |
| 1534 | - Moved BuildFileList here from DemoUtils. |
| 1535 | |
| 1536 | -- 0.21 Changes/Bug Fixes ----------------------------------- |
| 1537 | - Moved GetVolumeLevelCount from ImagingDds here. |
| 1538 | - Renamed FillMemory to FillMemoryByte to avoid name collision in C++ Builder. |
| 1539 | - Added Iff function for Char, Pointer, and Int64 types. |
| 1540 | - Added IsLittleEndian function. |
| 1541 | - Added array types for TWordRec, TLongWordRec, and TInt64Rec. |
| 1542 | - Added MatchFileNameMask function. |
| 1543 | |
| 1544 | -- 0.19 Changes/Bug Fixes ----------------------------------- |
| 1545 | - added ScaleRectToRect (thanks to Paul Michell) |
| 1546 | - added BoundsToRect, ClipBounds, ClipCopyBounds, ClipStretchBounds functions |
| 1547 | - added MulDiv function |
| 1548 | - FreeAndNil is not inline anymore - caused AV in one program |
| 1549 | |
| 1550 | -- 0.17 Changes/Bug Fixes ----------------------------------- |
| 1551 | |
| 1552 | - GetAppExe didn't return absolute path in FreeBSD, fixed |
| 1553 | - added debug message output |
| 1554 | - fixed Unix compatibility issues (thanks to Ales Katona). |
| 1555 | Imaging now compiles in FreeBSD and maybe in other Unixes as well. |
| 1556 | |
| 1557 | -- 0.15 Changes/Bug Fixes ----------------------------------- |
| 1558 | - added some new utility functions |
| 1559 | |
| 1560 | -- 0.13 Changes/Bug Fixes ----------------------------------- |
| 1561 | - added many new utility functions |
| 1562 | - minor change in SwapEndian to avoid range check error |
| 1563 | |
| 1564 | } |
| 1565 | end.
|
| 1566 |