游戏开发论坛

 找回密码
 立即注册
搜索
查看: 20784|回复: 30

OpenGL实现中文显示——改进版

[复制链接]

27

主题

418

帖子

455

积分

中级会员

Rank: 3Rank: 3

积分
455
QQ
发表于 2006-8-17 08:46:00 | 显示全部楼层 |阅读模式
      上次我发了个帖子,名为《OpenGL实现中文显示》。有朋友说使用 wglUseFontBitMapsW 函数就可以显示中文,由于考虑兼容问题,所以没有在原文给出实现方法。后来觉得使用 Window9X/Me 的人越来越少,所以决定改进一下原来的代码,以便支持 Unicode 编码。修改后的代码如下:

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Graphics, GL, GLu, GLext;

Type
  PGLFont = ^TGLFont;
  TGLFont = record
    b3D     : Boolean;    //三维字体?
    bBold   : Boolean;    //粗体?
    bItalic : Boolean;    //斜体?
    Height  : Integer;
    Weight  : Integer;
    CharSet : Cardinal;   //字符集
    Typeface: PChar;      //字体
  end;
  TGLText = class
  private
    bUniCode   : Boolean; //OS支持Unicode吗?
    GLStrEng   : PChar;   //英文字串
    GLStrChn   : PChar;   //中文字串

    procedure Build(srcDC: HDC; glStr: PChar; glFont: PGLFont);
    procedure GetWinVer;
    procedure wglUseFontBitmapsExt(srcDC : HDC; First, Count, ListBase : DWORD);
  public
    constructor Create(srcDC : HDC; EngStr, ChnStr : PChar;
                       EngFont, ChnFont : PGLFont);
    destructor Destroy; override;
    procedure glShowStr(glStr : PChar);
  end;


implementation

{ TGLText }
procedure TGLText.Build(srcDC: HDC; glStr: PChar; glFont: PGLFont);
var
  i,
  Chartmp     : Byte;
  CharW       : PWideChar;

  Font, oFont : HFONT;
  glBold      : Integer;
  glItalic    : Cardinal;
  dwChar      : DWORD;
  gmf         : GLYPHMETRICSFLOAT;
begin

  if glFont.bBold then
    glBold := FW_BOLD
  else
    glBold := FW_NORMAL;

  if glFont.bItalic then
    glItalic := 1
  else
    glItalic := 0;

  Font := CreateFont(glFont.Height, glFont.Weight, 0, 0, glBold, glItalic,
                     0, 0, glFont.CharSet, OUT_TT_PRECIS,
                     CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
                     FF_DONTCARE or DEFAULT_PITCH,
                     glFont.Typeface);

  oFont := SelectObject(srcDC, Font);

  if glStr = 'All English' then
  begin
    if bUniCode then
      if glFont.b3D then
        wglUseFontOutLinesW(srcDC, 32, 125, 32, 0, 0, WGL_FONT_POLYGONS, @gmf)
      else
        wglUseFontBitMapsW(srcDC, 32, 125, 32)
    else
      if glFont.b3D then
        wglUseFontOutLines(srcDC, 32, 125, 32, 0, 0, WGL_FONT_POLYGONS, @gmf)
      else
        wglUseFontBitMaps(srcDC, 32, 125, 32);
  end
  else
  begin
    i := 0;
    While i < Length(glStr) do
    begin
      if bUniCode then
      begin
        GetMem(CharW, (Length(glStr) + 1) * 2);
        StringToWideChar(glStr, CharW, Length(glStr) + 1);
        dwChar := Word(CharW);
        if glFont.b3D then
          wglUseFontOutLinesW(srcDC, dwChar, 1, dwChar, 0, 0,
                             WGL_FONT_POLYGONS, @gmf)
        else
          wglUseFontBitMapsW(srcDC, dwChar, 1, dwChar);
        FreeMem(CharW, (Length(glStr) + 1) * 2);
        i := i + 1;
      end
      else
      begin
        Chartmp := Byte(glStr);
        if glStr in LeadBytes  then
        begin
          dwChar := ((Chartmp shl 8) or Byte(glStr[i+1]));
          i := i + 2;
        end
        else
        begin
          dwChar := Chartmp;
          i := i + 1;
        end;
        if glFont.b3D then
          wglUseFontOutLines(srcDC, dwChar, 1, dwChar, 0, 0,
                             WGL_FONT_POLYGONS, @gmf)
        else
          wglUseFontBitMapsExt(srcDC, dwChar, 1, dwChar);
      end;
    end;
  end;

  SelectObject(srcDC, oFont);
  DeleteObject(Font);

