unit vnm_fn;
{****************************************************************************}
{Unit VNM_FN - it is a addon unit for graphics library VenomGFX.             }
{It brings a loader for .FN bitmap font file.                                }
{****************************************************************************}

{$IFDEF VER2}{$DEFINE NEWFPC}{$ENDIF}
{$IFDEF VER3}{$DEFINE NEWFPC}{$ENDIF}
{$IFDEF NEWFPC}{$CALLING OLDFPCCALL}{$ENDIF}
interface
uses VnmFnHlp;

type
PFontFN = ^TFontFN;
TFontFN = object(TBitMapZnaky256)
{vnitrnijmeno:string[32];}
maxpred,maxza,maxnad,maxpod:shortint;

Constructor Init;
Function Load_FN(s:string):boolean;
Function VyskaRadky:byte;virtual;
Function VratVelikost:byte;virtual;
Destructor Done;virtual;
end;

Function Load_FN_font(s:string;size:longint):pointer;
Function Zkontroluj_Format_FN(s:string):boolean;

var global_fn_loader_popisek:string;

const vnm_fn_dbg:byte=0;

implementation
uses GrpFile,VenomGFX,VenomMng;


const
     fnmagic  = 'ëmÕonç ';
     fnmagic2 = ' çnoÕmë';


function MyVal (S: string): longint;
var
  Pom2 : Integer;
  pom1 : longint;
begin { MyVal }
  Val (S, Pom1, Pom2);
  MyVal := Pom1;
end;  { MyVal }


Constructor TFontFN.Init;
begin
inherited Init(0);
prop:=true;
format:=FNFMT_FN;
{vnitrnijmeno:='';}
end;


Function Zkontroluj_Format_FN(s:string):boolean;
var grp:TGrpStream;
    mgl:byte;
    ss:string;

begin
grp.Init(DoplnJmenoFontu(s),grpOpenRead);
if grp.status<>grpOK then Exit(false);
if grp.GetSize<15 then
   begin
   grp.Done;
   Exit(false);
   end;

mgl:=Length(fnmagic);

ss[0]:=char(mgl);
grp.Read(ss[1],mgl);
grp.Done;
if ss<>fnmagic then Exit(false);  {konrola zahlavi}
Zkontroluj_Format_FN:=true;
end;




Function TFontFN.Load_FN(s:string):boolean;
var grp:TGrpStream;
    a,b,c,l,v,w:longint;
    _c,_d:^longint;
    z2,z3,t:^byte;
    n,m,oo:word;
    _n:^word;
    mgl,ftbyte,mxshift,hdr_sbunky:byte;
    p:pchar;
    ss,s2:string;
    pracbuf:packed array[0..4095] of byte;
    korX_np_tabulka:packed array[0..255] of shortint;
    z:pointer;


Procedure DekomprimujTabulkuKorX(tpo:longint);
var aa:longint;
    bb:byte;
    bb1,bb2:byte;
begin
bb1:=byte(p[tpo+0]);
bb2:=byte(p[tpo+1]);
aa:=tpo+2;
for bb:=bb1 to bb2 do
    begin
    korX_np_tabulka[bb]:=shortint(p[aa]);
    inc(aa);
    end;
end;


begin
ss:=DoplnJmenoFontu(s);
grp.Init(ss,grpOpenRead);
if grp.status<>grpOK then Exit(false);
if grp.GetSize<15 then Exit(false);

mgl:=Length(fnmagic);

ss[0]:=char(mgl);
grp.Read(ss[1],mgl);
if ss<>fnmagic then begin grp.Done;Exit(false);end;  {konrola zahlavi}

l:=grp.GetSize-mgl;

GetMem(p,l);             {pripravim si pamet}
grp.ReadStream(p^,l);    {nahraju do ni zbytek souboru}
grp.Done;                {ted uz muzu soubor zavrit}

a:=IndexByte(p^,l,0);    {kde v bufferu je prvni ASCII 0 ?}

hdr_sbunky:=0;
kodova_stranka:=895;     {fallback}

{Struktura FN souboru
 MAGIC
 zahlavi
 odkazy na data
 <muze a nemusi nasledovat pridatna tabulka s korX_np>
 data
    ev. extra udaje na konci souboru [ruzny pocet bajtu]
    "feature byte"   [1]
    velikost datoveho bloku na konci souboru [4]
        (pozn. nepocitaji se tyto 4 bajty a zahlavi, co bude nasledovat)
    ukoncovaci magic, signalizujici, ze jsou extra data budou na konci souboru

 bity z feature byte:
   0. - 1: kodova stranka je platna
        0: kodova stranka je nevalidni a ma se ignorovat

   1. - 1: hodnota sirky bunky (pro neproporcionalni mod) je platna
        0: hodnota sirky bunky je neplatna a je treba ji zjistit analyzou

   2. - 1: od poziz -12..-9 bude DWORD offset do souboru pro pridatnou
           tabulku s korX_np

Struktura pridatne tabulky s korX_np:
0.bajt: first - prvni znak s definovanou korX_np               (byte)
1.bajt: last - posledni znak s definovanou korX_np             (byte)
N (last-first+1) bajtu - hodnoty korX_np pro jednotlive znaky  (shortint)
                         Muze se tam vyskytovat i hodnota -128,
                         t.j. "nedefinovano"
}




