//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4 (Public License)
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Anti-Grain Geometry - Version 2.4 Release Milano 3 (AggPas 2.4 RM3)
// Pascal Port By: Milan Marusinec alias Milano
//                 milan@marusinec.sk
//                 http://www.aggpas.org
// Copyright (c) 2005-2006
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//
// [Pascal Port History] -----------------------------------------------------
//
// 23.06.2006-Milano: ptrcomp adjustments
// 16.02.2006-Milano: Unit port establishment
//
{ agg_font_cache_manager.pas }
unit
 agg_font_cache_manager ;

INTERFACE

{$I agg_mode.inc }

uses
 SysUtils ,
 agg_basics ,
 agg_array ,
 agg_font_engine ,
 agg_path_storage_integer ;

{ TYPES DEFINITION }
const
 glyph_data_invalid = 0;
 glyph_data_mono    = 1;
 glyph_data_gray8   = 2;
 glyph_data_outline = 3;

 block_size = 16384 - 16;

type
 glyph_cache_ptr_ptr = ^glyph_cache_ptr;
 glyph_cache_ptr = ^glyph_cache;
 glyph_cache = record
   glyph_index : unsigned;

   data : int8u_ptr;

   data_size ,
   data_type : unsigned;//: int8u;

   bounds : rect;

   advance_x ,
   advance_y : double;

  end;

 font_cache_ptr_ptr = ^font_cache_ptr; 
 font_cache_ptr = ^font_cache;
 font_cache = object
   m_allocator      : pod_allocator;
   m_glyphs         : array[0..256] of glyph_cache_ptr_ptr;
   m_font_signature : PChar;

   constructor Construct(font_signature : PChar );
   destructor  Destruct;

   function  font_is(font_signature : PChar ) : boolean;

   function  find_glyph(glyph_code : unsigned ) : glyph_cache_ptr;

   function  cache_glyph(
              glyph_code ,
              glyph_index ,
              data_size : unsigned;
              data_type : int8u;
              bounds : rect_ptr;
              advance_x ,
              advance_y : double ) : glyph_cache_ptr;

  end;

 font_cache_pool = object
   m_fonts     : font_cache_ptr_ptr;
   m_max_fonts ,
   m_num_fonts : unsigned;
   m_cur_font  : font_cache_ptr;

   constructor Construct(max_fonts : unsigned = 32 );
   destructor  Destruct;

   procedure font_(font_signature : PChar; reset_cache : boolean = false );
   function  _font : font_cache_ptr;

   function  find_glyph(glyph_code : unsigned ) : glyph_cache_ptr;

   function  cache_glyph(
              glyph_code ,
              glyph_index ,
              data_size : unsigned;
              data_type : int8u;
              bounds : rect_ptr;
              advance_x ,
              advance_y : double ) : glyph_cache_ptr;

   function  find_font(font_signature : PChar ) : int;

  end;

 glyph_rendering = (
  glyph_ren_native_mono ,
  glyph_ren_native_gray8 ,
  glyph_ren_outline ,
  glyph_ren_agg_mono ,
  glyph_ren_agg_gray8 );

 font_cache_manager_ptr = ^font_cache_manager; 
 font_cache_manager = object
   m_fonts  : font_cache_pool;
   m_engine : font_engine_ptr;

   m_change_stamp : int;

   m_dx ,
   m_dy : double;

   m_prev_glyph ,
   m_last_glyph : glyph_cache_ptr;

   m_path_adaptor   : path_adaptor_type_ptr;
   m_gray8_adaptor  : gray8_adaptor_type;
   m_gray8_scanline : gray8_scanline_type;
   m_mono_adaptor   : mono_adaptor_type;
   m_mono_scanline  : mono_scanline_type;

   constructor Construct(engine : font_engine_ptr; max_fonts : unsigned = 32 );
   destructor  Destruct;

   function  glyph(glyph_code : unsigned ) : glyph_cache_ptr;

   procedure init_embedded_adaptors(gl : glyph_cache_ptr; x ,y : double; scale : double = 1.0 );

   function  path_adaptor : path_adaptor_type_ptr;
   function  gray8_adaptor : gray8_adaptor_type_ptr;
   function  gray8_scanline : gray8_scanline_type_ptr;
   function  mono_adaptor : mono_adaptor_type_ptr;
   function  mono_scanline : mono_scanline_type_ptr;

   function  prev_glyph : glyph_cache_ptr;
   function  last_glyph : glyph_cache_ptr;

   function  add_kerning(x ,y : double_ptr ) : boolean;

   procedure precache(from ,to_ : unsigned );
   procedure reset_cache;
   procedure synchronize;

  end;

{ GLOBAL PROCEDURES }


IMPLEMENTATION
{ LOCAL VARIABLES & CONSTANTS }
{ UNIT IMPLEMENTATION }
{ CONSTRUCT }
constructor font_cache.Construct;
begin
 m_allocator.Construct(block_size );

 m_font_signature:=PChar(m_allocator.allocate(StrLen(font_signature ) + 1 ) );

 StrCopy (m_font_signature ,font_signature );
 fillchar(m_glyphs ,sizeof(m_glyphs ) ,0 );