end;

constructor TGLText.Create(srcDC : HDC; EngStr, ChnStr: PChar;
                           EngFont, ChnFont: PGLFont);
begin
  GetWinVer;

  GLStrEng := EngStr;
  GLStrChn := ChnStr;

  if EngStr <> nil then
      Build(srcDC, EngStr, EngFont);

  if ChnStr <> nil then
      Build(srcDC, ChnStr, ChnFont);

end;

destructor TGLText.Destroy;
var
  i,
  Chartmp : Byte;
  CharW   : PWideChar;
  dwChar  : DWORD;
begin

  if GLStrEng <> nil then
    if GLStrEng = 'All English' then
    begin
      glDeleteLists(32, 93);
    end
    else
    begin
      i := 0;
      While i < Length(GLStrEng) do
      begin
        dwChar := Byte(GLStrEng);
        glDeleteLists(dwChar, 1);
        i := i + 1;
      end;
    end;

  if GLStrChn <> nil then
  begin
    i := 0;
    While i < Length(GLStrChn) do
    begin
      if bUniCode then
      begin
        GetMem(CharW, (Length(GLStrChn) + 1) * 2);
        StringToWideChar(GLStrChn, CharW, Length(GLStrChn) + 1);
        dwChar := Word(CharW);
        glDeleteLists(dwChar, 1);
        FreeMem(CharW, (Length(GLStrChn) + 1) * 2);
        i := i + 1;
      end
      else
      begin
        Chartmp := Byte(GLStrChn);
        if GLStrChn in LeadBytes then
        begin
          dwChar := ((Chartmp shl 8) or Byte(GLStrChn[i+1]));
          i := i + 2;
        end
        else
        begin
          dwChar := Chartmp;
          i := i + 1;
        end;
        glDeleteLists(dwChar, 1);
      end;
    end;
  end;

inherited Destroy;

end;

procedure TGLText.GetWinVer;
var
  VersionInfo: TOSVersionInfo;
begin
  VersionInfo.dwOSVersionInfoSize := SizeOf(VersionInfo);
  GetVersionEx(VersionInfo);
  if Versioninfo.dwPlatformId < 2 then
    bUniCode := False
  else
    bUniCode := True;
end;

procedure TGLText.glShowStr(glStr : PChar);
var
  i,
  Chartmp     : Byte;
  CharW       : PWideChar;
  dwChar      : DWORD;
begin

  i := 0;
  While i < Length(glStr) do
  begin
    if bUniCode then
    begin
      GetMem(CharW, (Length(glStr) + 1) * 2);
      StringToWideChar(glStr, CharW, Length(glStr) + 1);
      dwChar := Word(CharW);
      FreeMem(CharW, (Length(glStr) + 1) * 2);
      i := i + 1;

      glCallList(dwChar);
    end
    else
    begin
      Chartmp := Byte(glStr);
      if glStr in LeadBytes then
      begin
        dwChar := ((Chartmp shl 8) or Byte(glStr[i+1]));
        i := i + 2;
      end
      else
      begin
        dwChar := Chartmp;
        i := i + 1;
      end;
      glCallList(dwChar);
    end;
  end;

end;

procedure TGLText.wglUseFontBitmapsExt(srcDC: HDC; First, Count,
                                       ListBase: DWORD);
var
  i : DWORD;
  size : DWORD;
  gm : GLYPHMETRICS;
  hBits : THANDLE;
  lpBits : PGLubyte;
  mat : MAT2;