if a<>0 then
   begin
   ss[0]:=char(a);
   Move(p^,ss[1],a);
   global_fn_loader_popisek:=ss;
   end
   else global_fn_loader_popisek:='';

maxpred     :=127;
maxza       :=-127;
maxnad      :=127;
maxpod      :=-127;
mxshift:=0;

FillChar(korX_np_tabulka,256,byte(-128));

first:=byte(p[a+1]);
last:=byte(p[a+2]);
pocetzn:=0;
if first>last then begin first:=0;last:=0;end
   else if last>0 then pocetzn:=last-first+1;

so:=shortint(p[a+3]);
su:=shortint(p[a+4]);
sosu:=so+su;
vel:=so+su;
add:=shortint(p[a+5]);
ftbyte:=byte(p[a+6]);


b:=l-mgl;
ss[0]:=char(mgl);
Move(p[b],ss[1],mgl); {prozkoumame MGL poslednich bajtu souboru}
if ss=fnmagic2 then   {je v tech poslednich bajtech specialni signatura?}
   begin          {Je tu! Pozn. Pred touto signaturou jsou 4 bajty...}
   _c:=@p[b-4];   {...ve kterych je ulozeno kolik dalsich bajtu bude predchazet}
   if _c^>1 then
      begin
      t:=@p[b-5];           {"feature byte"}
      if (t^ and 1)<>0 then {je ve "feature" oznacen nulty bit?}
         begin              {to znamena, ze mame definovanou kodovou stranku}
         _n:=@p[b-7];
         kodova_stranka:=_n^;
         end;
      if (t^ and 2)<>0 then {je ve "feature" oznacen prvni bit?}
         begin              {to znamena, ze mame definovanou sirku znakove}
         z2:=@p[b-8];       {bunky pro neproporcionalni mod}
         hdr_sbunky:=z2^;
         end;
      if (t^ and 4)<>0 then {je ve "feature" oznacen druhy bit?}
         begin              {to znamena ze v -12..-9 je offset k tabulce korX_np}
         _d:=@p[b-12];
         DekomprimujTabulkuKorX(_d^);
         end;
      end;
   end;



z:=@pracbuf;
for b:=0 to 255 do Znaky256[b].Init;
{vsechny znaky jsou sice zinicializovane, ale bitmapa je NIL, rozmery 0
 DATA=nil a paramatr READY je 0}

if pocetzn>0 then
begin
for b:=first to last do
    begin
    c:=(b-first)*7+a+mgl;
    znaky256[b].relx:=shortint(p[c+0]);
    znaky256[b].rely:=shortint(p[c+1]);
    znaky256[b].sirka:=byte(p[c+2]);
    znaky256[b].vyska:=byte(p[c+3]);
    znaky256[b].shift:=shortint(p[c+4]);
    znaky256[b].bunek_np:=1;
    znaky256[b].ready:=2;


{debug} {if (znaky256[b].sirka=0) or (znaky256[b].sirka>200) then
           begin
           writeln('divna sirka, znak: ',b,'...sirka: ',znaky256[b].sirka);
           readln;
           end;}

    if znaky256[b].sirka>max_sirka_bitmapy
       then max_sirka_bitmapy:=znaky256[b].sirka;

    v:=znaky256[b].sirka*znaky256[b].vyska; {pocet bodu, ze kterych znak je}
    if v>0 then   {znaky typu mezera maji SIRKA=0. Pro ty nebudu alokovat bitmapu}
       begin
       GetMem(znaky256[b].data,v); {alokuje bitmapu}
       w:=longint(p[c+5])+longint(p[c+6])*256;

       n:=(znaky256[b].sirka+7) div 8;
       t:=znaky256[b].data;     { zapisovaci pointer nastavi na bitmapu }
       for oo:=0 to znaky256[b].vyska-1 do
           begin
           z2:=z;
           for m:=0 to n-1 do
               begin
               ZnakBuf_Expand(byte(p[w-mgl+oo*n+m]),z2);
               inc(z2,8);
               end;
           Move(z^,t^,znaky256[b].sirka);
           inc(t,znaky256[b].sirka);
           end;

      {znak jsme dekomprimovali, ale ted ho prekvapive budu znovu komprimovat}
      {predchozi komprese totiz pakovala jednotlive radky zvlast, kdezto ja
      zapakuju celou bitmapu vcelku}
      znaky256[b].Komprimuj;
      end
      else begin     { V<=0 }
      if (znaky256[b].sirka=0) and (znaky256[b].shift=0)
         then begin      {tyto znaky budeme povazovat za nedefinovane}
         dec(pocetzn);
         if b=first then inc(first);
         if b=last then dec(last);
         znaky256[b].Done;
         end;

      end;

      with znaky256[b] do
         begin
         if relX<maxpred       then maxpred :=relX;
         if relY<maxnad        then maxnad  :=relY;
         if relX+sirka-1>maxza  then maxza   :=relX+sirka-1;
         if relY+vyska-1>maxpod then maxpod  :=relY+vyska-1;
         if shift>mxshift then mxshift:=shift;
         end;{with}
      end; {for}
