DEADSOFTWARE

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