end;

{ DESTRUCT }
destructor font_cache.Destruct;
begin
 m_allocator.Destruct;

end;

{ FONT_IS }
function font_cache.font_is;
begin
 result:=StrComp(font_signature ,m_font_signature ) = 0;

end;

{ FIND_GLYPH }
function font_cache.find_glyph;
var
 msb : unsigned;

begin
 msb:=(glyph_code shr 8 ) and $FF;

 if m_glyphs[msb ] <> NIL then
  result:=
   glyph_cache_ptr_ptr(
    ptrcomp(m_glyphs[msb ] ) + (glyph_code and $FF ) * sizeof(glyph_cache_ptr ) )^
 else
  result:=NIL;

end;

{ CACHE_GLYPH }
function font_cache.cache_glyph;
var
 msb ,lsb : unsigned;

 glyph : glyph_cache_ptr;

begin
 msb:=(glyph_code shr 8 ) and $FF;

 if m_glyphs[msb ] = NIL then
  begin
   m_glyphs[msb ]:=glyph_cache_ptr_ptr(
    m_allocator.allocate(sizeof(glyph_cache_ptr ) * 256 ,sizeof(glyph_cache_ptr ) ) );

   fillchar(m_glyphs[msb ]^ ,sizeof(glyph_cache_ptr ) * 256 ,0 );

  end;

 lsb:=glyph_code and $FF;

 if glyph_cache_ptr_ptr(ptrcomp(m_glyphs[msb ] ) + lsb * sizeof(glyph_cache_ptr ) )^ <> NIL then
  begin
   result:=NIL; // Already exists, do not overwrite

   exit;

  end;

 glyph:=glyph_cache_ptr(m_allocator.allocate(sizeof(glyph_cache ) ,sizeof(double ) ) );

 glyph.glyph_index:=glyph_index;
 glyph.data       :=m_allocator.allocate(data_size );
 glyph.data_size  :=data_size;
 glyph.data_type  :=data_type;
 glyph.bounds     :=bounds^;
 glyph.advance_x  :=advance_x;
 glyph.advance_y  :=advance_y;

 glyph_cache_ptr_ptr(ptrcomp(m_glyphs[msb ] ) + lsb * sizeof(glyph_cache_ptr ) )^:=glyph;

 result:=glyph;

end;

{ CONSTRUCT }
constructor font_cache_pool.Construct;
begin
 agg_getmem(pointer(m_fonts ) ,max_fonts * sizeof(font_cache_ptr ) );

 m_max_fonts:=max_fonts;
 m_num_fonts:=0;
 m_cur_font :=NIL;

end;

{ DESTRUCT }
destructor font_cache_pool.Destruct;
var
 i : unsigned;

 fnt : font_cache_ptr_ptr;

begin
 fnt:=m_fonts;
 i  :=0;

 while i < m_num_fonts do
  begin
   dispose(fnt^ ,Destruct );

   inc(ptrcomp(fnt ) ,sizeof(font_cache_ptr ) );
   inc(i );

  end;

 agg_freemem(pointer(m_fonts ) ,m_max_fonts * sizeof(font_cache_ptr ) );

end;

{ FONT_ }
procedure font_cache_pool.font_;
var
 idx : int;
 fnt : font_cache_ptr_ptr;

begin
 idx:=find_font(font_signature );

 if idx >= 0 then
  begin
   fnt:=font_cache_ptr_ptr(ptrcomp(m_fonts ) + idx * sizeof(font_cache_ptr ) );

   if reset_cache then
    begin
     dispose(fnt^ ,Destruct );

     fnt^:=new(font_cache_ptr ,Construct(font_signature ) );

    end;

   m_cur_font:=fnt^;

  end
 else
  begin
   if m_num_fonts >= m_max_fonts then
    begin
     fnt:=font_cache_ptr_ptr(ptrcomp(m_fonts ) + 1 * sizeof(font_cache_ptr ) );

     dispose(m_fonts^ ,Destruct );
     move   (fnt^ ,m_fonts^ ,(m_max_fonts - 1 ) * sizeof(font_cache_ptr ) );

     m_num_fonts:=m_max_fonts - 1;

    end;

   fnt :=font_cache_ptr_ptr(ptrcomp(m_fonts ) + m_num_fonts * sizeof(font_cache_ptr ) );
   fnt^:=new(font_cache_ptr ,Construct(font_signature ) );

   m_cur_font:=fnt^;

   inc(m_num_fonts );

  end;

end;

{ _FONT }
function font_cache_pool._font;
begin
 result:=m_cur_font;

end;

{ FIND_GLYPH }
function font_cache_pool.find_glyph;
begin
 if m_cur_font <> NIL then
  result:=m_cur_font.find_glyph(glyph_code )
 else
  result:=NIL;

end;

{ CACHE_GLYPH }
function font_cache_pool.cache_glyph;
begin
 if m_cur_font <> NIL then
  result:=
   m_cur_font.cache_glyph(
    glyph_code ,
    glyph_index ,
    data_size ,
    data_type ,
    bounds ,
    advance_x ,
    advance_y )
 else
  result:=NIL;

