Statistics
| Branch: | Tag: | Revision:

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