end;

if hdr_sbunky>0 then sbunky:=hdr_sbunky else sbunky:=mxshift;
if pocetzn=0 then begin first:=0;last:=0;end
   else begin               {jeste musime vyresit parametr korX_np}
   for b:=first to last do  {korekce pro zobrazeni v neproporc. modu}
       begin
       if korX_np_tabulka[b]=-128
          then znaky256[b].korX_np:=(sbunky div 2) - (znaky256[b].shift div 2)
          else znaky256[b].korX_np:=korX_np_tabulka[b];
       end;
   end;

system.FreeMem(p,l);
Load_FN:=true;
end;


Function TFontFN.VyskaRadky:byte;
begin
VyskaRadky:=so+su;
end;


Function TFontFN.VratVelikost:byte;
begin
VratVelikost:=vel;
end;


Destructor TFontFN.Done;
begin
inherited Done;
end;


Function Load_FN_font(s:string;size:longint):pointer;
var a,b:byte;
    grp:TGrpStream;
    n:string;
    l:longint;
    ok:boolean;
    pf:PFontFN;
    hf:PObecnyFont;

begin
pf:=New(PFontFN,Init);
pf^.rez:=NazevBezCesty(s);
if vnm_fn_dbg>0 then
   a:=a;  {/debug}
ok:=pf^.Load_FN(s);
if ok=false then begin Dispose(pf,Done);Exit(nil);end;

hf:=New(PObecnyFont,Init);
hf^.fdata:=pf;
pf^.rukojet:=hf;
hf^.typzdroje:=2;
{0 = nevyplneno/neznamo
 1 = VGA
 2 = samostatne nacteno (nikoliv v kontejneru)
 3 = bitmapovy kontejner (napr. GRP soubor)
 4 = vektorovy kontejner
}
Load_FN_font:=hf;
end;


Function FN_font_setstyle(fnt:pointer;podfunkce,param1,param2:longint):pointer;
var hf:PObecnyFont;
    n,m:byte;
begin
hf:=fnt;
if podfunkce=2 then
   if (param1 and prop_fn)<>0
      then VNMFN_PROP_MODE:=true
      else VNMFN_PROP_MODE:=false;
FN_font_setstyle:=hf;
end;


Function FN_Font_PrepChar(fnt:pointer;znak:word):pointer;
var hf:PObecnyFont;
begin
hf:=fnt;
FN_Font_PrepChar:=hf^.FData^.PrepChar(znak);
end;



Procedure FN_font_OutText(kam:pointer;x,y:longint;s:string;fnt:pointer;color:word);
var hf:PObecnyFont;
    pf:PFontFN;

begin
if fnt<>nil then
   begin
   hf:=fnt;
   pf:=PFontFN(hf^.fdata);
   VnmFnHlp_OutText(kam,x,y,s,pf,color);
   end;
end;


Function FN_Font_GetInfo(fnt:pointer;param1,param2:longint):longint;
var hf:PObecnyFont;
    i:longint;

begin
hf:=fnt;
i:=hf^.GetInfo(param1,param2);
FN_Font_GetInfo:=i;
end;


Function FN_Font_delete(fnt:pointer;mode:byte):boolean;
var hf:PObecnyFont;
begin
hf:=fnt;
Dispose(hf,Done);       {automaticky smaze i hf^.FData (ve formatu PFontFN)}
FN_Font_delete:=true;
end;


Procedure Register_FN_Loader;
begin
RegisterFontEngine('FN',
                   @Load_FN_font,
                   @FN_Font_PrepChar,
                   @FN_Font_OutText,
                   @FN_Font_setstyle,
                   @FN_Font_GetInfo,
                   @FN_Font_delete);

end;




begin
Register_FN_Loader;
end.