end;

{ FIND_FONT }
function font_cache_pool.find_font;
var
 i : unsigned;
 f : font_cache_ptr_ptr;

begin
 i:=0;
 f:=m_fonts;

 while i < m_num_fonts do
  begin
   if f^.font_is(font_signature ) then
    begin
     result:=i;

     exit;

    end;

   inc(ptrcomp(f ) ,sizeof(font_cache_ptr ) );
   inc(i );

  end;

 result:=-1;

end;

{ CONSTRUCT }
constructor font_cache_manager.Construct(engine : font_engine_ptr; max_fonts : unsigned);
begin
 m_fonts.Construct(max_fonts );

 m_engine:=engine;

 m_change_stamp:=-1;

 m_prev_glyph:=NIL;
 m_last_glyph:=NIL;

 if m_engine.flag32 then
  m_path_adaptor:=new(serialized_int32_path_adaptor_ptr ,Construct )
 else
  m_path_adaptor:=new(serialized_int16_path_adaptor_ptr ,Construct );

 m_gray8_adaptor.Construct;
 m_gray8_scanline.Construct(m_gray8_adaptor.m_sz );
 m_mono_adaptor.Construct;
 m_mono_scanline.Construct;

end;

{ DESTRUCT }
destructor font_cache_manager.Destruct;
begin
 m_fonts.Destruct;

 dispose(m_path_adaptor ,Destruct );

end;

{ GLYPH }
function font_cache_manager.glyph;
var
 gl : glyph_cache_ptr;

begin
 synchronize;

 gl:=m_fonts.find_glyph(glyph_code );

 if gl <> NIL then
  begin
   m_prev_glyph:=m_last_glyph;
   m_last_glyph:=gl;

   result:=gl;

   exit;

  end
 else
  if m_engine.prepare_glyph(glyph_code ) then
   begin
     m_prev_glyph:=m_last_glyph;
     m_last_glyph:=
      m_fonts.cache_glyph(
       glyph_code ,
       m_engine.glyph_index ,
       m_engine.data_size ,
       m_engine.data_type ,
       m_engine.bounds ,
       m_engine.advance_x ,
       m_engine.advance_y );

    m_engine.write_glyph_to(m_last_glyph.data );

    result:=m_last_glyph;

    exit;

   end;

 result:=NIL;

end;

{ INIT_EMBEDDED_ADAPTORS }
procedure font_cache_manager.init_embedded_adaptors;
begin
 if gl <> NIL then
  case gl.data_type of
   glyph_data_mono :
    m_mono_adaptor.init(gl.data ,gl.data_size ,x ,y );

   glyph_data_gray8 :
    m_gray8_adaptor.init(gl.data ,gl.data_size ,x ,y );

   glyph_data_outline :
    m_path_adaptor.init(gl.data ,gl.data_size ,x ,y ,scale );

  end;

end;

{ PATH_ADAPTOR }
function font_cache_manager.path_adaptor;
begin
 result:=m_path_adaptor;

end;

{ GRAY8_ADAPTOR }
function font_cache_manager.gray8_adaptor;
begin
 result:=@m_gray8_adaptor;

end;

{ GRAY8_SCANLINE }
function font_cache_manager.gray8_scanline;
begin
 result:=@m_gray8_scanline;

end;

{ MONO_ADAPTOR }
function font_cache_manager.mono_adaptor;
begin
 result:=@m_mono_adaptor;

end;

{ MONO_SCANLINE }
function font_cache_manager.mono_scanline;
begin
 result:=@m_mono_scanline;

end;

{ PREV_GLYPH }
function font_cache_manager.prev_glyph;
begin
 result:=m_prev_glyph;

end;

{ LAST_GLYPH }
function font_cache_manager.last_glyph;
begin
 result:=@m_last_glyph;

end;

{ ADD_KERNING }
function font_cache_manager.add_kerning;
begin
 if (m_prev_glyph <> NIL ) and
    (m_last_glyph <> NIL ) then
  result:=
   m_engine.add_kerning(
    m_prev_glyph.glyph_index ,
    m_last_glyph.glyph_index ,x ,y )
 else
  result:=false;

end;

{ PRECACHE }
procedure font_cache_manager.precache;
begin
 while from <= to_ do
  begin
   glyph(from );
   inc  (from );

  end;

end;

{ RESET_CACHE }
procedure font_cache_manager.reset_cache;
begin
 m_fonts.font_(m_engine.font_signature ,true );

 m_change_stamp:=m_engine.change_stamp;

 m_prev_glyph:=NIL;
 m_last_glyph:=NIL;

end;

{ SYNCHRONIZE }
procedure font_cache_manager.synchronize;
begin
 if m_change_stamp <> m_engine.change_stamp then
  begin
   m_fonts.font_(m_engine.font_signature );

   m_change_stamp:=m_engine.change_stamp;

   m_prev_glyph:=NIL;
   m_last_glyph:=NIL;

  end;

end;

END.

