AsphyreFonts.pas
上传用户:ctlcnc
上传日期:2021-12-10
资源大小:4933k
文件大小:27k
源码类别:

2D图形编程

开发平台:

Delphi

  1. unit AsphyreFonts;
  2. //---------------------------------------------------------------------------
  3. // AsphyreFonts.pas                                     Modified: 21-Feb-2007
  4. // Asphyre Native Font implementation                             Version 1.1
  5. //---------------------------------------------------------------------------
  6. // Important Notice:
  7. //
  8. // If you modify/use this code or one of its parts either in original or
  9. // modified form, you must comply with Mozilla Public License v1.1,
  10. // specifically section 3, "Distribution Obligations". Failure to do so will
  11. // result in the license breach, which will be resolved in the court.
  12. // Remember that violating author's rights is considered a serious crime in
  13. // many countries. Thank you!
  14. //
  15. // !! Please *read* Mozilla Public License 1.1 document located at:
  16. //  http://www.mozilla.org/MPL/
  17. //
  18. // If you require any clarifications about the license, feel free to contact
  19. // us or post your question on our forums at: http://www.afterwarp.net
  20. //---------------------------------------------------------------------------
  21. // The contents of this file are subject to the Mozilla Public License
  22. // Version 1.1 (the "License"); you may not use this file except in
  23. // compliance with the License. You may obtain a copy of the License at
  24. // http://www.mozilla.org/MPL/
  25. //
  26. // Software distributed under the License is distributed on an "AS IS"
  27. // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
  28. // License for the specific language governing rights and limitations
  29. // under the License.
  30. //
  31. // The Original Code is AsphyreFonts.pas.
  32. //
  33. // The Initial Developer of the Original Code is M. Sc. Yuriy Kotsarenko.
  34. // Portions created by M. Sc. Yuriy Kotsarenko are Copyright (C) 2007,
  35. // Afterwarp Interactive. All Rights Reserved.
  36. //---------------------------------------------------------------------------
  37. interface
  38. //---------------------------------------------------------------------------
  39. uses
  40.  Types, SysUtils, Vectors2px, HelperSets, AsphyreTypes, AsphyreUtils,
  41.  MediaFonts, AsphyreEvents;
  42. //---------------------------------------------------------------------------
  43. type
  44.  TAsphyreFonts = class;
  45. //---------------------------------------------------------------------------
  46.  PFontOptions = ^TFontOptions;
  47.  TFontOptions = record
  48.   // text shadow settings
  49.   ShowShadow : Boolean;
  50.   ShadowAlpha: Integer;
  51.   ShadowDepth: TPoint2px;
  52.   // text output parameters
  53.   Kerning: Integer;
  54.   Scale  : Integer;
  55.   Shift  : TPoint2px;
  56.   procedure Reset();
  57.  end;
  58. //---------------------------------------------------------------------------
  59.  PFontBitmapMetrics = ^TFontBitmapMetrics;
  60.  TFontBitmapMetrics = record
  61.   FirstLetter: Integer;
  62.   LetterCount: Integer;
  63.   Interleave : Integer;
  64.   BlankSpace : Integer;
  65.  end;
  66. //---------------------------------------------------------------------------
  67.  TFontPerformEvent = procedure(Sender: TObject; Tag, CharCode,
  68.   CharSpacing: Integer; const DrawRect: TRect; const CharSize: TPoint2px;
  69.   const Colors: TColor2) of object;
  70. //---------------------------------------------------------------------------
  71.  TAsphyreNativeFont = class
  72.  private
  73.   FOwner    : TAsphyreFonts;
  74.   FName     : string;
  75.   FFontIndex: Integer;
  76.   FOptions  : TFontOptions;
  77.   CharWidths: array of Integer;
  78.   FInitialized  : Boolean;
  79.   FBitmapMetrics: TFontBitmapMetrics;
  80.   FPatternSize  : TPoint2px;
  81.   FImageIndex   : Integer;
  82.   // NoExclude indicates that the component should not exclude itself
  83.   // from the owner.
  84.   NoExclude: Boolean;
  85.   function GetOptions(): PFontOptions;
  86.   function GetBitmapMetrics(): PFontBitmapMetrics;
  87.   procedure InitCharWidths(Count, Space: Integer);
  88.  protected
  89.   procedure DrawAtFx(Sender: TObject; Tag, CharCode, CharSpacing: Integer;
  90.    const DrawRect: TRect; const CharSize: TPoint2px;
  91.    const Colors: TColor2); virtual;
  92.   procedure EstimateSize(Sender: TObject; Tag, CharCode, CharSpacing: Integer;
  93.    const DrawRect: TRect; const CharSize: TPoint2px;
  94.    const Colors: TColor2); virtual;
  95.   procedure EstimateRects(Sender: TObject; Tag, CharCode, CharSpacing: Integer;
  96.    const DrawRect: TRect; const CharSize: TPoint2px;
  97.    const Colors: TColor2); virtual;
  98.  public
  99.   // The reference to the font holder.
  100.   property Owner: TAsphyreFonts read FOwner;
  101.   // The unique image identifier
  102.   property Name: string read FName;
  103.   // The index assigned by the owner.
  104.   property FontIndex: Integer read FFontIndex;
  105.   // Determines whether the font has been initialized successfully.
  106.   property Initialized: Boolean read FInitialized;
  107.   // Options controlling font appearance.
  108.   property Options: PFontOptions read GetOptions;
  109.   // Bitmap font parameters like number of letters and such.
  110.   property BitmapMetrics: PFontBitmapMetrics read GetBitmapMetrics;
  111.   // The index of image representing bitmap font.
  112.   property ImageIndex : Integer read FImageIndex;
  113.   // The pattern size in the font image.
  114.   property PatternSize: TPoint2px read FPatternSize;
  115.   //-------------------------------------------------------------------------
  116.   // The following routine performs the virtualized rendering of the text
  117.   // at the specified position, calling specified callback function to render
  118.   // the actual text or do any relevant task.
  119.   //-------------------------------------------------------------------------
  120.   procedure PerformAt(const Text: string; const DrawPos: TPoint2px;
  121.    Colors: TColor2; Tag: Integer; Event: TFontPerformEvent);
  122.   //-------------------------------------------------------------------------
  123.   // Draw the text at the specified position and color(s).
  124.   //-------------------------------------------------------------------------
  125.   procedure TextOut(const Text: string; x, y: Integer;
  126.    const Colors: TColor2); overload;
  127.   procedure TextOut(const Text: string; x, y: Integer;
  128.    Color: Cardinal); overload;
  129.   //-------------------------------------------------------------------------
  130.   // Estimates the width and height of text in pixels.
  131.   //-------------------------------------------------------------------------
  132.   function TextExtent(const Text: string): TPoint2px;
  133.   //-------------------------------------------------------------------------
  134.   // The following routines estimate either the width or the height of the
  135.   // given text in pixels. They use TextExtent(), so for performance reasons
  136.   // it is better to use TextExtent() if you need to determine the dimensions
  137.   // for the text both horizontally and vertically.
  138.   //-------------------------------------------------------------------------
  139.   function TextWidth(const Text: string): Integer;
  140.   function TextHeight(const Text: string): Integer;
  141.   //-------------------------------------------------------------------------
  142.   // The following method displays the text inside given rectangle using the
  143.   // particular text alignment.
  144.   //-------------------------------------------------------------------------
  145.   procedure TextRect(const Text: string; const Rect: TRect;
  146.    HorizontalAlign: THorizontalAlign; VerticalAlign: TVerticalAlign;
  147.    const Colors: TColor2);
  148.   //-------------------------------------------------------------------------
  149.   // The following method estimates the rectangles occupied by individual
  150.   // letters when rendered on the screen.
  151.   //-------------------------------------------------------------------------
  152.   procedure TextRects(const Text: string; List: TRectList);
  153.   function Initialize(Desc: PFontDesc): Boolean;
  154.   procedure Finalize();
  155.   constructor Create(AOwner: TAsphyreFonts); virtual;
  156.   destructor Destroy(); override;
  157.  end;
  158. //---------------------------------------------------------------------------
  159.  TAsphyreFonts = class
  160.  private
  161.   FDevice: TObject;
  162.   Data: array of TAsphyreNativeFont;
  163.   SearchObjects: array of Integer;
  164.   SearchDirty  : Boolean;
  165.   FMediaOption : string;
  166.   function GetCount(): Integer;
  167.   function GetItem(Num: Integer): TAsphyreNativeFont;
  168.   function CountSearchObjects(): Integer;
  169.   procedure FillSearchObjects(Amount: Integer);
  170.   procedure SortSearchObjects(Left, Right: Integer);
  171.   procedure PrepareSearchObjects();
  172.   function GetFont(const Name: string): TAsphyreNativeFont;
  173.   procedure OnDeviceDestroy(Sender: TObject; EventParam: Pointer;
  174.    var Success: Boolean);
  175.  protected
  176.   function FindEmptySlot(): Integer;
  177.   function Insert(Element: TAsphyreNativeFont): Integer;
  178.   function Include(Element: TAsphyreNativeFont): Integer;
  179.   procedure Exclude(Element: TAsphyreNativeFont);
  180.  public
  181.   property Device: TObject read FDevice;
  182.   property MediaOption: string read FMediaOption write FMediaOption;
  183.   property Count: Integer read GetCount;
  184.   property Items[Num: Integer]: TAsphyreNativeFont read GetItem; default;
  185.   property Font[const Name: string]: TAsphyreNativeFont read GetFont;
  186.   function IndexOf(Element: TAsphyreNativeFont): Integer; overload;
  187.   function IndexOf(const Name: string): Integer; overload;
  188.   procedure Remove(Num: Integer);
  189.   procedure RemoveAll();
  190.   function ResolveFont(Name: string): Integer;
  191.   procedure UnloadGroup(const GroupName: string);
  192.   constructor Create(ADevice: TObject);
  193.   destructor Destroy(); override;
  194.  end;
  195. //---------------------------------------------------------------------------
  196. implementation
  197. //---------------------------------------------------------------------------
  198. uses
  199.  AsphyreEffects, AsphyreDevices, AsphyreImages;
  200. //---------------------------------------------------------------------------
  201. const
  202.  // The padding wired into Asphyre 4.x bitmap fonts.
  203.  ConstantPadding = 2;
  204. //---------------------------------------------------------------------------
  205. procedure TFontOptions.Reset();
  206. begin
  207.  ShowShadow:= False;
  208.  ShadowAlpha:= 128;
  209.  ShadowDepth:= Point2px(2, 2);
  210.  Kerning:= 0;
  211.  Scale  := 65536;
  212.  Shift  := ZeroPoint2px;
  213. end;
  214. //---------------------------------------------------------------------------
  215. constructor TAsphyreNativeFont.Create(AOwner: TAsphyreFonts);
  216. begin
  217.  inherited Create();
  218.  FOwner      := AOwner;
  219.  NoExclude   := False;
  220.  FFontIndex  := -1;
  221.  FInitialized:= False;
  222.  if (FOwner <> nil) then FOwner.Insert(Self);
  223.  FImageIndex := -1;
  224.  FPatternSize:= InfPoint2px;
  225.  FOptions.Reset();
  226. end;
  227. //---------------------------------------------------------------------------
  228. destructor TAsphyreNativeFont.Destroy();
  229. begin
  230.  if (FInitialized) then Finalize();
  231.  if (not NoExclude)and(FOwner <> nil) then FOwner.Exclude(Self);
  232.  inherited;
  233. end;
  234. //---------------------------------------------------------------------------
  235. function TAsphyreNativeFont.GetOptions(): PFontOptions;
  236. begin
  237.  Result:= @FOptions;
  238. end;
  239. //---------------------------------------------------------------------------
  240. function TAsphyreNativeFont.GetBitmapMetrics(): PFontBitmapMetrics;
  241. begin
  242.  if (FInitialized) then Result:= @FBitmapMetrics else Result:= nil;
  243. end;
  244. //---------------------------------------------------------------------------
  245. procedure TAsphyreNativeFont.InitCharWidths(Count, Space: Integer);
  246. var
  247.  i: Integer;
  248. begin
  249.  SetLength(CharWidths, Count);
  250.  for i:= 0 to Length(CharWidths) - 1 do
  251.   CharWidths[i]:= Space;
  252. end;
  253. //---------------------------------------------------------------------------
  254. function TAsphyreNativeFont.Initialize(Desc: PFontDesc): Boolean;
  255. var
  256.  Images: TAsphyreImages;
  257.  i: Integer;
  258. begin
  259.  // (1) Initialization requires a valid Owner to have reference to existing
  260.  // device that is initialized.
  261.  if (FOwner = nil)or(FOwner.Device = nil)or
  262.   (not (FOwner.Device is TAsphyreDevice))or
  263.   (not TAsphyreDevice(FOwner.Device).Initialized) then
  264.   begin
  265.    Result:= False;
  266.    Exit;
  267.   end;
  268.  // (2) Make sure the font has corresponding bitmap image in the system.
  269.  Images:= TAsphyreDevice(FOwner.FDevice).Images;
  270.  FImageIndex:= Images.ResolveImage(Desc.Image);
  271.  Result:= (FImageIndex <> -1)and(Images[FImageIndex] is TAsphyreImage);
  272.  if (not Result) then Exit;
  273.  // (3) Initialize bitmap metrics that will be used later on to rasterize
  274.  // individual characters.
  275.  FBitmapMetrics.FirstLetter:= Desc.FirstLetter;
  276.  FBitmapMetrics.LetterCount:= Desc.PatCount;
  277.  FBitmapMetrics.Interleave := Desc.Interleave;
  278.  FBitmapMetrics.BlankSpace := Round((Desc.PatSize.X - ConstantPadding) *
  279.   Desc.BlankSpace);
  280.  // (4) The pattern size specified in the font overrides the information
  281.  // specified for the image (for convenience).
  282.  FPatternSize:= Desc.PatSize;
  283.  // (5) Override image specification in case such information was not
  284.  // provided in XML file.
  285.  with Images[FImageIndex] as TAsphyreImage do
  286.   begin
  287.    Padding     := Point2px(ConstantPadding, ConstantPadding);
  288.    PatternCount:= Desc.PatCount;
  289.    PatternSize := Desc.PatSize;
  290.   end;
  291.  // (6) Since not all characters may have their width described in XML file,
  292.  // the list is initially filled with blank space size.
  293.  InitCharWidths(FBitmapMetrics.LetterCount, FBitmapMetrics.BlankSpace);
  294.  // (7) Fill characters that are described in XML file with their respective
  295.  // width values.
  296.  for i:= 0 to Length(Desc.CharInfo) - 1 do
  297.   CharWidths[Desc.CharInfo[i].AsciiCode -
  298.    Desc.FirstLetter]:= Desc.CharInfo[i].Width;
  299.  // (8) Indicate that the initialization was successful.
  300.  FInitialized:= True;
  301. end;
  302. //---------------------------------------------------------------------------
  303. procedure TAsphyreNativeFont.Finalize();
  304. begin
  305.  FillChar(FBitmapMetrics, SizeOf(TFontBitmapMetrics), 0);
  306.  FPatternSize:= InfPoint2px;
  307.  FImageIndex := -1;
  308.  FInitialized:= False;
  309. end;
  310. //---------------------------------------------------------------------------
  311. procedure TAsphyreNativeFont.PerformAt(const Text: string;
  312.  const DrawPos: TPoint2px; Colors: TColor2; Tag: Integer;
  313.  Event: TFontPerformEvent);
  314. var
  315.  Index: Integer;
  316.  DrawSize: TPoint2px;
  317.  CharSize: TPoint2px;
  318.  Spacing : Integer;
  319.  AtPos   : TPoint2px;
  320.  CharCode: Integer;
  321. begin
  322.  // The initial drawing position.
  323.  AtPos:= Point2px(DrawPos.x + iMul16(FOptions.Shift.x, FOptions.Scale),
  324.   DrawPos.y + iMul16(FOptions.Shift.y, FOptions.Scale));
  325.  // Precalculate the drawing size of individual patterns.
  326.  DrawSize.x:= iMul16(FPatternSize.x - ConstantPadding, FOptions.Scale);
  327.  DrawSize.y:= iMul16(FPatternSize.y - ConstantPadding, FOptions.Scale);
  328.  // Character height is constant, so it can be precalculated.
  329.  CharSize.y:= DrawSize.y;
  330.  Index:= 1;
  331.  while (Index <= Length(Text)) do
  332.   begin
  333.    // (1) Retreive character code, which is the index to character width
  334.    // array and is the actual pattern to draw.
  335.    CharCode:= Byte(Text[Index]) - FBitmapMetrics.FirstLetter;
  336.    // (2) If there is no width associated with the character, consider it
  337.    // as a blank space.
  338.    if (CharCode < 0)or(CharCode > Length(CharWidths)) then
  339.     begin
  340.      CharCode  := -1;
  341.      CharSize.x:= FBitmapMetrics.BlankSpace;
  342.     end else CharSize.x:= CharWidths[CharCode] + FBitmapMetrics.Interleave;
  343.    // (3) Add kerning, if not the last character to display.
  344.    Spacing:= CharSize.x;
  345.    if (Index < Length(Text)) then Inc(Spacing, FOptions.Kerning);
  346.    // (4) Apply scaling to character width and its spacing.
  347.    CharSize.x:= iMul16(CharSize.x, FOptions.Scale);
  348.    Spacing   := iMul16(Spacing, FOptions.Scale);
  349.    // (5) Call the specified event to do the relevant task.
  350.    Event(Self, Tag, CharCode, Spacing, Bounds(AtPos.x, AtPos.y, DrawSize.x,
  351.     DrawSize.y), CharSize, Colors);
  352.    // (6) Advance both in text string and horizontal position.
  353.    Inc(AtPos.x, Spacing);
  354.    Inc(Index);
  355.   end;
  356. end;
  357. //---------------------------------------------------------------------------
  358. procedure TAsphyreNativeFont.DrawAtFx(Sender: TObject; Tag, CharCode,
  359.  CharSpacing: Integer; const DrawRect: TRect; const CharSize: TPoint2px;
  360.  const Colors: TColor2);
  361. var
  362.  Image: TAsphyreCustomImage;
  363. begin
  364.  if (CharCode <> -1) then
  365.   begin
  366.    Image:= TAsphyreDevice(FOwner.FDevice).Images[FImageIndex];
  367.    if (Image <> nil) then
  368.     with TAsphyreDevice(FOwner.FDevice).Canvas do
  369.      begin
  370.       UseImage(Image, CharCode);
  371.       TexMap(pRect4(DrawRect), cColor4(Colors[0], Colors[0], Colors[1],
  372.        Colors[1]), Tag);
  373.      end
  374.   end;
  375. end;
  376. //---------------------------------------------------------------------------
  377. procedure TAsphyreNativeFont.TextOut(const Text: string; x, y: Integer;
  378.  const Colors: TColor2);
  379. var
  380.  AddVec: TPoint2px;
  381. begin
  382.  if (FOptions.ShowShadow) then
  383.   begin
  384.    AddVec.x:= iMul16(FOptions.ShadowDepth.x, FOptions.Scale);
  385.    AddVec.y:= iMul16(FOptions.ShadowDepth.y, FOptions.Scale);
  386.    PerformAt(Text, Point2px(x, y) + AddVec, Colors, fxFullShadow,
  387.     DrawAtFx);
  388.   end;
  389.  PerformAt(Text, Point2px(x, y), Colors, fxFullBlend, DrawAtFx);
  390. end;
  391. //---------------------------------------------------------------------------
  392. procedure TAsphyreNativeFont.TextOut(const Text: string; x, y: Integer;
  393.  Color: Cardinal);
  394. begin
  395.  TextOut(Text, x, y, cColor2(Color));
  396. end;
  397. //---------------------------------------------------------------------------
  398. procedure TAsphyreNativeFont.TextRect(const Text: string;
  399.  const Rect: TRect; HorizontalAlign: THorizontalAlign;
  400.  VerticalAlign: TVerticalAlign; const Colors: TColor2);
  401. var
  402.  Size, DrawPos: TPoint2px;
  403. begin
  404.  Size:= TextExtent(Text);
  405.  case HorizontalAlign of
  406.   haRight:
  407.    DrawPos.x:= Rect.Right - Size.x;
  408.   haCenter:
  409.    DrawPos.x:= Rect.Left + ((Rect.Right - (Rect.Left + Size.x)) div 2);
  410.   else DrawPos.x:= Rect.Left;
  411.  end;
  412.  case VerticalAlign of
  413.   vaBottom:
  414.    DrawPos.y:= Rect.Bottom - Size.y;
  415.   vaCenter:
  416.    DrawPos.y:= Rect.Top + ((Rect.Bottom - (Rect.Top + Size.y)) div 2);
  417.   else DrawPos.x:= Rect.Left;
  418.  end;
  419.  TextOut(Text, DrawPos.x, DrawPos.y, Colors);
  420. end;
  421. //---------------------------------------------------------------------------
  422. procedure TAsphyreNativeFont.EstimateRects(Sender: TObject; Tag,
  423.  CharCode, CharSpacing: Integer; const DrawRect: TRect;
  424.  const CharSize: TPoint2px; const Colors: TColor2);
  425. var
  426.  List: TRectList;
  427. begin
  428.  List:= TRectList(Tag);
  429.  List.Add(DrawRect.Left, DrawRect.Top, CharSize.x, CharSize.y);
  430. end;
  431. //---------------------------------------------------------------------------
  432. procedure TAsphyreNativeFont.TextRects(const Text: string; List: TRectList);
  433. begin
  434.  PerformAt(Text, ZeroPoint2px, clWhite2, Integer(List), EstimateRects);
  435. end;
  436. //---------------------------------------------------------------------------
  437. procedure TAsphyreNativeFont.EstimateSize(Sender: TObject; Tag,
  438.  CharCode, CharSpacing: Integer; const DrawRect: TRect;
  439.  const CharSize: TPoint2px; const Colors: TColor2);
  440. var
  441.  Size: PPoint2px;
  442. begin
  443.  Size:= Pointer(Tag);
  444.  Inc(Size.x, CharSpacing);
  445.  Size.y:= Max2(Size.y, CharSize.y);
  446. end;
  447. //---------------------------------------------------------------------------
  448. function TAsphyreNativeFont.TextExtent(const Text: string): TPoint2px;
  449. begin
  450.  Result:= ZeroPoint2px;
  451.  PerformAt(Text, ZeroPoint2px, clWhite2, Integer(@Result), EstimateSize);
  452. end;
  453. //---------------------------------------------------------------------------
  454. function TAsphyreNativeFont.TextWidth(const Text: string): Integer;
  455. begin
  456.  Result:= TextExtent(Text).x;
  457. end;
  458. //---------------------------------------------------------------------------
  459. function TAsphyreNativeFont.TextHeight(const Text: string): Integer;
  460. begin
  461.  Result:= TextExtent(Text).y;
  462. end;
  463. //---------------------------------------------------------------------------
  464. constructor TAsphyreFonts.Create(ADevice: TObject);
  465. begin
  466.  inherited Create();
  467.  FDevice:= ADevice;
  468.  EventDeviceDestroy.Subscribe(OnDeviceDestroy, FDevice);
  469.  FMediaOption:= '';
  470.  SearchDirty := False;
  471. end;
  472. //---------------------------------------------------------------------------
  473. destructor TAsphyreFonts.Destroy();
  474. begin
  475.  RemoveAll();
  476.  EventDeviceDestroy.Unsubscribe(OnDeviceDestroy);
  477.  inherited;
  478. end;
  479. //---------------------------------------------------------------------------
  480. function TAsphyreFonts.GetCount(): Integer;
  481. begin
  482.  Result:= Length(Data);
  483. end;
  484. //---------------------------------------------------------------------------
  485. function TAsphyreFonts.GetItem(Num: Integer): TAsphyreNativeFont;
  486. begin
  487.  if (Num >= 0)and(Num < Length(Data)) then
  488.   Result:= Data[Num] else Result:= nil;
  489. end;
  490. //---------------------------------------------------------------------------
  491. function TAsphyreFonts.IndexOf(Element: TAsphyreNativeFont): Integer;
  492. var
  493.  i: Integer;
  494. begin
  495.  Result:= -1;
  496.  for i:= 0 to Length(Data) - 1 do
  497.   if (Data[i] = Element) then
  498.    begin
  499.     Result:= i;
  500.     Break;
  501.    end;
  502. end;
  503. //---------------------------------------------------------------------------
  504. function TAsphyreFonts.CountSearchObjects(): Integer;
  505. var
  506.  i: Integer;
  507. begin
  508.  Result:= 0;
  509.  for i:= 0 to Length(Data) - 1 do
  510.   if (Data[i] <> nil) then Inc(Result);
  511. end;
  512. //---------------------------------------------------------------------------
  513. procedure TAsphyreFonts.FillSearchObjects(Amount: Integer);
  514. var
  515.  i, DestIndex: Integer;
  516. begin
  517.  SetLength(SearchObjects, Amount);
  518.  DestIndex:= 0;
  519.  for i:= 0 to Length(Data) - 1 do
  520.   if (Data[i] <> nil) then
  521.    begin
  522.     SearchObjects[DestIndex]:= i;
  523.     Inc(DestIndex);
  524.    end;
  525. end;
  526. //---------------------------------------------------------------------------
  527. procedure TAsphyreFonts.SortSearchObjects(Left, Right: Integer);
  528. var
  529.  Lo, Hi: Integer;
  530.  TempIndex: Integer;
  531.  MidValue: string;
  532. begin
  533.  Lo:= Left;
  534.  Hi:= Right;
  535.  MidValue:= Data[SearchObjects[(Left + Right) shr 1]].Name;
  536.  repeat
  537.   while (Data[SearchObjects[Lo]].Name < MidValue) do Inc(Lo);
  538.   while (MidValue < Data[SearchObjects[Hi]].Name) do Dec(Hi);
  539.   if (Lo <= Hi) then
  540.    begin
  541.     TempIndex:= SearchObjects[Lo];
  542.     SearchObjects[Lo]:= SearchObjects[Hi];
  543.     SearchObjects[Hi]:= TempIndex;
  544.     Inc(Lo);
  545.     Dec(Hi);
  546.    end;
  547.  until (Lo > Hi);
  548.  if (Left < Hi) then SortSearchObjects(Left, Hi);
  549.  if (Lo < Right) then SortSearchObjects(Lo, Right);
  550. end;
  551. //---------------------------------------------------------------------------
  552. procedure TAsphyreFonts.PrepareSearchObjects();
  553. var
  554.  Amount: Integer;
  555. begin
  556.  Amount:= CountSearchObjects();
  557.  FillSearchObjects(Amount);
  558.  if (Amount > 0) then
  559.   SortSearchObjects(0, Amount - 1);
  560.  SearchDirty:= False;
  561. end;
  562. //---------------------------------------------------------------------------
  563. function TAsphyreFonts.IndexOf(const Name: string): Integer;
  564. var
  565.  Lo, Hi, Mid: Integer;
  566. begin
  567.  if (SearchDirty) then PrepareSearchObjects();
  568.  Result:= -1;
  569.  Lo:= 0;
  570.  Hi:= Length(SearchObjects) - 1;
  571.  while (Lo <= Hi) do
  572.   begin
  573.    Mid:= (Lo + Hi) div 2;
  574.    if (Data[SearchObjects[Mid]].Name = Name) then
  575.     begin
  576.      Result:= SearchObjects[Mid];
  577.      Break;
  578.     end;
  579.    if (Data[SearchObjects[Mid]].Name > Name) then Hi:= Mid - 1
  580.     else Lo:= Mid + 1;
  581.  end;
  582. end;
  583. //---------------------------------------------------------------------------
  584. function TAsphyreFonts.FindEmptySlot(): Integer;
  585. var
  586.  i: Integer;
  587. begin
  588.  Result:= -1;
  589.  for i:= 0 to Length(Data) - 1 do
  590.   if (Data[i] = nil) then
  591.    begin
  592.     Result:= i;
  593.     Break;
  594.    end;
  595. end;
  596. //---------------------------------------------------------------------------
  597. function TAsphyreFonts.Insert(Element: TAsphyreNativeFont): Integer;
  598. var
  599.  Slot: Integer;
  600. begin
  601.  Slot:= FindEmptySlot();
  602.  if (Slot = -1) then
  603.   begin
  604.    Slot:= Length(Data);
  605.    SetLength(Data, Slot + 1);
  606.   end;
  607.  Data[Slot]:= Element;
  608.  Element.FFontIndex:= Slot;
  609.  SearchDirty:= True;
  610.  Result:= Slot;
  611. end;
  612. //---------------------------------------------------------------------------
  613. function TAsphyreFonts.Include(Element: TAsphyreNativeFont): Integer;
  614. begin
  615.  Result:= IndexOf(Element);
  616.  if (Result = -1) then Result:= Insert(Element);
  617. end;
  618. //---------------------------------------------------------------------------
  619. procedure TAsphyreFonts.Exclude(Element: TAsphyreNativeFont);
  620. var
  621.  Index: Integer;
  622. begin
  623.  Index:= IndexOf(Element);
  624.  if (Index <> -1) then
  625.   begin
  626.    Data[Index]:= nil;
  627.    SearchDirty:= True;
  628.   end;
  629. end;
  630. //---------------------------------------------------------------------------
  631. procedure TAsphyreFonts.Remove(Num: Integer);
  632. begin
  633.  if (Num < 0)or(Num >= Length(Data)) then Exit;
  634.  Data[Num].NoExclude:= True;
  635.  Data[Num].Free();
  636.  Data[Num]:= nil;
  637.  SearchDirty:= True;
  638. end;
  639. //---------------------------------------------------------------------------
  640. procedure TAsphyreFonts.RemoveAll();
  641. var
  642.  i: Integer;
  643. begin
  644.  for i:= 0 to Length(Data) - 1 do
  645.   if (Data[i] <> nil) then
  646.    begin
  647.     Data[i].NoExclude:= True;
  648.     Data[i].Free();
  649.     Data[i]:= nil;
  650.    end;
  651.  SetLength(Data, 0);
  652.  SetLength(SearchObjects, 0);
  653.  SearchDirty:= False;
  654. end;
  655. //---------------------------------------------------------------------------
  656. function TAsphyreFonts.ResolveFont(Name: string): Integer;
  657. var
  658.  Index  : Integer;
  659.  FontDesc: PFontDesc;
  660.  Instance: TAsphyreNativeFont;
  661. begin
  662.  // (1) Identifiers are not case-sensitive.
  663.  Name:= LowerCase(Name);
  664.  // (2) Check whether the font has been previously loaded
  665.  Index:= IndexOf(Name);
  666.  if (Index <> -1) then
  667.   begin
  668.    Result:= Index;
  669.    Exit;
  670.   end;
  671.  // (3) Determine if description for the font exists.
  672.  FontDesc:= FontGroups.Find(Name, MediaOption);
  673.  if (FontDesc = nil) then
  674.   begin
  675.    EventResolveFailed.Notify(FDevice, Self, PChar(Name));
  676.    Result:= -1;
  677.    Exit;
  678.   end;
  679.  // (4) Create the particular image type.
  680.  Instance:= TAsphyreNativeFont.Create(Self);
  681.  // (5) Notify about symbol load.
  682.  EventSymbolResolve.Notify(FDevice, Self, PChar(Name));
  683.  // (6) Load and initialize image specification.
  684.  Instance.FName:= LowerCase(Name);
  685.  if (not Instance.Initialize(FontDesc)) then
  686.   begin
  687.    EventResolveFailed.Notify(FDevice, Self, PChar(Name));
  688.    Instance.Free();
  689.    Result:= -1;
  690.    Exit;
  691.   end;
  692.  // (7) Ok, we have a new font symbol in the list.
  693.  Result:= Instance.FontIndex;
  694. end;
  695. //---------------------------------------------------------------------------
  696. function TAsphyreFonts.GetFont(const Name: string): TAsphyreNativeFont;
  697. var
  698.  Index: Integer;
  699. begin
  700.  Index:= ResolveFont(Name);
  701.  if (Index <> -1) then Result:= Data[Index] else Result:= nil;
  702. end;
  703. //---------------------------------------------------------------------------
  704. procedure TAsphyreFonts.OnDeviceDestroy(Sender: TObject; EventParam: Pointer;
  705.  var Success: Boolean);
  706. begin
  707.  RemoveAll();
  708. end;
  709. //---------------------------------------------------------------------------
  710. procedure TAsphyreFonts.UnloadGroup(const GroupName: string);
  711. var
  712.  i: Integer;
  713.  Group: TFontGroup;
  714.  Desc: PFontDesc;
  715. begin
  716.  Group:= FontGroups.Group[GroupName];
  717.  if (Group = nil) then Exit;
  718.  for i:= 0 to Length(Data) - 1 do
  719.   begin
  720.    Desc:= Group.Find(Data[i].Name);
  721.    if (Desc <> nil) then Remove(i);
  722.   end;
  723. end;
  724. //---------------------------------------------------------------------------
  725. end.