ZenGl中文字体渲染解决方案。

来源:互联网 发布:销售公司数据流程图 编辑:程序博客网 时间:2024/06/05 17:01
//HGE 中文显示新方案//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//Author  - 微妙的平衡(BOGY)//Mail    - bogy.cn@gmail.com//Home    - http://bogy.cn//Porting to ZenGl by 一路随云//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~unit GfxFont;interfaceuses  Windows, {HGE, HGESprite,}zgl_main,zgl_textures,zgl_sprite_2d,zgl_types,zgl_fx,Vcl.Graphics;const  Font_Count = High(Word);type  tagEngineFontGlyph = record   // t: ITexture; //纹理    t: ZglPTexture;    w: Single;    h: Single;    x: Single;    y: Single;    c: Single;  end;  TENGINEFONTGLYPH = tagEngineFontGlyph;  TGfxFont = class  private     procedure Print(X: Single; Y: Single; Text: PWideChar);     function GetTextSize(Text: PWideChar): TSize;  public    constructor Create(const FontName: PWideChar; FaceSize: Integer; bBold: Boolean = False;      bItalic: Boolean = False; bAntialias: Boolean = True);    destructor Destroy(); override;  public    // 渲染文本    procedure TextOut(x,y:Single;Text:string);    // 设置与获取颜色    procedure SetColor(Color: Cardinal);overload;    Procedure SetColor(Color:TColor);overload;    function GetColor: Cardinal;    // 获取文本宽高    Function TextWidth(Text:String):Integer;    Function TextHeight(Text:String):Integer;    // 根据坐标获取字符    function GetCharacterFromPos(Text: PWideChar; Pixel_X, Pixel_Y: Single): WideChar;    // 设置字间距    procedure SetKerningWidth(Kerning: Single);    procedure SetKerningHeight(Kerning: Single);    // 获取字间距    function GetKerningWidth(): Single;    function GetKerningHeight(): Single;    // 字体大小    function GetFontSize(): Single;  private    m_Glyphs: array [0..Font_Count-1] of TENGINEFONTGLYPH;    m_nAntialias: Cardinal;//反锯齿    m_nAscent: Integer;//基线    //m_dwFontColor: Cardinal;    m_nFontSize: Single;    m_nKerningWidth: Single;    m_nKerningHeight: Single;    //m_pHGE: IHGE;   //Zengl不需要    //m_pSprite: IHGESprite;    m_pSprite:ZglPTexture;    // GDI设备    m_hMemDC: HDC;    m_hFont: HFONT;    function GetGlyphByCharacter(C: WideChar): Cardinal;    function GetWidthFromCharacter(C: WideChar; Original: Boolean = False): Single;    procedure CacheCharacter(Idx: Cardinal; C: WideChar);  end;function RGBA(const R, G, B, A: Byte): Longword; inline;implementation{ TGfxFont }const  g_byAlphaLever: array [0..65 - 1] of Byte =  (    0,  4,  8,  12, 16, 20, 24, 28, 32, 36, 40, 44, 48,      52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100,    104,108,112,116,120,124,128,132,136,140,144,148,152,    156,160,164,168,172,176,180,184,188,192,196,200,204,    208,212,216,220,224,228,232,236,240,244,248,252,255  );const  WideNull  = WideChar(#0);  WideCR    = WideChar(#13);  WideLF    = WideChar(#10);  WideCRLF  : WideString = #13#10;  type  TByteAry = array [0..0] of Byte;  PByteAry = ^TByteAry;procedure TGfxFont.CacheCharacter(Idx: Cardinal; C: WideChar);var  nChar: Cardinal;  mat: MAT2;  gm: GLYPHMETRICS;  nLen: Cardinal;   // hTex: ITexture; //纹理  hTex:zglPTexture;  lpBuf: PByteAry;  lpSrc: PByteAry;  lpDst: PLongWord;  lpTexData:PByteArray;  nSrcPitch, nDstPitch: Cardinal;  x, y, k, i,W,H: Cardinal;begin  if (Idx < Font_Count) and (m_Glyphs[Idx].t = nil) then  begin    nChar := Cardinal(C);    mat.eM11.fract := 0; mat.eM11.value := 1;    mat.eM12.fract := 0; mat.eM12.value := 0;    mat.eM21.fract := 0; mat.eM21.value := 0;    mat.eM22.fract := 0; mat.eM22.value := 1;    nLen := GetGlyphOutlineW(m_hMemDC, nChar, m_nAntialias, gm, 0, nil, mat);    hTex := tex_CreateZero( gm.gmBlackBoxX, gm.gmBlackBoxY,$FFFFFFFF);    //hTex := m_pHGE.Texture_Create(gm.gmBlackBoxX, gm.gmBlackBoxY); //创建一个纹理用存放字体    if hTex = nil then Exit;    if nLen > 0 then    begin      GetMem(lpBuf, nLen);      if nLen = GetGlyphOutlineW(m_hMemDC, nChar, m_nAntialias, gm, nLen, lpBuf,        mat) then      begin        lpSrc := lpBuf;        //lpDst := m_pHGE.Texture_Lock(hTex, False);        //这里其实不需要GetData因为内部是空的啊        {这句是为了提高能效而代替上面的。没必要因为读取一个空纹理而从显存内从新拷贝一次数据}        GetMem( lpDst, Round( htex.Width / htex.U ) * Round( htex.Height / htex.V ) * 4 );        lpTexData:=Pointer(lpDst);        if GGO_BITMAP = m_nAntialias then        begin         //此为BITMAP抗锯齿模式          if gm.gmBlackBoxX mod 32 = 0 then            nSrcPitch := (gm.gmBlackBoxX div 32)*4          else            nSrcPitch := ((gm.gmBlackBoxX div 32) + 1)*4;          //nDstPitch := m_pHGE.Texture_GetWidth(hTex);//返回创建的纹理宽度          nDstPitch := hTex.Width;          H:=gm.gmBlackBoxY;          W:=gm.gmBlackBoxX;          for y := 0 to gm.gmBlackBoxY - 1 do          begin            x := 0;            while x < gm.gmBlackBoxX do            begin              for k := 0 to 7 do              begin                i := 8*x + k;                if i >= gm.gmBlackBoxX then                begin                  Inc(x, 7);                  Break;                end;                //这里同样需要镜像左右像素                if (lpSrc[x] shr (7 - k)) and 1 = 0 then                  PCardinal(Cardinal(lpDst) + (W-i-1) * SizeOf(Integer))^ := 0                else                  PCardinal(Cardinal(lpDst) + (W-i-1) * SizeOf(Integer))^ := $FFFFFFFF;              end;              Inc(x);            end;            Inc(lpSrc, nSrcPitch);            Inc(lpDst, nDstPitch);          end;          end        else        begin          if gm.gmBlackBoxX mod 4 = 0 then            nSrcPitch := (gm.gmBlackBoxX div 4)*4          else            nSrcPitch := (gm.gmBlackBoxX div 4 + 1)*4;          //nDstPitch := m_pHGE.Texture_GetWidth(hTex);          nDstPitch := hTex.Width;          //可能DX和OpenGL的纹理顺序不同,如果源代码不加改动 绘制出来的字          //是左右镜像了 并且旋转了180度 ,旋转的问题 我可以旋转着绘制。          //但是镜像只能交换左右位置像素来解决          H:=gm.gmBlackBoxY;          W:=gm.gmBlackBoxX;          for y := 0 to gm.gmBlackBoxY - 1 do          begin            for x := 0 to gm.gmBlackBoxX - 1 do            PCardinal(Cardinal(lpDst) + (W-X-1) * SizeOf(Integer))^ :=RGBA($FF,$FF,$FF,g_byAlphaLever[lpSrc[x]]);                //ARGB(g_byAlphaLever[lpSrc[x]], $FF, $FF, $FF);            Inc(lpSrc, nSrcPitch);            Inc(lpDst, nDstPitch);          end;        end;        tex_SetData(hTex,lpTexData,0,0,gm.gmBlackBoxX,gm.gmBlackBoxY);        FreeMem(lpTexData);        //m_pHGE.Texture_Unlock(hTex);  //解锁纹理      end;      FreeMem(lpBuf);    end    else    begin      // 非正常显示字符    end;    m_Glyphs[Idx].t := hTex;    m_Glyphs[Idx].w := gm.gmBlackBoxX;    m_Glyphs[Idx].h := gm.gmBlackBoxY;    m_Glyphs[Idx].x := -gm.gmptGlyphOrigin.X;    m_Glyphs[Idx].y := gm.gmptGlyphOrigin.Y - m_nAscent;    m_Glyphs[Idx].c := gm.gmCellIncX;  end;end;constructor TGfxFont.Create(const FontName: PWideChar; FaceSize: Integer;  bBold, bItalic, bAntialias: Boolean);var  h_DC: HDC;  Bold: Integer;  tm: TEXTMETRICW;begin // m_pHGE := HGECreate(HGE_VERSION);  // 创建GDI相关设备 // h_DC := GetDC(m_pHGE.System_GetState(HGE_HWND));//在窗口句柄上创建DC  h_DC := GetDC(zgl_Get(WINDOW_HANDLE));  m_hMemDC := CreateCompatibleDC(h_DC);  if m_hMemDC = 0 then Exit;  ReleaseDC(zgl_Get(WINDOW_HANDLE), h_DC); //释放窗口句柄  SetMapMode(m_hMemDC, MM_TEXT);  SetTextColor(m_hMemDC, RGB($FF, $FF, $FF)); //颜色  SetBkColor(m_hMemDC, RGB(0, 0, 0));  if bBold then    Bold := FW_BOLD  else    Bold := FW_NORMAL;  m_hFont := CreateFontW(    -FaceSize,    0,    0,    0,    Bold,    Integer(bItalic),    Cardinal(False),    Cardinal(False),    DEFAULT_CHARSET,    OUT_DEFAULT_PRECIS,    CLIP_DEFAULT_PRECIS,    DEFAULT_QUALITY,    FF_DONTCARE or DEFAULT_PITCH,    FontName);  if m_hFont = 0 then Exit;  SelectObject(m_hMemDC, m_hFont);  FillChar(m_Glyphs, SizeOf(TENGINEFONTGLYPH)*Font_Count, 0);  if bAntialias then    m_nAntialias := GGO_GRAY8_BITMAP  else    m_nAntialias := GGO_BITMAP;  GetTextMetricsW(m_hMemDC, tm);  m_nAscent := tm.tmAscent;  m_nFontSize := FaceSize;  m_nKerningWidth := 0;  m_nKerningHeight := 0;  SetColor($FFFFFFFF);//设置默认颜色为黑色  //m_pSprite := THGESprite.Create(nil, 0, 0, 0, 0); //创建一个精灵 用来绘制字体 // m_pSprite.SetColor(ARGB($FF, $FF, $FF, $FF));   // 白色透明  //m_pSprite:=tex_CreateZero(0,0,$FFFFFF);  //这里主要是建立一个空纹理 然后给与顶点数据。但是zgl这里给颜色是不需要的end;destructor TGfxFont.Destroy;var  nIdx: Integer;begin  for nIdx := 0 to Font_Count - 1 do    if m_Glyphs[nIdx].t <> nil then      m_Glyphs[nIdx].t := nil;  if m_hFont <> 0 then    DeleteObject(m_hFont);  if m_hMemDC <> 0 then    DeleteDC(m_hMemDC); // if m_pSprite <> nil then m_pSprite := nil;    if m_pSprite <> nil then tex_Del(m_pSprite); // if m_pHGE <> nil then m_pHGE := nil;  //ZenGL不需要  inherited;end;function TGfxFont.GetCharacterFromPos(Text: PWideChar; Pixel_X,  Pixel_Y: Single): WideChar;var  X, Y, W: Single;begin  X := 0; Y := 0;  while Text^ <> WideNull do  begin    if (Text^ = WideCR) and (PWideChar(Integer(Text)+SizeOf(WideChar))^ = WideLF) then    begin      X := 0;      Y := m_nFontSize + m_nKerningWidth;      Inc(Text);      if Text^ = WideNull then        Break;    end;      W := GetWidthFromCharacter(Text^);    if (Pixel_X > X) and (Pixel_X <= X + W) and      (Pixel_Y > Y) and (Pixel_Y <= Y + m_nFontSize) then    begin      Result := Text^;      Exit;    end;    X := X + W + m_nKerningWidth;    Inc(Text);  end;  Result := WideNull;end;function TGfxFont.GetColor: Cardinal;varR,G,B,A:Byte;begin  //Result := m_pSprite.GetColor(i);//返回颜色顶点颜色  R:=fx2dColor[0];  G:=fx2dColor[1];  B:=fx2dColor[2];  A:=fx2dColor[3];  Result:=RGBA(R,G,B,A);end;function TGfxFont.GetFontSize: Single;begin  Result := m_nFontSize;end;function TGfxFont.GetGlyphByCharacter(C: WideChar): Cardinal;var  Idx: Cardinal;begin  Idx := Cardinal(C);  if m_Glyphs[Idx].t = nil then    CacheCharacter(Idx, C);  Result := Idx;end;function TGfxFont.GetKerningHeight: Single;begin  Result := m_nKerningHeight;end;function TGfxFont.GetKerningWidth: Single;begin  Result := m_nKerningWidth;end;function TGfxFont.GetTextSize(Text: PWideChar): TSize;var  Dim: TSize;  nRowWidth: Single;begin  nRowWidth := 0;  Dim.cx := 0;  Dim.cy := Round(m_nFontSize);  while Text^ <> WideNull do  begin    if (Text^ = WideCR) and (PWideChar(Integer(Text)+SizeOf(WideChar))^ = WideLF) then    begin      Dim.cy := Round(m_nFontSize + m_nKerningHeight);      if Dim.cx < nRowWidth then        Dim.cx := Round(nRowWidth);      nRowWidth := 0;      Inc(Text, 2);    end    else    begin      nRowWidth := nRowWidth + GetWidthFromCharacter(Text^) + m_nKerningWidth;      Inc(Text);    end;  end;  if Dim.cx < Round(nRowWidth) then    Dim.cx := Round(nRowWidth);  Result := Dim;end;function TGfxFont.GetWidthFromCharacter(C: WideChar;  Original: Boolean): Single;var  Idx: Cardinal;begin  Idx := GetGlyphByCharacter(C);  if Original and (Idx > 0) and (Idx < Font_Count) then  begin    Result := m_Glyphs[Idx].c;    Exit;  end;  if Idx >= $2000 then    Result := m_nFontSize  else    Result := m_nFontSize/2;end;procedure TGfxFont.TextOut(x, y: Single; Text: string);var  lx,ly:Single;begin  if Text='' then exit;  lx:=x;  ly:=y;  Print(lx,ly,PWideChar(Text));end;procedure TGfxFont.Print(X, Y: Single; Text: PWideChar);var  OffsetX, OffsetY: Single;  Idx: Cardinal;begin  OffsetX := X; OffsetY := Y;  while Text^ <> WideNull do  begin    if (Text^ = WideCR) and (PWideChar(Integer(Text)+SizeOf(WideChar))^ = WideLF) then    begin      OffsetX := X;      OffsetY := OffsetY + m_nFontSize + m_nKerningHeight;      Inc(Text, 2);    end    else    begin      Idx := GetGlyphByCharacter(Text^);      if Idx > 0 then      begin        //设置纹理        //m_pSprite.SetTexture(m_Glyphs[Idx].t);        m_pSprite:=m_Glyphs[Idx].t;        //设置纹理范围(应该是按照上面设置的纹理的区域),因为是直接赋值所以这里是不需要的        //m_pSprite.SetTextureRect(0, 0, m_Glyphs[Idx].w, m_Glyphs[Idx].h);        //绘制出来,        //m_pSprite.Render(OffsetX - m_Glyphs[Idx].x, OffsetY - m_Glyphs[Idx].y);        ssprite2d_Draw(m_pSprite,           OffsetX - m_Glyphs[Idx].x,OffsetY - m_Glyphs[Idx].y,           m_Glyphs[Idx].w, m_Glyphs[Idx].h,180,fx2dColor[3],FX_BLEND or FX_COLOR);        OffsetX := OffsetX + GetWidthFromCharacter(Text^) + m_nKerningWidth;      end      else        OffsetX := OffsetX + GetWidthFromCharacter(Text^) + m_nKerningWidth;      Inc(Text);    end;  end;end;procedure TGfxFont.SetColor(Color: Cardinal);varR,G,B,A:Byte;begin  //分解颜色到RGBA分量。  R:=Color shr 24;  G:=Color shr 16;  B:=Color shr 8;  A:=Color and $FF;  //给颜色会混合赋值  fx2dColor[0]:=R;  fx2dColor[1]:=G;  fx2dColor[2]:=B;  fx2dColor[3]:=A;  //m_pSprite.SetColor(Color, i);  //设置顶点颜色,如果I为-1那么就设置四个顶点颜色为colorend;procedure TGfxFont.SetColor(Color: TColor);varR,G,B,A:Byte;begin  //TColor颜色顺序是BGR  B:=Color shr 16;  G:=Color shr 8;  R:=Color and $FF;  A:=$FF;  fx2dColor[0]:=R;  fx2dColor[1]:=G;  fx2dColor[2]:=B;  fx2dColor[3]:=A;end;procedure TGfxFont.SetKerningHeight(Kerning: Single);begin  m_nKerningHeight := Kerning;end;procedure TGfxFont.SetKerningWidth(Kerning: Single);begin  m_nKerningWidth := Kerning;end;function TGfxFont.TextHeight(Text: String): Integer;begin  Result:=GetTextSize(PWideChar(Text)).cy;end;function TGfxFont.TextWidth(Text: String): Integer;begin  Result:=GetTextSize(PWideChar(Text)).cx;end;function RGBA(const R, G, B, A: Byte): Longword; inline;begin  Result := (A shl 24) or (R shl 16) or (G shl 8) or B;end;end.

0 0
原创粉丝点击