begin

  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;

  for i := 0 to Count - 1 do
  begin
    glNewList(ListBase+i, GL_COMPILE);

      size := GetGlyphOutline(srcDC, First+i, GGO_BITMAP, gm, 0, nil, mat);

      hBits  := GlobalAlloc(GHND, size);
      lpBits := GlobalLock(hBits);

      GetGlyphOutline(srcDC,             //* handle to device context */
                       First+i,          //* character to query */
                       GGO_BITMAP,       //* format of data to return */
                       gm,               //* pointer to structure for metrics */
                       size,             //* size of buffer for data */
                       lpBits,           //* pointer to buffer for data */
                       mat               //* pointer to transformation */
                                         //* matrix structure */
                    );

      glBitmap(gm.gmBlackBoxX,gm.gmBlackBoxY,
               gm.gmptGlyphOrigin.x,
               gm.gmptGlyphOrigin.y,
               gm.gmCellIncX,gm.gmCellIncY,
               lpBits);

      GlobalUnlock(hBits);
      GlobalFree(hBits);

    glEndList;
  end;

end;

end.

这次我把使用的例子也贴上了:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DotWindow, GL, GLu, GLext, Unit2;

type
  TForm1 = class(TDotForm)
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormPaint(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
  private
    { Private declarations }
    fX, fY : Integer;
    MyText  : TGLText;

    procedure BuildLists;
    procedure Clearing;
    procedure DrawInfo;
    procedure InitGLStatus(width, height: GLsizei);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation


{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
fX := 640;
fY := 480;
Self.BorderStyle := bsNone;
Self.Height      := fY;
Self.Width       := fX;
Self.Context.DC  := GetDC(Self.Handle);
ShowCursor(False);
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  Self.Context.QuickPF(32, 0, 24, 0);
  Self.Context.InitGL;
  InitGLStatus(Self.ClientWidth, Self.ClientHeight);
  BuildLists;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  MyText.Free;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
  clearing;
  DrawInfo;
  Self.Context.PageFlip;
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  case Key of
    VK_ESCAPE :
    begin
      Clearing;
      Self.Close;
    end;
  end;
end;

procedure TForm1.Clearing;
begin
        glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
end;

procedure TForm1.InitGLStatus(width, height: GLsizei);
begin
        glClearColor(0, 0, 0, 0.5);
  glMatrixMode(GL_PROJECTION);
    gluOrtho2D(-(fX div 2), fX div 2, -(fY div 2), fY div 2);
  glMatrixMode(GL_MODELVIEW);
end;

procedure TForm1.BuildLists;
var
  StrE, StrC : TGLFont;
begin
//中、英文字体
  StrE.b3D := False;
  StrE.bBold := False;
  StrE.bItalic := False;
  StrE.Height := 13;
  StrE.Weight := 0;
  StrE.CharSet:= ANSI_CHARSET;
  StrE.Typeface := 'Tahoma';

  StrC.b3D := False;
  StrC.bBold := False;
  StrC.bItalic := False;
  StrC.Height := 13;
  StrC.Weight := 0;
  StrC.CharSet:= GB2312_CHARSET;
  StrC.Typeface := 'Tahoma';

  MyText := TGLText.Create(Self.Context.DC,
                           'All English' , '这是中文请按键退出。……',
                           @StrE, @StrC);
end;

procedure TForm1.DrawInfo;
begin
  glLoadIdentity;
  glRasterPos2f(-250, 200);
  MyText.glShowStr('这是中文。');

  glRasterPos2f(-250, 150);
  MyText.glShowStr('These are English characters. ');

  glRasterPos2f(-80, -80);
  MyText.glShowStr('请按 Esc 键退出……');
end;

end.

我用 Delphi 6 以及 Delphi 2006(Delphi 10) 验证通过。
有疑问可以发帖,或者发邮件给我,地址如下:
testerHooK@126.com
演示程序请见附件(含源代码)。

P.S. 演示程序用到了DOT工具,请到www.delphi3d.net下载最新版。




               

sf_200681784614.rar

265.96 KB, 下载次数:

4

主题

76

帖子

78

积分

注册会员

Rank: 2

积分
78
QQ
发表于 2006-8-19 09:13:00 | 显示全部楼层

Re:OpenGL实现中文显示——改进版

多谢分享.
我接触3D领域很少,只知道OPenGL方面游戏编程有个控件GLScene,我有一问题请教,我说的这个控件和你用的这个有什么不同和优缺点,麻烦你解释一下.再次谢谢!

190

主题

1801

帖子

2096

积分

金牌会员

Rank: 6Rank: 6

积分
2096
QQ
发表于 2006-8-19 21:45:00 | 显示全部楼层

Re:OpenGL实现中文显示——改进版

glscene based on opengl

27

主题

418

帖子

455

积分

中级会员

Rank: 3Rank: 3

积分
455
QQ
 楼主| 发表于 2006-8-24 08:58:00 | 显示全部楼层

Re: Re:OpenGL实现中文显示——改进版

wxhui: Re:OpenGL实现中文显示——改进版

多谢分享.
我接触3D领域很少,只知道OPenGL方面游戏编程有个控件GLScene,我有一问题请教,我说的这个控件和你用的这个有什么不同和优缺点,麻烦你解释一下.再次谢谢!

根据我的使用经验, DOT比GLScene好得多。DOT支持目前几乎所有OpenGL特性,而且可以随时改进,使用起来也很方便。
[em16] 注意:DOT不是控件,而是开放源代码的函数(类)库。

59

主题

1104

帖子

1199

积分

金牌会员

Rank: 6Rank: 6

积分
1199
发表于 2006-8-24 09:27:00 | 显示全部楼层

Re:OpenGL实现中文显示——改进版

自己读字库生成贴图贴吧,这是最常用的方法

27

主题

418

帖子

455

积分

中级会员

Rank: 3Rank: 3

积分
455
QQ
 楼主| 发表于 2006-8-24 10:41:00 | 显示全部楼层

Re: Re:OpenGL实现中文显示——改进版

tarkey: Re:OpenGL实现中文显示——改进版

自己读字库生成贴图贴吧,这是最常用的方法


请你试试制作五十个中文文字的贴图,然后调用它们。
[em18]

你应该知道什么叫 烦 了吧!

使用我的方法调用 200~500 个不重复文字应该没问题。
[em21]

59

主题

1104

帖子

1199

积分

金牌会员

Rank: 6Rank: 6

积分
1199
发表于 2006-8-24 11:12:00 | 显示全部楼层

Re:OpenGL实现中文显示——改进版

这有什么烦的, 做个索引就好了,实际应用的时候,大部分text中的字都是重复的,用贴图是效率最高的。

27

主题

418

帖子

455

积分

中级会员

Rank: 3Rank: 3

积分
455
QQ
 楼主| 发表于 2006-8-24 12:34:00 | 显示全部楼层

Re:OpenGL实现中文显示——改进版

To tarkey:
你使用的索引是如何编号的?连续的数字?用数字调用?

我的方法是直接用要显示的字串调用。如下:

MyText.glShowStr('这是中文。');

59

主题

1104

帖子

1199

积分

金牌会员

Rank: 6Rank: 6

积分
1199
发表于 2006-8-24 13:13:00 | 显示全部楼层

Re:OpenGL实现中文显示——改进版

std::map<char, CharIndex> m_SingleCharMap;
std::map<string, CharIndex> m_ChineseCharMap;

89

主题

4036

帖子

4132

积分

论坛元老

Rank: 8Rank: 8

积分
4132
发表于 2006-8-24 15:30:00 | 显示全部楼层

Re:OpenGL实现中文显示——改进版

读truetype字体。读完后生成一个 std::map。
std::map<wchar_t, ITexture*> 。

要画哪个就就去map里找。 如果觉得浪费内存。把map用资源管理的策略管理起来。我目前就是这样做的。代码也没怎么乱和复杂。非常干净直接
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-8-7 04:49

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表