integration of font and text system
This commit is contained in:
parent
d8bd7419f9
commit
7c7612a971
169
3rdparty/freetype/FTL.TXT
vendored
Normal file
169
3rdparty/freetype/FTL.TXT
vendored
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
The FreeType Project LICENSE
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
2006-Jan-27
|
||||||
|
|
||||||
|
Copyright 1996-2002, 2006 by
|
||||||
|
David Turner, Robert Wilhelm, and Werner Lemberg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
The FreeType Project is distributed in several archive packages;
|
||||||
|
some of them may contain, in addition to the FreeType font engine,
|
||||||
|
various tools and contributions which rely on, or relate to, the
|
||||||
|
FreeType Project.
|
||||||
|
|
||||||
|
This license applies to all files found in such packages, and
|
||||||
|
which do not fall under their own explicit license. The license
|
||||||
|
affects thus the FreeType font engine, the test programs,
|
||||||
|
documentation and makefiles, at the very least.
|
||||||
|
|
||||||
|
This license was inspired by the BSD, Artistic, and IJG
|
||||||
|
(Independent JPEG Group) licenses, which all encourage inclusion
|
||||||
|
and use of free software in commercial and freeware products
|
||||||
|
alike. As a consequence, its main points are that:
|
||||||
|
|
||||||
|
o We don't promise that this software works. However, we will be
|
||||||
|
interested in any kind of bug reports. (`as is' distribution)
|
||||||
|
|
||||||
|
o You can use this software for whatever you want, in parts or
|
||||||
|
full form, without having to pay us. (`royalty-free' usage)
|
||||||
|
|
||||||
|
o You may not pretend that you wrote this software. If you use
|
||||||
|
it, or only parts of it, in a program, you must acknowledge
|
||||||
|
somewhere in your documentation that you have used the
|
||||||
|
FreeType code. (`credits')
|
||||||
|
|
||||||
|
We specifically permit and encourage the inclusion of this
|
||||||
|
software, with or without modifications, in commercial products.
|
||||||
|
We disclaim all warranties covering The FreeType Project and
|
||||||
|
assume no liability related to The FreeType Project.
|
||||||
|
|
||||||
|
|
||||||
|
Finally, many people asked us for a preferred form for a
|
||||||
|
credit/disclaimer to use in compliance with this license. We thus
|
||||||
|
encourage you to use the following text:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Portions of this software are copyright © <year> The FreeType
|
||||||
|
Project (www.freetype.org). All rights reserved.
|
||||||
|
"""
|
||||||
|
|
||||||
|
Please replace <year> with the value from the FreeType version you
|
||||||
|
actually use.
|
||||||
|
|
||||||
|
|
||||||
|
Legal Terms
|
||||||
|
===========
|
||||||
|
|
||||||
|
0. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Throughout this license, the terms `package', `FreeType Project',
|
||||||
|
and `FreeType archive' refer to the set of files originally
|
||||||
|
distributed by the authors (David Turner, Robert Wilhelm, and
|
||||||
|
Werner Lemberg) as the `FreeType Project', be they named as alpha,
|
||||||
|
beta or final release.
|
||||||
|
|
||||||
|
`You' refers to the licensee, or person using the project, where
|
||||||
|
`using' is a generic term including compiling the project's source
|
||||||
|
code as well as linking it to form a `program' or `executable'.
|
||||||
|
This program is referred to as `a program using the FreeType
|
||||||
|
engine'.
|
||||||
|
|
||||||
|
This license applies to all files distributed in the original
|
||||||
|
FreeType Project, including all source code, binaries and
|
||||||
|
documentation, unless otherwise stated in the file in its
|
||||||
|
original, unmodified form as distributed in the original archive.
|
||||||
|
If you are unsure whether or not a particular file is covered by
|
||||||
|
this license, you must contact us to verify this.
|
||||||
|
|
||||||
|
The FreeType Project is copyright (C) 1996-2000 by David Turner,
|
||||||
|
Robert Wilhelm, and Werner Lemberg. All rights reserved except as
|
||||||
|
specified below.
|
||||||
|
|
||||||
|
1. No Warranty
|
||||||
|
--------------
|
||||||
|
|
||||||
|
THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO
|
||||||
|
USE, OF THE FREETYPE PROJECT.
|
||||||
|
|
||||||
|
2. Redistribution
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
This license grants a worldwide, royalty-free, perpetual and
|
||||||
|
irrevocable right and license to use, execute, perform, compile,
|
||||||
|
display, copy, create derivative works of, distribute and
|
||||||
|
sublicense the FreeType Project (in both source and object code
|
||||||
|
forms) and derivative works thereof for any purpose; and to
|
||||||
|
authorize others to exercise some or all of the rights granted
|
||||||
|
herein, subject to the following conditions:
|
||||||
|
|
||||||
|
o Redistribution of source code must retain this license file
|
||||||
|
(`FTL.TXT') unaltered; any additions, deletions or changes to
|
||||||
|
the original files must be clearly indicated in accompanying
|
||||||
|
documentation. The copyright notices of the unaltered,
|
||||||
|
original files must be preserved in all copies of source
|
||||||
|
files.
|
||||||
|
|
||||||
|
o Redistribution in binary form must provide a disclaimer that
|
||||||
|
states that the software is based in part of the work of the
|
||||||
|
FreeType Team, in the distribution documentation. We also
|
||||||
|
encourage you to put an URL to the FreeType web page in your
|
||||||
|
documentation, though this isn't mandatory.
|
||||||
|
|
||||||
|
These conditions apply to any software derived from or based on
|
||||||
|
the FreeType Project, not just the unmodified files. If you use
|
||||||
|
our work, you must acknowledge us. However, no fee need be paid
|
||||||
|
to us.
|
||||||
|
|
||||||
|
3. Advertising
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Neither the FreeType authors and contributors nor you shall use
|
||||||
|
the name of the other for commercial, advertising, or promotional
|
||||||
|
purposes without specific prior written permission.
|
||||||
|
|
||||||
|
We suggest, but do not require, that you use one or more of the
|
||||||
|
following phrases to refer to this software in your documentation
|
||||||
|
or advertising materials: `FreeType Project', `FreeType Engine',
|
||||||
|
`FreeType library', or `FreeType Distribution'.
|
||||||
|
|
||||||
|
As you have not signed this license, you are not required to
|
||||||
|
accept it. However, as the FreeType Project is copyrighted
|
||||||
|
material, only this license, or another one contracted with the
|
||||||
|
authors, grants you the right to use, distribute, and modify it.
|
||||||
|
Therefore, by using, distributing, or modifying the FreeType
|
||||||
|
Project, you indicate that you understand and accept all the terms
|
||||||
|
of this license.
|
||||||
|
|
||||||
|
4. Contacts
|
||||||
|
-----------
|
||||||
|
|
||||||
|
There are two mailing lists related to FreeType:
|
||||||
|
|
||||||
|
o freetype@nongnu.org
|
||||||
|
|
||||||
|
Discusses general use and applications of FreeType, as well as
|
||||||
|
future and wanted additions to the library and distribution.
|
||||||
|
If you are looking for support, start in this list if you
|
||||||
|
haven't found anything to help you in the documentation.
|
||||||
|
|
||||||
|
o freetype-devel@nongnu.org
|
||||||
|
|
||||||
|
Discusses bugs, as well as engine internals, design issues,
|
||||||
|
specific licenses, porting, etc.
|
||||||
|
|
||||||
|
Our home page can be found at
|
||||||
|
|
||||||
|
http://www.freetype.org
|
||||||
|
|
||||||
|
|
||||||
|
--- end of FTL.TXT ---
|
5
3rdparty/freetype/README.md
vendored
Normal file
5
3rdparty/freetype/README.md
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
The Freetype code is copyright 2013 The FreeType Project.
|
||||||
|
All rights reserved. (www.freetype.org).
|
||||||
|
Distributed under the FreeType License: see FTL.TXT.
|
||||||
|
|
||||||
|
freetype.h is an amalagmation of Freetype 2.4.11 made by Jeremie Roy in march 2013.
|
117420
3rdparty/freetype/freetype.h
vendored
Normal file
117420
3rdparty/freetype/freetype.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
207
examples/10-font_alpha/basics.cpp
Normal file
207
examples/10-font_alpha/basics.cpp
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
#include <bgfx.h>
|
||||||
|
#include <bx/bx.h>
|
||||||
|
#include <bx/timer.h>
|
||||||
|
#include "../common/entry.h"
|
||||||
|
#include "../common/dbg.h"
|
||||||
|
#include "../common/math.h"
|
||||||
|
#include "../common/processevents.h"
|
||||||
|
|
||||||
|
#include "../common/font/font_manager.h"
|
||||||
|
#include "../common/font/text_buffer_manager.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static const char* s_shaderPath = NULL;
|
||||||
|
|
||||||
|
int _main_(int /*_argc*/, char** /*_argv*/)
|
||||||
|
{
|
||||||
|
uint32_t width = 1280;
|
||||||
|
uint32_t height = 720;
|
||||||
|
uint32_t debug = BGFX_DEBUG_TEXT;
|
||||||
|
uint32_t reset = 0;
|
||||||
|
|
||||||
|
bgfx::init();
|
||||||
|
|
||||||
|
|
||||||
|
bgfx::reset(width, height);
|
||||||
|
|
||||||
|
// Enable debug text.
|
||||||
|
bgfx::setDebug(debug);
|
||||||
|
|
||||||
|
// Set view 0 clear state.
|
||||||
|
bgfx::setViewClear(0
|
||||||
|
, BGFX_CLEAR_COLOR_BIT|BGFX_CLEAR_DEPTH_BIT
|
||||||
|
, 0x303030ff
|
||||||
|
, 1.0f
|
||||||
|
, 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setup root path for binary shaders. Shader binaries are different
|
||||||
|
// for each renderer.
|
||||||
|
switch (bgfx::getRendererType() )
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case bgfx::RendererType::Direct3D9:
|
||||||
|
s_shaderPath = "shaders/dx9/";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case bgfx::RendererType::Direct3D11:
|
||||||
|
s_shaderPath = "shaders/dx11/";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case bgfx::RendererType::OpenGL:
|
||||||
|
s_shaderPath = "shaders/glsl/";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case bgfx::RendererType::OpenGLES2:
|
||||||
|
case bgfx::RendererType::OpenGLES3:
|
||||||
|
s_shaderPath = "shaders/gles/";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//init the text rendering system
|
||||||
|
bgfx_font::FontManager* fontManager = new bgfx_font::FontManager(512);
|
||||||
|
bgfx_font::TextBufferManager* textBufferManager = new bgfx_font::TextBufferManager(fontManager);
|
||||||
|
textBufferManager->init(s_shaderPath);
|
||||||
|
|
||||||
|
|
||||||
|
//load some truetype files
|
||||||
|
bgfx_font::TrueTypeHandle times_tt = fontManager->loadTrueTypeFromFile("c:/windows/fonts/times.ttf");
|
||||||
|
bgfx_font::TrueTypeHandle consola_tt = fontManager->loadTrueTypeFromFile("c:/windows/fonts/consola.ttf");
|
||||||
|
|
||||||
|
//create some usable font with of a specific size
|
||||||
|
bgfx_font::FontHandle times_24 = fontManager->createFontByPixelSize(times_tt, 0, 24);
|
||||||
|
|
||||||
|
//preload glyphs and blit them to atlas
|
||||||
|
fontManager->preloadGlyph(times_24, L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ. \n");
|
||||||
|
|
||||||
|
//You can unload the truetype files at this stage, but in that case, the set of glyph's will be limited to the set of preloaded glyph
|
||||||
|
fontManager->unloadTrueType(times_tt);
|
||||||
|
|
||||||
|
//this font doesn't have any preloaded glyph's but the truetype file is loaded
|
||||||
|
//so glyph will be generated as needed
|
||||||
|
bgfx_font::FontHandle consola_16 = fontManager->createFontByPixelSize(consola_tt, 0, 16);
|
||||||
|
|
||||||
|
//create a static text buffer compatible with alpha font
|
||||||
|
//a static text buffer content cannot be modified after its first submit.
|
||||||
|
bgfx_font::TextBufferHandle staticText = textBufferManager->createTextBuffer(bgfx_font::FONT_TYPE_ALPHA, bgfx_font::STATIC);
|
||||||
|
|
||||||
|
//the pen position represent the top left of the box of the first line of text
|
||||||
|
textBufferManager->setPenPosition(staticText, 20.0f, 100.0f);
|
||||||
|
|
||||||
|
//add some text to the buffer
|
||||||
|
textBufferManager->appendText(staticText, times_24, L"The quick brown fox jumps over the lazy dog\n");
|
||||||
|
//the position of the pen is adjusted when there is an endline
|
||||||
|
|
||||||
|
//setup style colors
|
||||||
|
textBufferManager->setBackgroundColor(staticText, 0x551111FF);
|
||||||
|
textBufferManager->setUnderlineColor(staticText, 0xFF2222FF);
|
||||||
|
textBufferManager->setOverlineColor(staticText, 0x2222FFFF);
|
||||||
|
textBufferManager->setStrikeThroughColor(staticText, 0x22FF22FF);
|
||||||
|
|
||||||
|
|
||||||
|
//text + bkg
|
||||||
|
textBufferManager->setStyle(staticText, bgfx_font::STYLE_BACKGROUND);
|
||||||
|
textBufferManager->appendText(staticText, times_24, L"The quick brown fox jumps over the lazy dog\n");
|
||||||
|
|
||||||
|
//text + strike-through
|
||||||
|
textBufferManager->setStyle(staticText, bgfx_font::STYLE_STRIKE_THROUGH);
|
||||||
|
textBufferManager->appendText(staticText, times_24, L"The quick brown fox jumps over the lazy dog\n");
|
||||||
|
|
||||||
|
//text + overline
|
||||||
|
textBufferManager->setStyle(staticText, bgfx_font::STYLE_OVERLINE);
|
||||||
|
textBufferManager->appendText(staticText, times_24, L"The quick brown fox jumps over the lazy dog\n");
|
||||||
|
|
||||||
|
//text + underline
|
||||||
|
textBufferManager->setStyle(staticText, bgfx_font::STYLE_UNDERLINE);
|
||||||
|
textBufferManager->appendText(staticText, times_24, L"The quick brown fox jumps over the lazy dog\n");
|
||||||
|
|
||||||
|
|
||||||
|
//text + bkg + strike-through
|
||||||
|
textBufferManager->setStyle(staticText, bgfx_font::STYLE_BACKGROUND|bgfx_font::STYLE_STRIKE_THROUGH);
|
||||||
|
textBufferManager->appendText(staticText, times_24, L"The quick brown fox jumps over the lazy dog\n");
|
||||||
|
|
||||||
|
//create a transient buffer for realtime data
|
||||||
|
bgfx_font::TextBufferHandle transientText = textBufferManager->createTextBuffer(bgfx_font::FONT_TYPE_ALPHA, bgfx_font::TRANSIENT);
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t w = 0,h = 0;
|
||||||
|
while (!processEvents(width, height, debug, reset) )
|
||||||
|
{
|
||||||
|
|
||||||
|
if(w!=width|| h!=height)
|
||||||
|
{
|
||||||
|
w=width;
|
||||||
|
h= height;
|
||||||
|
printf("ri: %d,%d\n",width,height);
|
||||||
|
}
|
||||||
|
// Set view 0 default viewport.
|
||||||
|
bgfx::setViewRect(0, 0, 0, width, height);
|
||||||
|
|
||||||
|
// This dummy draw call is here to make sure that view 0 is cleared
|
||||||
|
// if no other draw calls are submitted to view 0.
|
||||||
|
bgfx::submit(0);
|
||||||
|
|
||||||
|
int64_t now = bx::getHPCounter();
|
||||||
|
static int64_t last = now;
|
||||||
|
const int64_t frameTime = now - last;
|
||||||
|
last = now;
|
||||||
|
|
||||||
|
const double freq = double(bx::getHPFrequency() );
|
||||||
|
const double toMs = 1000.0/freq;
|
||||||
|
//float time = (float)(bx::getHPCounter()/double(bx::getHPFrequency() ) );
|
||||||
|
|
||||||
|
float at[3] = { 0, 0, 0.0f };
|
||||||
|
float eye[3] = {0, 0, -1.0f };
|
||||||
|
|
||||||
|
float view[16];
|
||||||
|
float proj[16];
|
||||||
|
mtxLookAt(view, eye, at);
|
||||||
|
//setup a top-left ortho matrix for screen space drawing
|
||||||
|
float centering = 0.5f;
|
||||||
|
mtxOrtho(proj, centering, width+centering,height+centering, centering,-1.0f, 1.0f);
|
||||||
|
|
||||||
|
// Set view and projection matrix for view 0.
|
||||||
|
bgfx::setViewTransform(0, view, proj);
|
||||||
|
|
||||||
|
//submit the static text
|
||||||
|
textBufferManager->submitTextBuffer(staticText, 0);
|
||||||
|
|
||||||
|
|
||||||
|
//submit some realtime text
|
||||||
|
wchar_t fpsText[64];
|
||||||
|
swprintf(fpsText,L"Frame: % 7.3f[ms]", double(frameTime)*toMs);
|
||||||
|
|
||||||
|
textBufferManager->clearTextBuffer(transientText);
|
||||||
|
textBufferManager->setPenPosition(transientText, 20.0, 4.0f);
|
||||||
|
|
||||||
|
textBufferManager->appendText(transientText, consola_16, L"bgfx_font\\sample\\01_basics\n");
|
||||||
|
textBufferManager->appendText(transientText, consola_16, L"Description: truetype, font, text and style\n");
|
||||||
|
textBufferManager->appendText(transientText, consola_16, fpsText);
|
||||||
|
|
||||||
|
textBufferManager->submitTextBuffer(transientText, 0);
|
||||||
|
|
||||||
|
// Advance to next frame. Rendering thread will be kicked to
|
||||||
|
// process submitted rendering primitives.
|
||||||
|
bgfx::frame();
|
||||||
|
//just to prevent my CG Fan to howl
|
||||||
|
Sleep(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fontManager->unloadTrueType(consola_tt);
|
||||||
|
fontManager->destroyFont(consola_16);
|
||||||
|
fontManager->destroyFont(times_24);
|
||||||
|
|
||||||
|
textBufferManager->destroyTextBuffer(staticText);
|
||||||
|
textBufferManager->destroyTextBuffer(transientText);
|
||||||
|
|
||||||
|
delete textBufferManager;
|
||||||
|
delete fontManager;
|
||||||
|
|
||||||
|
// Shutdown bgfx.
|
||||||
|
bgfx::shutdown();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
160
examples/11-font_distance_field/distance_field_text.cpp
Normal file
160
examples/11-font_distance_field/distance_field_text.cpp
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#include <bgfx.h>
|
||||||
|
#include <bx/bx.h>
|
||||||
|
#include <bx/timer.h>
|
||||||
|
#include "../common/entry.h"
|
||||||
|
#include "../common/dbg.h"
|
||||||
|
#include "../common/math.h"
|
||||||
|
#include "../common/processevents.h"
|
||||||
|
|
||||||
|
#include "../common/font/font_manager.h"
|
||||||
|
#include "../common/font/text_buffer_manager.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static const char* s_shaderPath = NULL;
|
||||||
|
|
||||||
|
int _main_(int /*_argc*/, char** /*_argv*/)
|
||||||
|
{
|
||||||
|
uint32_t width = 1280;
|
||||||
|
uint32_t height = 720;
|
||||||
|
uint32_t debug = BGFX_DEBUG_TEXT;
|
||||||
|
uint32_t reset = 0;
|
||||||
|
|
||||||
|
bgfx::init();
|
||||||
|
|
||||||
|
bgfx::reset(width, height);
|
||||||
|
|
||||||
|
// Enable debug text.
|
||||||
|
bgfx::setDebug(debug);
|
||||||
|
|
||||||
|
// Set view 0 clear state.
|
||||||
|
bgfx::setViewClear(0
|
||||||
|
, BGFX_CLEAR_COLOR_BIT|BGFX_CLEAR_DEPTH_BIT
|
||||||
|
//, 0x303030ff
|
||||||
|
//, 0xffffffff
|
||||||
|
, 0x000000FF
|
||||||
|
, 1.0f
|
||||||
|
, 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setup root path for binary shaders. Shader binaries are different
|
||||||
|
// for each renderer.
|
||||||
|
switch (bgfx::getRendererType() )
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case bgfx::RendererType::Direct3D9:
|
||||||
|
s_shaderPath = "shaders/dx9/";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case bgfx::RendererType::Direct3D11:
|
||||||
|
s_shaderPath = "shaders/dx11/";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case bgfx::RendererType::OpenGL:
|
||||||
|
s_shaderPath = "shaders/glsl/";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case bgfx::RendererType::OpenGLES2:
|
||||||
|
case bgfx::RendererType::OpenGLES3:
|
||||||
|
s_shaderPath = "shaders/gles/";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//init the text rendering system
|
||||||
|
bgfx_font::FontManager* fontManager = new bgfx_font::FontManager(512);
|
||||||
|
bgfx_font::TextBufferManager* textBufferManager = new bgfx_font::TextBufferManager(fontManager);
|
||||||
|
textBufferManager->init(s_shaderPath);
|
||||||
|
|
||||||
|
//load a truetype files
|
||||||
|
bgfx_font::TrueTypeHandle times_tt = fontManager->loadTrueTypeFromFile("c:/windows/fonts/times.ttf");
|
||||||
|
bgfx_font::FontHandle distance_font = fontManager->createFontByPixelSize(times_tt, 0, 48, bgfx_font::FONT_TYPE_DISTANCE);
|
||||||
|
//preload glyph and generate (generate bitmap's)
|
||||||
|
fontManager->preloadGlyph(distance_font, L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ. \n");
|
||||||
|
|
||||||
|
uint32_t fontsCount = 0;
|
||||||
|
bgfx_font::FontHandle fonts[64];
|
||||||
|
fonts[fontsCount++] = distance_font;
|
||||||
|
//generate various sub distance field fonts at various size
|
||||||
|
int step=4;
|
||||||
|
for(int i = 64; i>1 ; i-=step)
|
||||||
|
{
|
||||||
|
if(i<32) step = 2;
|
||||||
|
//instantiate a usable font
|
||||||
|
bgfx_font::FontHandle font = fontManager->createScaledFontToPixelSize(distance_font, i);
|
||||||
|
fonts[fontsCount++] = font;
|
||||||
|
}
|
||||||
|
//You can unload the truetype files at this stage, but in that case, the set of glyph's will be limited to the set of preloaded glyph
|
||||||
|
fontManager->unloadTrueType(times_tt);
|
||||||
|
|
||||||
|
bgfx_font::TextBufferHandle staticText = textBufferManager->createTextBuffer(bgfx_font::FONT_TYPE_DISTANCE, bgfx_font::STATIC);
|
||||||
|
|
||||||
|
textBufferManager->setPenPosition(staticText, 10.0f, 10.0f);
|
||||||
|
textBufferManager->setTextColor(staticText, 0xFFFFFFFF);
|
||||||
|
//textBufferManager->setTextColor(staticText, 0x000000FF);
|
||||||
|
for(size_t i = 0; i< fontsCount; ++i)
|
||||||
|
{
|
||||||
|
textBufferManager->appendText(staticText, fonts[i], L"The quick brown fox jumps over the lazy dog\n");
|
||||||
|
//textBufferManager->appendText(staticText, fonts[i], L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!processEvents(width, height, debug, reset) )
|
||||||
|
{
|
||||||
|
// Set view 0 default viewport.
|
||||||
|
bgfx::setViewRect(0, 0, 0, width, height);
|
||||||
|
|
||||||
|
// This dummy draw call is here to make sure that view 0 is cleared
|
||||||
|
// if no other draw calls are submitted to view 0.
|
||||||
|
bgfx::submit(0);
|
||||||
|
|
||||||
|
//int64_t now = bx::getHPCounter();
|
||||||
|
//static int64_t last = now;
|
||||||
|
//const int64_t frameTime = now - last;
|
||||||
|
//last = now;
|
||||||
|
//const double freq = double(bx::getHPFrequency() );
|
||||||
|
//const double toMs = 1000.0/freq;
|
||||||
|
//float time = (float)(bx::getHPCounter()/double(bx::getHPFrequency() ) );
|
||||||
|
|
||||||
|
// Use debug font to print information about this example.
|
||||||
|
bgfx::dbgTextClear();
|
||||||
|
//bgfx::dbgTextPrintf(0, 1, 0x4f, "bgfx/examples/00-helloworld");
|
||||||
|
//bgfx::dbgTextPrintf(0, 2, 0x6f, "Description: Initialization and debug text.");
|
||||||
|
//bgfx::dbgTextPrintf(0, 3, 0x0f, "Frame: % 7.3f[ms]", double(frameTime)*toMs);
|
||||||
|
|
||||||
|
float at[3] = { 0, 0, 0.0f };
|
||||||
|
float eye[3] = {0, 0, -1.0f };
|
||||||
|
|
||||||
|
float view[16];
|
||||||
|
float proj[16];
|
||||||
|
mtxLookAt(view, eye, at);
|
||||||
|
float centering = 0.5f;
|
||||||
|
//setup a top-left ortho matrix for screen space drawing
|
||||||
|
mtxOrtho(proj, centering, width+centering,height+centering, centering,-1.0f, 1.0f);
|
||||||
|
|
||||||
|
// Set view and projection matrix for view 0.
|
||||||
|
bgfx::setViewTransform(0, view, proj);
|
||||||
|
|
||||||
|
//draw your text
|
||||||
|
textBufferManager->submitTextBuffer(staticText, 0);
|
||||||
|
|
||||||
|
// Advance to next frame. Rendering thread will be kicked to
|
||||||
|
// process submitted rendering primitives.
|
||||||
|
bgfx::frame();
|
||||||
|
//just to prevent my CG Fan to howl
|
||||||
|
Sleep(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//destroy the fonts
|
||||||
|
for(size_t i=0; i<fontsCount;++i)
|
||||||
|
{
|
||||||
|
fontManager->destroyFont(fonts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
textBufferManager->destroyTextBuffer(staticText);
|
||||||
|
delete textBufferManager;
|
||||||
|
delete fontManager;
|
||||||
|
// Shutdown bgfx.
|
||||||
|
bgfx::shutdown();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
483
examples/common/cube_atlas.cpp
Normal file
483
examples/common/cube_atlas.cpp
Normal file
@ -0,0 +1,483 @@
|
|||||||
|
/* Copyright 2013 Jeremie Roy. All rights reserved.
|
||||||
|
* License: http://www.opensource.org/licenses/BSD-2-Clause
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <bgfx.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <vector>
|
||||||
|
#include "cube_atlas.h"
|
||||||
|
|
||||||
|
namespace bgfx
|
||||||
|
{
|
||||||
|
|
||||||
|
//********** Rectangle packer implementation ************
|
||||||
|
class RectanglePacker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RectanglePacker();
|
||||||
|
RectanglePacker(uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
/// non constructor initialization
|
||||||
|
void init(uint32_t width, uint32_t height);
|
||||||
|
/// find a suitable position for the given rectangle
|
||||||
|
/// @return true if the rectangle can be added, false otherwise
|
||||||
|
bool addRectangle(uint16_t width, uint16_t height, uint16_t& outX, uint16_t& outY );
|
||||||
|
/// return the used surface in squared unit
|
||||||
|
uint32_t getUsedSurface() { return m_usedSpace; }
|
||||||
|
/// return the total available surface in squared unit
|
||||||
|
uint32_t getTotalSurface() { return m_width*m_height; }
|
||||||
|
/// return the usage ratio of the available surface [0:1]
|
||||||
|
float getUsageRatio();
|
||||||
|
/// reset to initial state
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int32_t fit(uint32_t skylineNodeIndex, uint16_t width, uint16_t height);
|
||||||
|
/// Merges all skyline nodes that are at the same level.
|
||||||
|
void merge();
|
||||||
|
|
||||||
|
struct Node
|
||||||
|
{
|
||||||
|
Node(int16_t _x, int16_t _y, int16_t _width):x(_x), y(_y), width(_width) {}
|
||||||
|
|
||||||
|
/// The starting x-coordinate (leftmost).
|
||||||
|
int16_t x;
|
||||||
|
/// The y-coordinate of the skyline level line.
|
||||||
|
int16_t y;
|
||||||
|
/// The line width. The ending coordinate (inclusive) will be x+width-1.
|
||||||
|
int32_t width; //32bit to avoid padding
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Width (in pixels) of the underlying texture
|
||||||
|
uint32_t m_width;
|
||||||
|
/// Height (in pixels) of the underlying texture
|
||||||
|
uint32_t m_height;
|
||||||
|
/// Surface used in squared pixel
|
||||||
|
uint32_t m_usedSpace;
|
||||||
|
/// node of the skyline algorithm
|
||||||
|
std::vector<Node> m_skyline;
|
||||||
|
};
|
||||||
|
|
||||||
|
RectanglePacker::RectanglePacker(): m_width(0), m_height(0), m_usedSpace(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RectanglePacker::RectanglePacker(uint32_t width, uint32_t height):m_width(width), m_height(height), m_usedSpace(0)
|
||||||
|
{
|
||||||
|
// We want a one pixel border around the whole atlas to avoid any artefact when
|
||||||
|
// sampling texture
|
||||||
|
m_skyline.push_back(Node(1,1, width-2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RectanglePacker::init(uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
assert(width > 2);
|
||||||
|
assert(height > 2);
|
||||||
|
m_width = width;
|
||||||
|
m_height = height;
|
||||||
|
m_usedSpace = 0;
|
||||||
|
|
||||||
|
m_skyline.clear();
|
||||||
|
// We want a one pixel border around the whole atlas to avoid any artifact when
|
||||||
|
// sampling texture
|
||||||
|
m_skyline.push_back(Node(1,1, width-2));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RectanglePacker::addRectangle(uint16_t width, uint16_t height, uint16_t& outX, uint16_t& outY)
|
||||||
|
{
|
||||||
|
int y, best_height, best_index;
|
||||||
|
int32_t best_width;
|
||||||
|
Node* node;
|
||||||
|
Node* prev;
|
||||||
|
outX = 0;
|
||||||
|
outY = 0;
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
best_height = INT_MAX;
|
||||||
|
best_index = -1;
|
||||||
|
best_width = INT_MAX;
|
||||||
|
for( i = 0; i < m_skyline.size(); ++i )
|
||||||
|
{
|
||||||
|
y = fit( i, width, height );
|
||||||
|
if( y >= 0 )
|
||||||
|
{
|
||||||
|
node = &m_skyline[i];
|
||||||
|
if( ( (y + height) < best_height ) ||
|
||||||
|
( ((y + height) == best_height) && (node->width < best_width)) )
|
||||||
|
{
|
||||||
|
best_height = y + height;
|
||||||
|
best_index = i;
|
||||||
|
best_width = node->width;
|
||||||
|
outX = node->x;
|
||||||
|
outY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( best_index == -1 )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node newNode(outX,outY + height, width);
|
||||||
|
m_skyline.insert(m_skyline.begin() + best_index, newNode);
|
||||||
|
|
||||||
|
for(i = best_index+1; i < m_skyline.size(); ++i)
|
||||||
|
{
|
||||||
|
node = &m_skyline[i];
|
||||||
|
prev = &m_skyline[i-1];
|
||||||
|
if (node->x < (prev->x + prev->width) )
|
||||||
|
{
|
||||||
|
int shrink = prev->x + prev->width - node->x;
|
||||||
|
node->x += shrink;
|
||||||
|
node->width -= shrink;
|
||||||
|
if (node->width <= 0)
|
||||||
|
{
|
||||||
|
m_skyline.erase(m_skyline.begin() + i);
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
merge();
|
||||||
|
m_usedSpace += width * height;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float RectanglePacker::getUsageRatio()
|
||||||
|
{
|
||||||
|
uint32_t total = m_width*m_height;
|
||||||
|
if(total > 0)
|
||||||
|
return (float) m_usedSpace / (float) total;
|
||||||
|
else
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RectanglePacker::clear()
|
||||||
|
{
|
||||||
|
m_skyline.clear();
|
||||||
|
m_usedSpace = 0;
|
||||||
|
|
||||||
|
// We want a one pixel border around the whole atlas to avoid any artefact when
|
||||||
|
// sampling texture
|
||||||
|
m_skyline.push_back(Node(1,1, m_width-2));
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t RectanglePacker::fit(uint32_t skylineNodeIndex, uint16_t _width, uint16_t _height)
|
||||||
|
{
|
||||||
|
int32_t width = _width;
|
||||||
|
int32_t height = _height;
|
||||||
|
|
||||||
|
const Node& baseNode = m_skyline[skylineNodeIndex];
|
||||||
|
|
||||||
|
int32_t x = baseNode.x, y;
|
||||||
|
int32_t width_left = width;
|
||||||
|
int32_t i = skylineNodeIndex;
|
||||||
|
|
||||||
|
if ( (x + width) > (int32_t)(m_width-1) )
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
y = baseNode.y;
|
||||||
|
while( width_left > 0 )
|
||||||
|
{
|
||||||
|
const Node& node = m_skyline[i];
|
||||||
|
if( node.y > y )
|
||||||
|
{
|
||||||
|
y = node.y;
|
||||||
|
}
|
||||||
|
if( (y + height) > (int32_t)(m_height-1) )
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
width_left -= node.width;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RectanglePacker::merge()
|
||||||
|
{
|
||||||
|
Node* node;
|
||||||
|
Node* next;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for( i=0; i < m_skyline.size()-1; ++i )
|
||||||
|
{
|
||||||
|
node = (Node *) &m_skyline[i];
|
||||||
|
next = (Node *) &m_skyline[i+1];
|
||||||
|
if( node->y == next->y )
|
||||||
|
{
|
||||||
|
node->width += next->width;
|
||||||
|
m_skyline.erase(m_skyline.begin() + i + 1);
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//********** Cube Atlas implementation ************
|
||||||
|
|
||||||
|
struct Atlas::PackedLayer
|
||||||
|
{
|
||||||
|
RectanglePacker packer;
|
||||||
|
AtlasRegion faceRegion;
|
||||||
|
};
|
||||||
|
|
||||||
|
Atlas::Atlas(uint16_t textureSize, uint16_t maxRegionsCount )
|
||||||
|
{
|
||||||
|
assert(textureSize >= 64 && textureSize <= 4096 && "suspicious texture size" );
|
||||||
|
assert(maxRegionsCount >= 64 && maxRegionsCount <= 32000 && "suspicious regions count" );
|
||||||
|
m_layers = new PackedLayer[24];
|
||||||
|
for(int i=0; i<24;++i)
|
||||||
|
{
|
||||||
|
m_layers[i].packer.init(textureSize, textureSize);
|
||||||
|
}
|
||||||
|
m_usedLayers = 0;
|
||||||
|
m_usedFaces = 0;
|
||||||
|
|
||||||
|
m_textureSize = textureSize;
|
||||||
|
m_regionCount = 0;
|
||||||
|
m_maxRegionCount = maxRegionsCount;
|
||||||
|
m_regions = new AtlasRegion[maxRegionsCount];
|
||||||
|
m_textureBuffer = new uint8_t[ textureSize * textureSize * 6 * 4 ];
|
||||||
|
memset(m_textureBuffer, 0, textureSize * textureSize * 6 * 4);
|
||||||
|
//BGFX_TEXTURE_MIN_POINT|BGFX_TEXTURE_MAG_POINT|BGFX_TEXTURE_MIP_POINT;
|
||||||
|
//BGFX_TEXTURE_MIN_ANISOTROPIC|BGFX_TEXTURE_MAG_ANISOTROPIC|BGFX_TEXTURE_MIP_POINT
|
||||||
|
//BGFX_TEXTURE_U_CLAMP|BGFX_TEXTURE_V_CLAMP
|
||||||
|
uint32_t flags = 0;// BGFX_TEXTURE_MIN_ANISOTROPIC|BGFX_TEXTURE_MAG_ANISOTROPIC|BGFX_TEXTURE_MIP_POINT;
|
||||||
|
|
||||||
|
//Uncomment this to debug atlas
|
||||||
|
//const bgfx::Memory* mem = bgfx::alloc(textureSize*textureSize * 6 * 4);
|
||||||
|
//memset(mem->data, 255, mem->size);
|
||||||
|
const bgfx::Memory* mem = NULL;
|
||||||
|
m_textureHandle = bgfx::createTextureCube(6
|
||||||
|
, textureSize
|
||||||
|
, 1
|
||||||
|
, bgfx::TextureFormat::BGRA8
|
||||||
|
, flags
|
||||||
|
,mem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Atlas::Atlas(uint16_t textureSize, const uint8_t* textureBuffer , uint16_t regionCount, const uint8_t* regionBuffer, uint16_t maxRegionsCount)
|
||||||
|
{
|
||||||
|
assert(regionCount <= 64 && maxRegionsCount <= 4096);
|
||||||
|
//layers are frozen
|
||||||
|
m_usedLayers = 24;
|
||||||
|
m_usedFaces = 6;
|
||||||
|
|
||||||
|
m_textureSize = textureSize;
|
||||||
|
m_regionCount = regionCount;
|
||||||
|
//regions are frozen
|
||||||
|
m_maxRegionCount = regionCount;
|
||||||
|
m_regions = new AtlasRegion[regionCount];
|
||||||
|
m_textureBuffer = new uint8_t[getTextureBufferSize()];
|
||||||
|
|
||||||
|
//BGFX_TEXTURE_MIN_POINT|BGFX_TEXTURE_MAG_POINT|BGFX_TEXTURE_MIP_POINT;
|
||||||
|
//BGFX_TEXTURE_MIN_ANISOTROPIC|BGFX_TEXTURE_MAG_ANISOTROPIC|BGFX_TEXTURE_MIP_POINT
|
||||||
|
//BGFX_TEXTURE_U_CLAMP|BGFX_TEXTURE_V_CLAMP
|
||||||
|
uint32_t flags = 0;//BGFX_TEXTURE_MIN_ANISOTROPIC|BGFX_TEXTURE_MAG_ANISOTROPIC|BGFX_TEXTURE_MIP_POINT;
|
||||||
|
memcpy(m_regions, regionBuffer, regionCount * sizeof(AtlasRegion));
|
||||||
|
memcpy(m_textureBuffer, textureBuffer, getTextureBufferSize());
|
||||||
|
|
||||||
|
m_textureHandle = bgfx::createTextureCube(6
|
||||||
|
, textureSize
|
||||||
|
, 1
|
||||||
|
, bgfx::TextureFormat::BGRA8
|
||||||
|
, flags
|
||||||
|
, bgfx::makeRef(m_textureBuffer, getTextureBufferSize())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Atlas::~Atlas()
|
||||||
|
{
|
||||||
|
delete[] m_layers;
|
||||||
|
delete[] m_regions;
|
||||||
|
delete[] m_textureBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Atlas::addRegion(uint16_t width, uint16_t height, const uint8_t* bitmapBuffer, AtlasRegion::Type type)
|
||||||
|
{
|
||||||
|
if (m_regionCount >= m_maxRegionCount)
|
||||||
|
{
|
||||||
|
return UINT16_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t x,y;
|
||||||
|
// We want each bitmap to be separated by at least one black pixel
|
||||||
|
// TODO manage mipmaps
|
||||||
|
uint32_t idx = 0;
|
||||||
|
while(idx<m_usedLayers)
|
||||||
|
{
|
||||||
|
if(m_layers[idx].faceRegion.getType() == type)
|
||||||
|
{
|
||||||
|
if(m_layers[idx].packer.addRectangle(width+1,height+1,x,y)) break;
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(idx >= m_usedLayers)
|
||||||
|
{
|
||||||
|
//do we have still room to add layers ?
|
||||||
|
if( (idx + type) > 24 || m_usedFaces>=6)
|
||||||
|
{
|
||||||
|
return UINT16_MAX;
|
||||||
|
}
|
||||||
|
//create new layers
|
||||||
|
for(int i=0; i < type;++i)
|
||||||
|
{
|
||||||
|
m_layers[idx+i].faceRegion.setMask(type, m_usedFaces, i);
|
||||||
|
}
|
||||||
|
m_usedLayers += type;
|
||||||
|
m_usedFaces++;
|
||||||
|
|
||||||
|
|
||||||
|
//add it to the created layer
|
||||||
|
if(!m_layers[idx].packer.addRectangle(width+1,height+1,x,y))
|
||||||
|
{
|
||||||
|
return UINT16_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AtlasRegion& region = m_regions[m_regionCount];
|
||||||
|
region.x = x;
|
||||||
|
region.y = y;
|
||||||
|
region.width = width;
|
||||||
|
region.height = height;
|
||||||
|
region.mask = m_layers[idx].faceRegion.mask;
|
||||||
|
|
||||||
|
updateRegion(region, bitmapBuffer);
|
||||||
|
return m_regionCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Atlas::updateRegion(const AtlasRegion& region, const uint8_t* bitmapBuffer)
|
||||||
|
{
|
||||||
|
const bgfx::Memory* mem = bgfx::alloc(region.width * region.height * 4);
|
||||||
|
//BAD!
|
||||||
|
memset(mem->data,0, mem->size);
|
||||||
|
if(region.getType() == AtlasRegion::TYPE_BGRA8)
|
||||||
|
{
|
||||||
|
const uint8_t* inLineBuffer = bitmapBuffer;
|
||||||
|
uint8_t* outLineBuffer = m_textureBuffer + region.getFaceIndex() * (m_textureSize*m_textureSize*4) + (((region.y *m_textureSize)+region.x)*4);
|
||||||
|
|
||||||
|
//update the cpu buffer
|
||||||
|
for(int y = 0; y < region.height; ++y)
|
||||||
|
{
|
||||||
|
memcpy(outLineBuffer, inLineBuffer, region.width * 4);
|
||||||
|
inLineBuffer += region.width*4;
|
||||||
|
outLineBuffer += m_textureSize*4;
|
||||||
|
}
|
||||||
|
//update the GPU buffer
|
||||||
|
memcpy(mem->data, bitmapBuffer, mem->size);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
uint32_t layer = region.getComponentIndex();
|
||||||
|
uint32_t face = region.getFaceIndex();
|
||||||
|
const uint8_t* inLineBuffer = bitmapBuffer;
|
||||||
|
uint8_t* outLineBuffer = (m_textureBuffer + region.getFaceIndex() * (m_textureSize*m_textureSize*4) + (((region.y *m_textureSize)+region.x)*4));
|
||||||
|
|
||||||
|
//update the cpu buffer
|
||||||
|
for(int y = 0; y<region.height; ++y)
|
||||||
|
{
|
||||||
|
for(int x = 0; x<region.width; ++x)
|
||||||
|
{
|
||||||
|
outLineBuffer[(x*4) + layer] = inLineBuffer[x];
|
||||||
|
}
|
||||||
|
//update the GPU buffer
|
||||||
|
memcpy(mem->data + y*region.width*4, outLineBuffer, region.width*4);
|
||||||
|
inLineBuffer += region.width;
|
||||||
|
outLineBuffer += m_textureSize*4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bgfx::updateTextureCube(m_textureHandle, (uint8_t)region.getFaceIndex(), 0, region.x, region.y, region.width, region.height, mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Atlas::packFaceLayerUV(uint32_t idx, uint8_t* vertexBuffer, uint32_t offset, uint32_t stride )
|
||||||
|
{
|
||||||
|
packUV(m_layers[idx].faceRegion, vertexBuffer, offset, stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Atlas::packUV( uint16_t handle, uint8_t* vertexBuffer, uint32_t offset, uint32_t stride )
|
||||||
|
{
|
||||||
|
const AtlasRegion& region = m_regions[handle];
|
||||||
|
packUV(region, vertexBuffer, offset, stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Atlas::packUV( const AtlasRegion& region, uint8_t* vertexBuffer, uint32_t offset, uint32_t stride )
|
||||||
|
{
|
||||||
|
float texMult = 65535.0f / ((float)(m_textureSize));
|
||||||
|
static const int16_t minVal = -32768;
|
||||||
|
static const int16_t maxVal = 32767;
|
||||||
|
|
||||||
|
int16_t x0 = (int16_t)(region.x * texMult)-32768;
|
||||||
|
int16_t y0 = (int16_t)(region.y * texMult)-32768;
|
||||||
|
int16_t x1 = (int16_t)((region.x + region.width)* texMult)-32768;
|
||||||
|
int16_t y1 = (int16_t)((region.y + region.height)* texMult)-32768;
|
||||||
|
int16_t w = (int16_t) ((32767.0f/4.0f) * region.getComponentIndex());
|
||||||
|
|
||||||
|
vertexBuffer+=offset;
|
||||||
|
switch(region.getFaceIndex())
|
||||||
|
{
|
||||||
|
case 0: // +X
|
||||||
|
x0= -x0;
|
||||||
|
x1= -x1;
|
||||||
|
y0= -y0;
|
||||||
|
y1= -y1;
|
||||||
|
writeUV(vertexBuffer, maxVal, y0, x0, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, maxVal, y1, x0, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, maxVal, y1, x1, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, maxVal, y0, x1, w); vertexBuffer+=stride;
|
||||||
|
break;
|
||||||
|
case 1: // -X
|
||||||
|
y0= -y0;
|
||||||
|
y1= -y1;
|
||||||
|
writeUV(vertexBuffer, minVal, y0, x0, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, minVal, y1, x0, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, minVal, y1, x1, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, minVal, y0, x1, w); vertexBuffer+=stride;
|
||||||
|
break;
|
||||||
|
case 2: // +Y
|
||||||
|
writeUV(vertexBuffer, x0, maxVal, y0, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, x0, maxVal, y1, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, x1, maxVal, y1, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, x1, maxVal, y0, w); vertexBuffer+=stride;
|
||||||
|
break;
|
||||||
|
case 3: // -Y
|
||||||
|
y0= -y0;
|
||||||
|
y1= -y1;
|
||||||
|
writeUV(vertexBuffer, x0, minVal, y0, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, x0, minVal, y1, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, x1, minVal, y1, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, x1, minVal, y0, w); vertexBuffer+=stride;
|
||||||
|
break;
|
||||||
|
case 4: // +Z
|
||||||
|
y0= -y0;
|
||||||
|
y1= -y1;
|
||||||
|
writeUV(vertexBuffer, x0, y0, maxVal, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, x0, y1, maxVal, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, x1, y1, maxVal, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, x1, y0, maxVal, w); vertexBuffer+=stride;
|
||||||
|
break;
|
||||||
|
case 5: // -Z
|
||||||
|
x0= -x0;
|
||||||
|
x1= -x1;
|
||||||
|
y0= -y0;
|
||||||
|
y1= -y1;
|
||||||
|
writeUV(vertexBuffer, x0, y0, minVal, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, x0, y1, minVal, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, x1, y1, minVal, w); vertexBuffer+=stride;
|
||||||
|
writeUV(vertexBuffer, x1, y0, minVal, w); vertexBuffer+=stride;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
136
examples/common/cube_atlas.h
Normal file
136
examples/common/cube_atlas.h
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/* Copyright 2013 Jeremie Roy. All rights reserved.
|
||||||
|
* License: http://www.opensource.org/licenses/BSD-2-Clause
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/// Inspired from texture-atlas from freetype-gl (http://code.google.com/p/freetype-gl/)
|
||||||
|
/// by Nicolas Rougier (Nicolas.Rougier@inria.fr)
|
||||||
|
/// The actual implementation is based on the article by Jukka Jylänki : "A
|
||||||
|
/// Thousand Ways to Pack the Bin - A Practical Approach to Two-Dimensional
|
||||||
|
/// Rectangle Bin Packing", February 27, 2010.
|
||||||
|
/// More precisely, this is an implementation of the Skyline Bottom-Left
|
||||||
|
/// algorithm based on C++ sources provided by Jukka Jylänki at:
|
||||||
|
/// http://clb.demon.fi/files/RectangleBinPack/
|
||||||
|
|
||||||
|
#include <bgfx.h>
|
||||||
|
|
||||||
|
namespace bgfx
|
||||||
|
{
|
||||||
|
|
||||||
|
struct AtlasRegion
|
||||||
|
{
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
TYPE_GRAY = 1, // 1 component
|
||||||
|
TYPE_BGRA8 = 4 // 4 components
|
||||||
|
};
|
||||||
|
|
||||||
|
uint16_t x, y;
|
||||||
|
uint16_t width, height;
|
||||||
|
uint32_t mask; //encode the region type, the face index and the component index in case of a gray region
|
||||||
|
|
||||||
|
Type getType()const { return (Type) ((mask >> 0) & 0x0000000F); }
|
||||||
|
uint32_t getFaceIndex()const { return (mask >> 4) & 0x0000000F; }
|
||||||
|
uint32_t getComponentIndex()const { return (mask >> 8) & 0x0000000F; }
|
||||||
|
void setMask(Type type, uint32_t faceIndex, uint32_t componentIndex) { mask = (componentIndex << 8) + (faceIndex << 4) + (uint32_t)type; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Atlas
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// create an empty dynamic atlas (region can be updated and added)
|
||||||
|
/// @param textureSize an atlas creates a texture cube of 6 faces with size equal to (textureSize*textureSize * sizeof(RGBA))
|
||||||
|
/// @param maxRegionCount maximum number of region allowed in the atlas
|
||||||
|
Atlas(uint16_t textureSize, uint16_t _maxRegionsCount = 4096);
|
||||||
|
|
||||||
|
/// initialize a static atlas with serialized data (region can be updated but not added)
|
||||||
|
/// @param textureSize an atlas creates a texture cube of 6 faces with size equal to (textureSize*textureSize * sizeof(RGBA))
|
||||||
|
/// @param textureBuffer buffer of size 6*textureSize*textureSize*sizeof(uint32_t) (will be copied)
|
||||||
|
/// @param regionCount number of region in the Atlas
|
||||||
|
/// @param regionBuffer buffer containing the region (will be copied)
|
||||||
|
/// @param maxRegionCount maximum number of region allowed in the atlas
|
||||||
|
Atlas(uint16_t textureSize, const uint8_t * textureBuffer, uint16_t regionCount, const uint8_t* regionBuffer, uint16_t maxRegionsCount = 4096);
|
||||||
|
~Atlas();
|
||||||
|
|
||||||
|
/// add a region to the atlas, and copy the content of mem to the underlying texture
|
||||||
|
uint16_t addRegion(uint16_t width, uint16_t height, const uint8_t* bitmapBuffer, AtlasRegion::Type type = AtlasRegion::TYPE_BGRA8);
|
||||||
|
|
||||||
|
/// update a preallocated region
|
||||||
|
void updateRegion(const AtlasRegion& region, const uint8_t* bitmapBuffer);
|
||||||
|
|
||||||
|
/// Pack the UV coordinates of the four corners of a region to a vertex buffer using the supplied vertex format.
|
||||||
|
/// v0 -- v3
|
||||||
|
/// | | encoded in that order: v0,v1,v2,v3
|
||||||
|
/// v1 -- v2
|
||||||
|
/// @remark the UV are four signed short normalized components.
|
||||||
|
/// @remark the x,y,z components encode cube uv coordinates. The w component encode the color channel if any.
|
||||||
|
/// @param handle handle to the region we are interested in
|
||||||
|
/// @param vertexBuffer address of the first vertex we want to update. Must be valid up to vertexBuffer + offset + 3*stride + 4*sizeof(int16_t), which means the buffer must contains at least 4 vertex includind the first.
|
||||||
|
/// @param offset byte offset to the first uv coordinate of the vertex in the buffer
|
||||||
|
/// @param stride stride between tho UV coordinates, usually size of a Vertex.
|
||||||
|
void packUV( uint16_t regionHandle, uint8_t* vertexBuffer, uint32_t offset, uint32_t stride );
|
||||||
|
void packUV( const AtlasRegion& region, uint8_t* vertexBuffer, uint32_t offset, uint32_t stride );
|
||||||
|
|
||||||
|
/// Same as packUV but pack a whole face of the atlas cube, mostly used for debugging and visualizing atlas
|
||||||
|
void packFaceLayerUV(uint32_t idx, uint8_t* vertexBuffer, uint32_t offset, uint32_t stride );
|
||||||
|
|
||||||
|
/// Pack the vertex index of the region as 2 quad into an index buffer
|
||||||
|
void packIndex(uint16_t* indexBuffer, uint32_t startIndex, uint32_t startVertex )
|
||||||
|
{
|
||||||
|
indexBuffer[startIndex+0] = startVertex+0;
|
||||||
|
indexBuffer[startIndex+1] = startVertex+1;
|
||||||
|
indexBuffer[startIndex+2] = startVertex+2;
|
||||||
|
indexBuffer[startIndex+3] = startVertex+0;
|
||||||
|
indexBuffer[startIndex+4] = startVertex+2;
|
||||||
|
indexBuffer[startIndex+5] = startVertex+3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the TextureHandle (cube) of the atlas
|
||||||
|
bgfx::TextureHandle getTextureHandle() const { return m_textureHandle; }
|
||||||
|
|
||||||
|
//retrieve a region info
|
||||||
|
const AtlasRegion& getRegion(uint16_t handle) const { return m_regions[handle]; }
|
||||||
|
|
||||||
|
/// retrieve the size of side of a texture in pixels
|
||||||
|
uint16_t getTextureSize(){ return m_textureSize; }
|
||||||
|
|
||||||
|
/// retrieve the usage ratio of the atlas
|
||||||
|
//float getUsageRatio() const { return 0.0f; }
|
||||||
|
|
||||||
|
/// retrieve the numbers of region in the atlas
|
||||||
|
uint16_t getRegionCount() const { return m_regionCount; }
|
||||||
|
|
||||||
|
/// retrieve a pointer to the region buffer (in order to serialize it)
|
||||||
|
const AtlasRegion* getRegionBuffer() const { return m_regions; }
|
||||||
|
|
||||||
|
/// retrieve the byte size of the texture
|
||||||
|
uint32_t getTextureBufferSize() const { return 6*m_textureSize*m_textureSize*4; }
|
||||||
|
|
||||||
|
/// retrieve the mirrored texture buffer (to serialize it)
|
||||||
|
const uint8_t* getTextureBuffer() const { return m_textureBuffer; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void writeUV( uint8_t* vertexBuffer, int16_t x, int16_t y, int16_t z, int16_t w)
|
||||||
|
{
|
||||||
|
((uint16_t*) vertexBuffer)[0] = x;
|
||||||
|
((uint16_t*) vertexBuffer)[1] = y;
|
||||||
|
((uint16_t*) vertexBuffer)[2] = z;
|
||||||
|
((uint16_t*) vertexBuffer)[3] = w;
|
||||||
|
}
|
||||||
|
struct PackedLayer;
|
||||||
|
PackedLayer* m_layers;
|
||||||
|
|
||||||
|
uint32_t m_usedLayers;
|
||||||
|
uint32_t m_usedFaces;
|
||||||
|
|
||||||
|
bgfx::TextureHandle m_textureHandle;
|
||||||
|
uint16_t m_textureSize;
|
||||||
|
|
||||||
|
uint16_t m_regionCount;
|
||||||
|
uint16_t m_maxRegionCount;
|
||||||
|
|
||||||
|
AtlasRegion* m_regions;
|
||||||
|
uint8_t* m_textureBuffer;
|
||||||
|
|
||||||
|
};}
|
792
examples/common/font/font_manager.cpp
Normal file
792
examples/common/font/font_manager.cpp
Normal file
@ -0,0 +1,792 @@
|
|||||||
|
/* Copyright 2013 Jeremie Roy. All rights reserved.
|
||||||
|
* License: http://www.opensource.org/licenses/BSD-2-Clause
|
||||||
|
*/
|
||||||
|
#include "font_manager.h"
|
||||||
|
#include "../cube_atlas.h"
|
||||||
|
|
||||||
|
#pragma warning( push )
|
||||||
|
#pragma warning( disable: 4146 )
|
||||||
|
#pragma warning( disable: 4700 )
|
||||||
|
#pragma warning( disable: 4100 )
|
||||||
|
#pragma warning( disable: 4701 )
|
||||||
|
#include "../../../3rdparty/freetype/freetype.h"
|
||||||
|
#pragma warning( pop )
|
||||||
|
|
||||||
|
#include "../../../3rdparty/edtaa3/edtaa3func.h"
|
||||||
|
#include "../../../3rdparty/edtaa3/edtaa3func.cpp"
|
||||||
|
#include <math.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
|
||||||
|
#if BGFX_CONFIG_USE_TINYSTL
|
||||||
|
namespace tinystl
|
||||||
|
{
|
||||||
|
//struct bgfx_allocator
|
||||||
|
//{
|
||||||
|
//static void* static_allocate(size_t _bytes);
|
||||||
|
//static void static_deallocate(void* _ptr, size_t /*_bytes*/);
|
||||||
|
//};
|
||||||
|
} // namespace tinystl
|
||||||
|
//# define TINYSTL_ALLOCATOR tinystl::bgfx_allocator
|
||||||
|
# include <TINYSTL/unordered_map.h>
|
||||||
|
//# include <TINYSTL/unordered_set.h>
|
||||||
|
namespace stl = tinystl;
|
||||||
|
#else
|
||||||
|
# include <unordered_map>
|
||||||
|
namespace std { namespace tr1 {} }
|
||||||
|
namespace stl {
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::tr1;
|
||||||
|
}
|
||||||
|
#endif // BGFX_CONFIG_USE_TINYSTL
|
||||||
|
|
||||||
|
|
||||||
|
#define BGFX_FONT_ASSERT(cond, message) assert((cond) && (message));
|
||||||
|
|
||||||
|
namespace bgfx_font
|
||||||
|
{
|
||||||
|
|
||||||
|
class FontManager::TrueTypeFont
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TrueTypeFont();
|
||||||
|
~TrueTypeFont();
|
||||||
|
|
||||||
|
/// Initialize from an external buffer
|
||||||
|
/// @remark The ownership of the buffer is external, and you must ensure it stays valid up to this object lifetime
|
||||||
|
/// @return true if the initialization succeed
|
||||||
|
bool init(const uint8_t* buffer, uint32_t bufferSize, int32_t fontIndex, uint32_t pixelHeight );
|
||||||
|
|
||||||
|
/// return the font descriptor of the current font
|
||||||
|
FontInfo getFontInfo();
|
||||||
|
|
||||||
|
/// raster a glyph as 8bit alpha to a memory buffer
|
||||||
|
/// update the GlyphInfo according to the raster strategy
|
||||||
|
/// @ remark buffer min size: glyphInfo.width * glyphInfo * height * sizeof(char)
|
||||||
|
bool bakeGlyphAlpha(CodePoint_t codePoint, GlyphInfo& outGlyphInfo, uint8_t* outBuffer);
|
||||||
|
|
||||||
|
/// raster a glyph as 32bit subpixel rgba to a memory buffer
|
||||||
|
/// update the GlyphInfo according to the raster strategy
|
||||||
|
/// @ remark buffer min size: glyphInfo.width * glyphInfo * height * sizeof(uint32_t)
|
||||||
|
bool bakeGlyphSubpixel(CodePoint_t codePoint, GlyphInfo& outGlyphInfo, uint8_t* outBuffer);
|
||||||
|
|
||||||
|
/// raster a glyph as 8bit signed distance to a memory buffer
|
||||||
|
/// update the GlyphInfo according to the raster strategy
|
||||||
|
/// @ remark buffer min size: glyphInfo.width * glyphInfo * height * sizeof(char)
|
||||||
|
bool bakeGlyphDistance(CodePoint_t codePoint, GlyphInfo& outGlyphInfo, uint8_t* outBuffer);
|
||||||
|
private:
|
||||||
|
void* m_font;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct FTHolder
|
||||||
|
{
|
||||||
|
FT_Library library;
|
||||||
|
FT_Face face;
|
||||||
|
};
|
||||||
|
FontManager::TrueTypeFont::TrueTypeFont(): m_font(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FontManager::TrueTypeFont::~TrueTypeFont()
|
||||||
|
{
|
||||||
|
if(m_font!=NULL)
|
||||||
|
{
|
||||||
|
FTHolder* holder = (FTHolder*) m_font;
|
||||||
|
FT_Done_Face( holder->face );
|
||||||
|
FT_Done_FreeType( holder->library );
|
||||||
|
delete m_font;
|
||||||
|
m_font = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FontManager::TrueTypeFont::init(const uint8_t* buffer, uint32_t bufferSize, int32_t fontIndex, uint32_t pixelHeight)
|
||||||
|
{
|
||||||
|
assert((bufferSize > 256 && bufferSize < 100000000) && "TrueType buffer size is suspicious");
|
||||||
|
assert((pixelHeight > 4 && pixelHeight < 128) && "TrueType buffer size is suspicious");
|
||||||
|
|
||||||
|
assert(m_font == NULL && "TrueTypeFont already initialized" );
|
||||||
|
|
||||||
|
FTHolder* holder = new FTHolder();
|
||||||
|
|
||||||
|
// Initialize Freetype library
|
||||||
|
FT_Error error = FT_Init_FreeType( &holder->library );
|
||||||
|
if( error)
|
||||||
|
{
|
||||||
|
delete holder;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = FT_New_Memory_Face( holder->library, buffer, bufferSize, fontIndex, &holder->face );
|
||||||
|
if ( error == FT_Err_Unknown_File_Format )
|
||||||
|
{
|
||||||
|
// the font file could be opened and read, but it appears
|
||||||
|
//that its font format is unsupported
|
||||||
|
FT_Done_FreeType( holder->library );
|
||||||
|
delete holder;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if ( error )
|
||||||
|
{
|
||||||
|
// another error code means that the font file could not
|
||||||
|
// be opened or read, or simply that it is broken...
|
||||||
|
FT_Done_FreeType( holder->library );
|
||||||
|
delete holder;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select unicode charmap
|
||||||
|
error = FT_Select_Charmap( holder->face, FT_ENCODING_UNICODE );
|
||||||
|
if( error )
|
||||||
|
{
|
||||||
|
FT_Done_Face( holder->face );
|
||||||
|
FT_Done_FreeType( holder->library );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//set size in pixels
|
||||||
|
error = FT_Set_Pixel_Sizes( holder->face, 0, pixelHeight );
|
||||||
|
if( error )
|
||||||
|
{
|
||||||
|
FT_Done_Face( holder->face );
|
||||||
|
FT_Done_FreeType( holder->library );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_font = holder;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FontInfo FontManager::TrueTypeFont::getFontInfo()
|
||||||
|
{
|
||||||
|
assert(m_font != NULL && "TrueTypeFont not initialized" );
|
||||||
|
FTHolder* holder = (FTHolder*) m_font;
|
||||||
|
|
||||||
|
assert(FT_IS_SCALABLE (holder->face));
|
||||||
|
|
||||||
|
FT_Size_Metrics metrics = holder->face->size->metrics;
|
||||||
|
|
||||||
|
//todo manage unscalable font
|
||||||
|
FontInfo outFontInfo;
|
||||||
|
outFontInfo.scale = 1.0f;
|
||||||
|
outFontInfo.ascender = metrics.ascender /64.0f;
|
||||||
|
outFontInfo.descender = metrics.descender /64.0f;
|
||||||
|
outFontInfo.lineGap = (metrics.height - metrics.ascender + metrics.descender) /64.0f;
|
||||||
|
|
||||||
|
outFontInfo.underline_position = FT_MulFix(holder->face->underline_position, metrics.y_scale) /64.0f;
|
||||||
|
outFontInfo.underline_thickness= FT_MulFix(holder->face->underline_thickness,metrics.y_scale) /64.0f;
|
||||||
|
return outFontInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FontManager::TrueTypeFont::bakeGlyphAlpha(CodePoint_t codePoint, GlyphInfo& glyphInfo, uint8_t* outBuffer)
|
||||||
|
{
|
||||||
|
assert(m_font != NULL && "TrueTypeFont not initialized" );
|
||||||
|
FTHolder* holder = (FTHolder*) m_font;
|
||||||
|
|
||||||
|
glyphInfo.glyphIndex = FT_Get_Char_Index( holder->face, codePoint );
|
||||||
|
|
||||||
|
FT_GlyphSlot slot = holder->face->glyph;
|
||||||
|
FT_Error error = FT_Load_Glyph( holder->face, glyphInfo.glyphIndex, FT_LOAD_DEFAULT );
|
||||||
|
if(error) { return false; }
|
||||||
|
|
||||||
|
FT_Glyph glyph;
|
||||||
|
error = FT_Get_Glyph( slot, &glyph );
|
||||||
|
if ( error ) { return false; }
|
||||||
|
|
||||||
|
error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
|
||||||
|
if(error){ return false; }
|
||||||
|
|
||||||
|
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph;
|
||||||
|
|
||||||
|
int x = bitmap->left;
|
||||||
|
int y = -bitmap->top;
|
||||||
|
int w = bitmap->bitmap.width;
|
||||||
|
int h = bitmap->bitmap.rows;
|
||||||
|
|
||||||
|
glyphInfo.offset_x = (float) x;
|
||||||
|
glyphInfo.offset_y = (float) y;
|
||||||
|
glyphInfo.width = (float) w;
|
||||||
|
glyphInfo.height = (float) h;
|
||||||
|
glyphInfo.advance_x = (float)slot->advance.x /64.0f;
|
||||||
|
glyphInfo.advance_y = (float)slot->advance.y /64.0f;
|
||||||
|
|
||||||
|
int charsize = 1;
|
||||||
|
int depth=1;
|
||||||
|
int stride = bitmap->bitmap.pitch;
|
||||||
|
for( int i=0; i<h; ++i )
|
||||||
|
{
|
||||||
|
memcpy(outBuffer+(i*w) * charsize * depth,
|
||||||
|
bitmap->bitmap.buffer + (i*stride) * charsize, w * charsize * depth );
|
||||||
|
}
|
||||||
|
FT_Done_Glyph(glyph);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FontManager::TrueTypeFont::bakeGlyphSubpixel(CodePoint_t codePoint, GlyphInfo& glyphInfo, uint8_t* outBuffer)
|
||||||
|
{
|
||||||
|
assert(m_font != NULL && "TrueTypeFont not initialized" );
|
||||||
|
FTHolder* holder = (FTHolder*) m_font;
|
||||||
|
|
||||||
|
glyphInfo.glyphIndex = FT_Get_Char_Index( holder->face, codePoint );
|
||||||
|
|
||||||
|
FT_GlyphSlot slot = holder->face->glyph;
|
||||||
|
FT_Error error = FT_Load_Glyph( holder->face, glyphInfo.glyphIndex, FT_LOAD_DEFAULT );
|
||||||
|
if(error) { return false; }
|
||||||
|
|
||||||
|
FT_Glyph glyph;
|
||||||
|
error = FT_Get_Glyph( slot, &glyph );
|
||||||
|
if ( error ) { return false; }
|
||||||
|
|
||||||
|
error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_LCD, 0, 1 );
|
||||||
|
if(error){ return false; }
|
||||||
|
|
||||||
|
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph;
|
||||||
|
int x = bitmap->left;
|
||||||
|
int y = -bitmap->top;
|
||||||
|
int w = bitmap->bitmap.width;
|
||||||
|
int h = bitmap->bitmap.rows;
|
||||||
|
|
||||||
|
glyphInfo.offset_x = (float) x;
|
||||||
|
glyphInfo.offset_y = (float) y;
|
||||||
|
glyphInfo.width = (float) w;
|
||||||
|
glyphInfo.height = (float) h;
|
||||||
|
glyphInfo.advance_x = (float)slot->advance.x /64.0f;
|
||||||
|
glyphInfo.advance_y = (float)slot->advance.y /64.0f;
|
||||||
|
int charsize = 1;
|
||||||
|
int depth=3;
|
||||||
|
int stride = bitmap->bitmap.pitch;
|
||||||
|
for( int i=0; i<h; ++i )
|
||||||
|
{
|
||||||
|
memcpy(outBuffer+(i*w) * charsize * depth,
|
||||||
|
bitmap->bitmap.buffer + (i*stride) * charsize, w * charsize * depth );
|
||||||
|
}
|
||||||
|
FT_Done_Glyph(glyph);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO optimize: remove dynamic allocation and convert double to float
|
||||||
|
void make_distance_map( unsigned char *img, unsigned char *outImg, unsigned int width, unsigned int height )
|
||||||
|
{
|
||||||
|
short * xdist = (short *) malloc( width * height * sizeof(short) );
|
||||||
|
short * ydist = (short *) malloc( width * height * sizeof(short) );
|
||||||
|
double * gx = (double *) calloc( width * height, sizeof(double) );
|
||||||
|
double * gy = (double *) calloc( width * height, sizeof(double) );
|
||||||
|
double * data = (double *) calloc( width * height, sizeof(double) );
|
||||||
|
double * outside = (double *) calloc( width * height, sizeof(double) );
|
||||||
|
double * inside = (double *) calloc( width * height, sizeof(double) );
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
// Convert img into double (data)
|
||||||
|
double img_min = 255, img_max = -255;
|
||||||
|
for( i=0; i<width*height; ++i)
|
||||||
|
{
|
||||||
|
double v = img[i];
|
||||||
|
data[i] = v;
|
||||||
|
if (v > img_max) img_max = v;
|
||||||
|
if (v < img_min) img_min = v;
|
||||||
|
}
|
||||||
|
// Rescale image levels between 0 and 1
|
||||||
|
for( i=0; i<width*height; ++i)
|
||||||
|
{
|
||||||
|
data[i] = (img[i]-img_min)/(img_max-img_min);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute outside = edtaa3(bitmap); % Transform background (0's)
|
||||||
|
computegradient( data, width, height, gx, gy);
|
||||||
|
edtaa3(data, gx, gy, width, height, xdist, ydist, outside);
|
||||||
|
for( i=0; i<width*height; ++i)
|
||||||
|
if( outside[i] < 0 )
|
||||||
|
outside[i] = 0.0;
|
||||||
|
|
||||||
|
// Compute inside = edtaa3(1-bitmap); % Transform foreground (1's)
|
||||||
|
memset(gx, 0, sizeof(double)*width*height );
|
||||||
|
memset(gy, 0, sizeof(double)*width*height );
|
||||||
|
for( i=0; i<width*height; ++i)
|
||||||
|
data[i] = 1.0 - data[i];
|
||||||
|
computegradient( data, width, height, gx, gy);
|
||||||
|
edtaa3(data, gx, gy, width, height, xdist, ydist, inside);
|
||||||
|
for( i=0; i<width*height; ++i)
|
||||||
|
if( inside[i] < 0 )
|
||||||
|
inside[i] = 0.0;
|
||||||
|
|
||||||
|
// distmap = outside - inside; % Bipolar distance field
|
||||||
|
unsigned char *out = outImg;//(unsigned char *) malloc( width * height * sizeof(unsigned char) );
|
||||||
|
for( i=0; i<width*height; ++i)
|
||||||
|
{
|
||||||
|
//out[i] = 127 - outside[i]*8;
|
||||||
|
//if(out[i]<0) out[i] = 0;
|
||||||
|
//out[i] += inside[i]*16;
|
||||||
|
//if(out[i]>255) out[i] = 255;
|
||||||
|
|
||||||
|
outside[i] -= inside[i];
|
||||||
|
outside[i] = 128 + outside[i]*16;
|
||||||
|
|
||||||
|
//if(outside[i] > 8) outside[i] = 8;
|
||||||
|
//if(inside[i] > 8) outside[i] = 8;
|
||||||
|
|
||||||
|
//outside[i] = 128 - inside[i]*8 + outside[i]*8;
|
||||||
|
|
||||||
|
if( outside[i] < 0 ) outside[i] = 0;
|
||||||
|
if( outside[i] > 255 ) outside[i] = 255;
|
||||||
|
out[i] = 255 - (unsigned char) outside[i];
|
||||||
|
//out[i] = (unsigned char) outside[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
free( xdist );
|
||||||
|
free( ydist );
|
||||||
|
free( gx );
|
||||||
|
free( gy );
|
||||||
|
free( data );
|
||||||
|
free( outside );
|
||||||
|
free( inside );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool FontManager::TrueTypeFont::bakeGlyphDistance(CodePoint_t codePoint, GlyphInfo& glyphInfo, uint8_t* outBuffer)
|
||||||
|
{
|
||||||
|
assert(m_font != NULL && "TrueTypeFont not initialized" );
|
||||||
|
FTHolder* holder = (FTHolder*) m_font;
|
||||||
|
|
||||||
|
glyphInfo.glyphIndex = FT_Get_Char_Index( holder->face, codePoint );
|
||||||
|
|
||||||
|
FT_Int32 loadMode = FT_LOAD_DEFAULT|FT_LOAD_NO_HINTING;
|
||||||
|
FT_Render_Mode renderMode = FT_RENDER_MODE_NORMAL;
|
||||||
|
|
||||||
|
FT_GlyphSlot slot = holder->face->glyph;
|
||||||
|
FT_Error error = FT_Load_Glyph( holder->face, glyphInfo.glyphIndex, loadMode );
|
||||||
|
if(error) { return false; }
|
||||||
|
|
||||||
|
FT_Glyph glyph;
|
||||||
|
error = FT_Get_Glyph( slot, &glyph );
|
||||||
|
if ( error ) { return false; }
|
||||||
|
|
||||||
|
error = FT_Glyph_To_Bitmap( &glyph, renderMode, 0, 1 );
|
||||||
|
if(error){ return false; }
|
||||||
|
|
||||||
|
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph;
|
||||||
|
|
||||||
|
int x = bitmap->left;
|
||||||
|
int y = -bitmap->top;
|
||||||
|
int w = bitmap->bitmap.width;
|
||||||
|
int h = bitmap->bitmap.rows;
|
||||||
|
|
||||||
|
glyphInfo.offset_x = (float) x;
|
||||||
|
glyphInfo.offset_y = (float) y;
|
||||||
|
glyphInfo.width = (float) w;
|
||||||
|
glyphInfo.height = (float) h;
|
||||||
|
glyphInfo.advance_x = (float)slot->advance.x /64.0f;
|
||||||
|
glyphInfo.advance_y = (float)slot->advance.y /64.0f;
|
||||||
|
|
||||||
|
int charsize = 1;
|
||||||
|
int depth=1;
|
||||||
|
int stride = bitmap->bitmap.pitch;
|
||||||
|
|
||||||
|
for( int i=0; i<h; ++i )
|
||||||
|
{
|
||||||
|
|
||||||
|
memcpy(outBuffer+(i*w) * charsize * depth,
|
||||||
|
bitmap->bitmap.buffer + (i*stride) * charsize, w * charsize * depth );
|
||||||
|
}
|
||||||
|
FT_Done_Glyph(glyph);
|
||||||
|
|
||||||
|
if(w*h >0)
|
||||||
|
{
|
||||||
|
uint32_t dw = 6;
|
||||||
|
uint32_t dh = 6;
|
||||||
|
if(dw<2) dw = 2;
|
||||||
|
if(dh<2) dh = 2;
|
||||||
|
|
||||||
|
uint32_t nw = w + dw*2;
|
||||||
|
uint32_t nh = h + dh*2;
|
||||||
|
assert(nw*nh < 128*128);
|
||||||
|
uint32_t buffSize = nw*nh*sizeof(uint8_t);
|
||||||
|
|
||||||
|
uint8_t * alphaImg = (uint8_t *) malloc( buffSize );
|
||||||
|
memset(alphaImg, 0, nw*nh*sizeof(uint8_t));
|
||||||
|
|
||||||
|
//copy the original buffer to the temp one
|
||||||
|
for(uint32_t i= dh; i< nh-dh; ++i)
|
||||||
|
{
|
||||||
|
memcpy(alphaImg+i*nw+dw, outBuffer+(i-dh)*w, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
make_distance_map(alphaImg, outBuffer, nw, nh);
|
||||||
|
free(alphaImg);
|
||||||
|
|
||||||
|
glyphInfo.offset_x -= (float) dw;
|
||||||
|
glyphInfo.offset_y -= (float) dh;
|
||||||
|
glyphInfo.width = (float) nw ;
|
||||||
|
glyphInfo.height = (float) nh;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//*************************************************************
|
||||||
|
|
||||||
|
typedef stl::unordered_map<CodePoint_t, GlyphInfo> GlyphHash_t;
|
||||||
|
// cache font data
|
||||||
|
struct FontManager::CachedFont
|
||||||
|
{
|
||||||
|
CachedFont(){ trueTypeFont = NULL; masterFontHandle.idx = -1; }
|
||||||
|
FontInfo fontInfo;
|
||||||
|
GlyphHash_t cachedGlyphs;
|
||||||
|
FontManager::TrueTypeFont* trueTypeFont;
|
||||||
|
// an handle to a master font in case of sub distance field font
|
||||||
|
FontHandle masterFontHandle;
|
||||||
|
int16_t __padding__;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const uint16_t MAX_OPENED_FILES = 64;
|
||||||
|
const uint16_t MAX_OPENED_FONT = 64;
|
||||||
|
const uint32_t MAX_FONT_BUFFER_SIZE = 512*512*4;
|
||||||
|
|
||||||
|
FontManager::FontManager(bgfx::Atlas* atlas):m_filesHandles(MAX_OPENED_FILES), m_fontHandles(MAX_OPENED_FONT)
|
||||||
|
{
|
||||||
|
m_atlas = atlas;
|
||||||
|
m_ownAtlas = false;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
FontManager::FontManager(uint32_t textureSideWidth):m_filesHandles(MAX_OPENED_FILES), m_fontHandles(MAX_OPENED_FONT)
|
||||||
|
{
|
||||||
|
m_atlas = new bgfx::Atlas(textureSideWidth);
|
||||||
|
m_ownAtlas = true;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FontManager::init()
|
||||||
|
{
|
||||||
|
m_cachedFiles = new CachedFile[MAX_OPENED_FILES];
|
||||||
|
m_cachedFonts = new CachedFont[MAX_OPENED_FONT];
|
||||||
|
m_buffer = new uint8_t[MAX_FONT_BUFFER_SIZE];
|
||||||
|
|
||||||
|
// Create filler rectangle
|
||||||
|
uint8_t buffer[4*4*4];
|
||||||
|
memset( buffer, 255, 4 * 4 * 4);
|
||||||
|
|
||||||
|
m_blackGlyph.width=3;
|
||||||
|
m_blackGlyph.height=3;
|
||||||
|
assert( addBitmap(m_blackGlyph, buffer) );
|
||||||
|
//make sure the black glyph doesn't bleed
|
||||||
|
|
||||||
|
/*int16_t texUnit = 65535 / m_textureWidth;
|
||||||
|
m_blackGlyph.texture_x0 += texUnit;
|
||||||
|
m_blackGlyph.texture_y0 += texUnit;
|
||||||
|
m_blackGlyph.texture_x1 -= texUnit;
|
||||||
|
m_blackGlyph.texture_y1 -= texUnit;*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FontManager::~FontManager()
|
||||||
|
{
|
||||||
|
assert(m_fontHandles.getNumHandles() == 0 && "All the fonts must be destroyed before destroying the manager");
|
||||||
|
delete [] m_cachedFonts;
|
||||||
|
|
||||||
|
assert(m_filesHandles.getNumHandles() == 0 && "All the font files must be destroyed before destroying the manager");
|
||||||
|
delete [] m_cachedFiles;
|
||||||
|
|
||||||
|
delete [] m_buffer;
|
||||||
|
|
||||||
|
if(m_ownAtlas)
|
||||||
|
{
|
||||||
|
delete m_atlas;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TrueTypeHandle FontManager::loadTrueTypeFromFile(const char* fontPath)
|
||||||
|
{
|
||||||
|
FILE * pFile;
|
||||||
|
pFile = fopen (fontPath, "rb");
|
||||||
|
if (pFile==NULL)
|
||||||
|
{
|
||||||
|
TrueTypeHandle invalid = BGFX_INVALID_HANDLE;
|
||||||
|
return invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to the end of the file.
|
||||||
|
if (fseek(pFile, 0L, SEEK_END) == 0)
|
||||||
|
{
|
||||||
|
// Get the size of the file.
|
||||||
|
long bufsize = ftell(pFile);
|
||||||
|
if (bufsize == -1)
|
||||||
|
{
|
||||||
|
fclose(pFile);
|
||||||
|
TrueTypeHandle invalid = BGFX_INVALID_HANDLE;
|
||||||
|
return invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* buffer = new uint8_t[bufsize];
|
||||||
|
|
||||||
|
// Go back to the start of the file.
|
||||||
|
fseek(pFile, 0L, SEEK_SET);
|
||||||
|
|
||||||
|
// Read the entire file into memory.
|
||||||
|
size_t newLen = fread((void*)buffer, sizeof(char), bufsize, pFile);
|
||||||
|
if (newLen == 0)
|
||||||
|
{
|
||||||
|
fclose(pFile);
|
||||||
|
delete [] buffer;
|
||||||
|
TrueTypeHandle invalid = BGFX_INVALID_HANDLE;
|
||||||
|
return invalid;
|
||||||
|
}
|
||||||
|
fclose(pFile);
|
||||||
|
|
||||||
|
uint16_t id = m_filesHandles.alloc();
|
||||||
|
assert(id != bx::HandleAlloc::invalid);
|
||||||
|
m_cachedFiles[id].buffer = buffer;
|
||||||
|
m_cachedFiles[id].bufferSize = bufsize;
|
||||||
|
TrueTypeHandle ret = {id};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
//TODO validate font
|
||||||
|
TrueTypeHandle invalid = BGFX_INVALID_HANDLE;
|
||||||
|
return invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrueTypeHandle FontManager::loadTrueTypeFromMemory(const uint8_t* buffer, uint32_t size)
|
||||||
|
{
|
||||||
|
uint16_t id = m_filesHandles.alloc();
|
||||||
|
assert(id != bx::HandleAlloc::invalid);
|
||||||
|
m_cachedFiles[id].buffer = new uint8_t[size];
|
||||||
|
m_cachedFiles[id].bufferSize = size;
|
||||||
|
memcpy(m_cachedFiles[id].buffer, buffer, size);
|
||||||
|
|
||||||
|
//TODO validate font
|
||||||
|
TrueTypeHandle ret = {id};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FontManager::unloadTrueType(TrueTypeHandle handle)
|
||||||
|
{
|
||||||
|
assert(bgfx::invalidHandle != handle.idx);
|
||||||
|
delete m_cachedFiles[handle.idx].buffer;
|
||||||
|
m_cachedFiles[handle.idx].bufferSize = 0;
|
||||||
|
m_cachedFiles[handle.idx].buffer = NULL;
|
||||||
|
m_filesHandles.free(handle.idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
FontHandle FontManager::createFontByPixelSize(TrueTypeHandle handle, uint32_t typefaceIndex, uint32_t pixelSize, FontType fontType)
|
||||||
|
{
|
||||||
|
assert(bgfx::invalidHandle != handle.idx);
|
||||||
|
|
||||||
|
TrueTypeFont* ttf = new TrueTypeFont();
|
||||||
|
if(!ttf->init( m_cachedFiles[handle.idx].buffer, m_cachedFiles[handle.idx].bufferSize, typefaceIndex, pixelSize))
|
||||||
|
{
|
||||||
|
delete ttf;
|
||||||
|
FontHandle invalid = BGFX_INVALID_HANDLE;
|
||||||
|
return invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t fontIdx = m_fontHandles.alloc();
|
||||||
|
assert(fontIdx != bx::HandleAlloc::invalid);
|
||||||
|
|
||||||
|
m_cachedFonts[fontIdx].trueTypeFont = ttf;
|
||||||
|
m_cachedFonts[fontIdx].fontInfo = ttf->getFontInfo();
|
||||||
|
m_cachedFonts[fontIdx].fontInfo.fontType = fontType;
|
||||||
|
m_cachedFonts[fontIdx].fontInfo.pixelSize = pixelSize;
|
||||||
|
m_cachedFonts[fontIdx].cachedGlyphs.clear();
|
||||||
|
m_cachedFonts[fontIdx].masterFontHandle.idx = -1;
|
||||||
|
FontHandle ret = {fontIdx};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
FontHandle FontManager::createScaledFontToPixelSize(FontHandle _baseFontHandle, uint32_t _pixelSize)
|
||||||
|
{
|
||||||
|
assert(bgfx::invalidHandle != _baseFontHandle.idx);
|
||||||
|
CachedFont& font = m_cachedFonts[_baseFontHandle.idx];
|
||||||
|
FontInfo& fontInfo = font.fontInfo;
|
||||||
|
|
||||||
|
FontInfo newFontInfo = fontInfo;
|
||||||
|
newFontInfo.pixelSize = _pixelSize;
|
||||||
|
newFontInfo.scale = (float)_pixelSize / (float) fontInfo.pixelSize;
|
||||||
|
newFontInfo.ascender = (newFontInfo.ascender * newFontInfo.scale);
|
||||||
|
newFontInfo.descender = (newFontInfo.descender * newFontInfo.scale);
|
||||||
|
newFontInfo.lineGap = (newFontInfo.lineGap * newFontInfo.scale);
|
||||||
|
newFontInfo.underline_thickness = (newFontInfo.underline_thickness * newFontInfo.scale);
|
||||||
|
newFontInfo.underline_position = (newFontInfo.underline_position * newFontInfo.scale);
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t fontIdx = m_fontHandles.alloc();
|
||||||
|
assert(fontIdx != bx::HandleAlloc::invalid);
|
||||||
|
m_cachedFonts[fontIdx].cachedGlyphs.clear();
|
||||||
|
m_cachedFonts[fontIdx].fontInfo = newFontInfo;
|
||||||
|
m_cachedFonts[fontIdx].trueTypeFont = NULL;
|
||||||
|
m_cachedFonts[fontIdx].masterFontHandle = _baseFontHandle;
|
||||||
|
FontHandle ret = {fontIdx};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
FontHandle FontManager::loadBakedFontFromFile(const char* /*fontPath*/, const char* /*descriptorPath*/)
|
||||||
|
{
|
||||||
|
assert(false); //TODO implement
|
||||||
|
FontHandle invalid = BGFX_INVALID_HANDLE;
|
||||||
|
return invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
FontHandle FontManager::loadBakedFontFromMemory(const uint8_t* /*imageBuffer*/, uint32_t /*imageSize*/, const uint8_t* /*descriptorBuffer*/, uint32_t /*descriptorSize*/)
|
||||||
|
{
|
||||||
|
assert(false); //TODO implement
|
||||||
|
FontHandle invalid = BGFX_INVALID_HANDLE;
|
||||||
|
return invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FontManager::destroyFont(FontHandle _handle)
|
||||||
|
{
|
||||||
|
assert(bgfx::invalidHandle != _handle.idx);
|
||||||
|
|
||||||
|
if(m_cachedFonts[_handle.idx].trueTypeFont != NULL)
|
||||||
|
{
|
||||||
|
delete m_cachedFonts[_handle.idx].trueTypeFont;
|
||||||
|
m_cachedFonts[_handle.idx].trueTypeFont = NULL;
|
||||||
|
}
|
||||||
|
m_cachedFonts[_handle.idx].cachedGlyphs.clear();
|
||||||
|
m_fontHandles.free(_handle.idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FontManager::preloadGlyph(FontHandle handle, const wchar_t* _string)
|
||||||
|
{
|
||||||
|
assert(bgfx::invalidHandle != handle.idx);
|
||||||
|
CachedFont& font = m_cachedFonts[handle.idx];
|
||||||
|
|
||||||
|
//if truetype present
|
||||||
|
if(font.trueTypeFont != NULL)
|
||||||
|
{
|
||||||
|
//parse string
|
||||||
|
for( size_t i=0, end = wcslen(_string) ; i < end; ++i )
|
||||||
|
{
|
||||||
|
//if glyph cached, continue
|
||||||
|
CodePoint_t codePoint = _string[i];
|
||||||
|
if(!preloadGlyph(handle, codePoint))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FontManager::preloadGlyph(FontHandle handle, CodePoint_t codePoint)
|
||||||
|
{
|
||||||
|
assert(bgfx::invalidHandle != handle.idx);
|
||||||
|
CachedFont& font = m_cachedFonts[handle.idx];
|
||||||
|
FontInfo& fontInfo = font.fontInfo;
|
||||||
|
//check if glyph not already present
|
||||||
|
GlyphHash_t::iterator iter = font.cachedGlyphs.find(codePoint);
|
||||||
|
if(iter != font.cachedGlyphs.end())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if truetype present
|
||||||
|
if(font.trueTypeFont != NULL)
|
||||||
|
{
|
||||||
|
GlyphInfo glyphInfo;
|
||||||
|
|
||||||
|
//bake glyph as bitmap to buffer
|
||||||
|
switch(font.fontInfo.fontType)
|
||||||
|
{
|
||||||
|
case FONT_TYPE_ALPHA:
|
||||||
|
font.trueTypeFont->bakeGlyphAlpha(codePoint, glyphInfo, m_buffer);
|
||||||
|
break;
|
||||||
|
//case FONT_TYPE_LCD:
|
||||||
|
//font.trueTypeFont->bakeGlyphSubpixel(codePoint, glyphInfo, m_buffer);
|
||||||
|
//break;
|
||||||
|
case FONT_TYPE_DISTANCE:
|
||||||
|
font.trueTypeFont->bakeGlyphDistance(codePoint, glyphInfo, m_buffer);
|
||||||
|
break;
|
||||||
|
case FONT_TYPE_DISTANCE_SUBPIXEL:
|
||||||
|
font.trueTypeFont->bakeGlyphDistance(codePoint, glyphInfo, m_buffer);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false && "TextureType not supported yet");
|
||||||
|
};
|
||||||
|
|
||||||
|
//copy bitmap to texture
|
||||||
|
if(!addBitmap(glyphInfo, m_buffer) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glyphInfo.advance_x = (glyphInfo.advance_x * fontInfo.scale);
|
||||||
|
glyphInfo.advance_y = (glyphInfo.advance_y * fontInfo.scale);
|
||||||
|
glyphInfo.offset_x = (glyphInfo.offset_x * fontInfo.scale);
|
||||||
|
glyphInfo.offset_y = (glyphInfo.offset_y * fontInfo.scale);
|
||||||
|
glyphInfo.height = (glyphInfo.height * fontInfo.scale);
|
||||||
|
glyphInfo.width = (glyphInfo.width * fontInfo.scale);
|
||||||
|
|
||||||
|
// store cached glyph
|
||||||
|
font.cachedGlyphs[codePoint] = glyphInfo;
|
||||||
|
return true;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
//retrieve glyph from parent font if any
|
||||||
|
if(font.masterFontHandle.idx != bgfx::invalidHandle)
|
||||||
|
{
|
||||||
|
if(preloadGlyph(font.masterFontHandle, codePoint))
|
||||||
|
{
|
||||||
|
GlyphInfo glyphInfo;
|
||||||
|
getGlyphInfo(font.masterFontHandle, codePoint, glyphInfo);
|
||||||
|
|
||||||
|
glyphInfo.advance_x = (glyphInfo.advance_x * fontInfo.scale);
|
||||||
|
glyphInfo.advance_y = (glyphInfo.advance_y * fontInfo.scale);
|
||||||
|
glyphInfo.offset_x = (glyphInfo.offset_x * fontInfo.scale);
|
||||||
|
glyphInfo.offset_y = (glyphInfo.offset_y * fontInfo.scale);
|
||||||
|
glyphInfo.height = (glyphInfo.height * fontInfo.scale);
|
||||||
|
glyphInfo.width = (glyphInfo.width * fontInfo.scale);
|
||||||
|
|
||||||
|
// store cached glyph
|
||||||
|
font.cachedGlyphs[codePoint] = glyphInfo;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FontInfo& FontManager::getFontInfo(FontHandle handle)
|
||||||
|
{
|
||||||
|
assert(handle.idx != bgfx::invalidHandle);
|
||||||
|
return m_cachedFonts[handle.idx].fontInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FontManager::getGlyphInfo(FontHandle fontHandle, CodePoint_t codePoint, GlyphInfo& outInfo)
|
||||||
|
{
|
||||||
|
GlyphHash_t::iterator iter = m_cachedFonts[fontHandle.idx].cachedGlyphs.find(codePoint);
|
||||||
|
if(iter == m_cachedFonts[fontHandle.idx].cachedGlyphs.end())
|
||||||
|
{
|
||||||
|
if(preloadGlyph(fontHandle, codePoint))
|
||||||
|
{
|
||||||
|
iter = m_cachedFonts[fontHandle.idx].cachedGlyphs.find(codePoint);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outInfo = iter->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ****************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
bool FontManager::addBitmap(GlyphInfo& glyphInfo, const uint8_t* data)
|
||||||
|
{
|
||||||
|
glyphInfo.regionIndex = m_atlas->addRegion((uint16_t) ceil(glyphInfo.width),(uint16_t) ceil(glyphInfo.height), data, bgfx::AtlasRegion::TYPE_GRAY);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
208
examples/common/font/font_manager.h
Normal file
208
examples/common/font/font_manager.h
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
/* Copyright 2013 Jeremie Roy. All rights reserved.
|
||||||
|
* License: http://www.opensource.org/licenses/BSD-2-Clause
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <bgfx.h>
|
||||||
|
#include <bx/handlealloc.h>
|
||||||
|
|
||||||
|
namespace bgfx{ class Atlas; }
|
||||||
|
|
||||||
|
namespace bgfx_font
|
||||||
|
{
|
||||||
|
|
||||||
|
enum FontType
|
||||||
|
{
|
||||||
|
FONT_TYPE_ALPHA = 0x00000100 , // L8
|
||||||
|
//FONT_TYPE_LCD = 0x00000200, // BGRA8
|
||||||
|
//FONT_TYPE_RGBA = 0x00000300, // BGRA8
|
||||||
|
FONT_TYPE_DISTANCE = 0x00000400, // L8
|
||||||
|
FONT_TYPE_DISTANCE_SUBPIXEL = 0x00000500 // L8
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FontInfo
|
||||||
|
{
|
||||||
|
//the font height in pixel
|
||||||
|
uint16_t pixelSize;
|
||||||
|
/// Rendering type used for the font
|
||||||
|
int16_t fontType;
|
||||||
|
|
||||||
|
/// The pixel extents above the baseline in pixels (typically positive)
|
||||||
|
float ascender;
|
||||||
|
/// The extents below the baseline in pixels (typically negative)
|
||||||
|
float descender;
|
||||||
|
/// The spacing in pixels between one row's descent and the next row's ascent
|
||||||
|
float lineGap;
|
||||||
|
/// The thickness of the under/hover/striketrough line in pixels
|
||||||
|
float underline_thickness;
|
||||||
|
/// The position of the underline relatively to the baseline
|
||||||
|
float underline_position;
|
||||||
|
|
||||||
|
//scale to apply to glyph data
|
||||||
|
float scale;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Glyph metrics:
|
||||||
|
// --------------
|
||||||
|
//
|
||||||
|
// xmin xmax
|
||||||
|
// | |
|
||||||
|
// |<-------- width -------->|
|
||||||
|
// | |
|
||||||
|
// | +-------------------------+----------------- ymax
|
||||||
|
// | | ggggggggg ggggg | ^ ^
|
||||||
|
// | | g:::::::::ggg::::g | | |
|
||||||
|
// | | g:::::::::::::::::g | | |
|
||||||
|
// | | g::::::ggggg::::::gg | | |
|
||||||
|
// | | g:::::g g:::::g | | |
|
||||||
|
// offset_x -|-------->| g:::::g g:::::g | offset_y |
|
||||||
|
// | | g:::::g g:::::g | | |
|
||||||
|
// | | g::::::g g:::::g | | |
|
||||||
|
// | | g:::::::ggggg:::::g | | |
|
||||||
|
// | | g::::::::::::::::g | | height
|
||||||
|
// | | gg::::::::::::::g | | |
|
||||||
|
// baseline ---*---------|---- gggggggg::::::g-----*-------- |
|
||||||
|
// / | | g:::::g | |
|
||||||
|
// origin | | gggggg g:::::g | |
|
||||||
|
// | | g:::::gg gg:::::g | |
|
||||||
|
// | | g::::::ggg:::::::g | |
|
||||||
|
// | | gg:::::::::::::g | |
|
||||||
|
// | | ggg::::::ggg | |
|
||||||
|
// | | gggggg | v
|
||||||
|
// | +-------------------------+----------------- ymin
|
||||||
|
// | |
|
||||||
|
// |------------- advance_x ---------->|
|
||||||
|
|
||||||
|
/// Unicode value of a character
|
||||||
|
typedef int32_t CodePoint_t;
|
||||||
|
|
||||||
|
/// A structure that describe a glyph.
|
||||||
|
struct GlyphInfo
|
||||||
|
{
|
||||||
|
/// Index for faster retrieval
|
||||||
|
int32_t glyphIndex;
|
||||||
|
|
||||||
|
/// Glyph's width in pixels.
|
||||||
|
float width;
|
||||||
|
|
||||||
|
/// Glyph's height in pixels.
|
||||||
|
float height;
|
||||||
|
|
||||||
|
/// Glyph's left offset in pixels
|
||||||
|
float offset_x;
|
||||||
|
|
||||||
|
/// Glyph's top offset in pixels
|
||||||
|
/// Remember that this is the distance from the baseline to the top-most
|
||||||
|
/// glyph scan line, upwards y coordinates being positive.
|
||||||
|
float offset_y;
|
||||||
|
|
||||||
|
/// For horizontal text layouts, this is the unscaled horizontal distance in pixels
|
||||||
|
/// used to increment the pen position when the glyph is drawn as part of a string of text.
|
||||||
|
float advance_x;
|
||||||
|
|
||||||
|
/// For vertical text layouts, this is the unscaled vertical distance in pixels
|
||||||
|
/// used to increment the pen position when the glyph is drawn as part of a string of text.
|
||||||
|
float advance_y;
|
||||||
|
|
||||||
|
/// region index in the atlas storing textures
|
||||||
|
uint16_t regionIndex;
|
||||||
|
///32 bits alignment
|
||||||
|
int16_t padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
BGFX_HANDLE(TrueTypeHandle);
|
||||||
|
BGFX_HANDLE(FontHandle);
|
||||||
|
|
||||||
|
class FontManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// create the font manager using an external cube atlas (doesn't take ownership of the atlas)
|
||||||
|
FontManager(bgfx::Atlas* atlas);
|
||||||
|
/// create the font manager and create the texture cube as BGRA8 with linear filtering
|
||||||
|
FontManager(uint32_t textureSideWidth = 512);
|
||||||
|
|
||||||
|
~FontManager();
|
||||||
|
|
||||||
|
/// retrieve the atlas used by the font manager (e.g. to add stuff to it)
|
||||||
|
bgfx::Atlas* getAtlas() { return m_atlas; }
|
||||||
|
|
||||||
|
/// load a TrueType font from a file path
|
||||||
|
/// @return invalid handle if the loading fail
|
||||||
|
TrueTypeHandle loadTrueTypeFromFile(const char* fontPath);
|
||||||
|
|
||||||
|
/// load a TrueType font from a given buffer.
|
||||||
|
/// the buffer is copied and thus can be freed or reused after this call
|
||||||
|
/// @return invalid handle if the loading fail
|
||||||
|
TrueTypeHandle loadTrueTypeFromMemory(const uint8_t* buffer, uint32_t size);
|
||||||
|
|
||||||
|
/// unload a TrueType font (free font memory) but keep loaded glyphs
|
||||||
|
void unloadTrueType(TrueTypeHandle handle);
|
||||||
|
|
||||||
|
/// return a font whose height is a fixed pixel size
|
||||||
|
FontHandle createFontByPixelSize(TrueTypeHandle handle, uint32_t typefaceIndex, uint32_t pixelSize, FontType fontType = FONT_TYPE_ALPHA);
|
||||||
|
|
||||||
|
/// return a scaled child font whose height is a fixed pixel size
|
||||||
|
FontHandle createScaledFontToPixelSize(FontHandle baseFontHandle, uint32_t pixelSize);
|
||||||
|
|
||||||
|
/// load a baked font (the set of glyph is fixed)
|
||||||
|
/// @return INVALID_HANDLE if the loading fail
|
||||||
|
FontHandle loadBakedFontFromFile(const char* imagePath, const char* descriptorPath);
|
||||||
|
|
||||||
|
/// load a baked font (the set of glyph is fixed)
|
||||||
|
/// @return INVALID_HANDLE if the loading fail
|
||||||
|
FontHandle loadBakedFontFromMemory(const uint8_t* imageBuffer, uint32_t imageSize, const uint8_t* descriptorBuffer, uint32_t descriptorSize);
|
||||||
|
|
||||||
|
/// destroy a font (truetype or baked)
|
||||||
|
void destroyFont(FontHandle _handle);
|
||||||
|
|
||||||
|
/// Preload a set of glyphs from a TrueType file
|
||||||
|
/// @return true if every glyph could be preloaded, false otherwise
|
||||||
|
/// if the Font is a baked font, this only do validation on the characters
|
||||||
|
bool preloadGlyph(FontHandle handle, const wchar_t* _string);
|
||||||
|
|
||||||
|
/// Preload a single glyph, return true on success
|
||||||
|
bool preloadGlyph(FontHandle handle, CodePoint_t character);
|
||||||
|
|
||||||
|
/// bake a font to disk (the set of preloaded glyph)
|
||||||
|
/// @return true if the baking succeed, false otherwise
|
||||||
|
bool saveBakedFont(FontHandle handle, const char* fontDirectory, const char* fontName );
|
||||||
|
|
||||||
|
/// return the font descriptor of a font
|
||||||
|
/// @remark the handle is required to be valid
|
||||||
|
const FontInfo& getFontInfo(FontHandle handle);
|
||||||
|
|
||||||
|
/// Return the rendering informations about the glyph region
|
||||||
|
/// Load the glyph from a TrueType font if possible
|
||||||
|
/// @return true if the Glyph is available
|
||||||
|
bool getGlyphInfo(FontHandle fontHandle, CodePoint_t codePoint, GlyphInfo& outInfo);
|
||||||
|
|
||||||
|
GlyphInfo& getBlackGlyph(){ return m_blackGlyph; }
|
||||||
|
|
||||||
|
class TrueTypeFont; //public to shut off Intellisense warning
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct CachedFont;
|
||||||
|
struct CachedFile
|
||||||
|
{
|
||||||
|
uint8_t* buffer;
|
||||||
|
uint32_t bufferSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
void init();
|
||||||
|
bool addBitmap(GlyphInfo& glyphInfo, const uint8_t* data);
|
||||||
|
|
||||||
|
bool m_ownAtlas;
|
||||||
|
(bgfx::Atlas* m_atlas;
|
||||||
|
|
||||||
|
bx::HandleAlloc m_fontHandles;
|
||||||
|
CachedFont* m_cachedFonts;
|
||||||
|
|
||||||
|
bx::HandleAlloc m_filesHandles;
|
||||||
|
CachedFile* m_cachedFiles;
|
||||||
|
|
||||||
|
GlyphInfo m_blackGlyph;
|
||||||
|
|
||||||
|
//temporary buffer to raster glyph
|
||||||
|
uint8_t* m_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
16
examples/common/font/fs_font_basic.sc
Normal file
16
examples/common/font/fs_font_basic.sc
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
$input v_color0, v_texcoord0
|
||||||
|
|
||||||
|
#include "../../common/common.sh"
|
||||||
|
|
||||||
|
SAMPLERCUBE(u_texColor, 0);
|
||||||
|
|
||||||
|
uniform float u_inverse_gamma;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 color = textureCube(u_texColor, v_texcoord0.xyz);
|
||||||
|
int index = int(v_texcoord0.w*4.0 + 0.5);
|
||||||
|
float a = color.bgra[index];
|
||||||
|
//a = pow(a, u_inverse_gamma); //I'll deal with gamma later
|
||||||
|
gl_FragColor = vec4(v_color0.rgb, v_color0.a * a);
|
||||||
|
}
|
27
examples/common/font/fs_font_distance_field.sc
Normal file
27
examples/common/font/fs_font_distance_field.sc
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
$input v_color0, v_texcoord0
|
||||||
|
|
||||||
|
#include "../../common/common.sh"
|
||||||
|
|
||||||
|
SAMPLERCUBE(u_texColor, 0);
|
||||||
|
|
||||||
|
uniform float u_inverse_gamma;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 color = textureCube(u_texColor, v_texcoord0.xyz);
|
||||||
|
int index = int(v_texcoord0.w*4.0 + 0.5);
|
||||||
|
float distance = color.bgra[index];
|
||||||
|
|
||||||
|
float dx = length(dFdx(v_texcoord0.xyz));
|
||||||
|
float dy = length(dFdy(v_texcoord0.xyz));
|
||||||
|
float w = 16.0*0.5*(dx+dy);
|
||||||
|
|
||||||
|
// alternatives that seems to give identical results
|
||||||
|
//float w = 16.0*max(dx,dy);
|
||||||
|
//float w = 16.0*length(vec2(dx,dy))/sqrt(2.0);
|
||||||
|
//float w = 16.0*length(fwidth(v_texcoord0.xyz))/sqrt(2.0);
|
||||||
|
|
||||||
|
float a = smoothstep(0.5-w, 0.5+w, distance);
|
||||||
|
//a = pow(a, u_inverse_gamma); //I'll deal with gamma later
|
||||||
|
gl_FragColor = vec4(v_color0.rgb, v_color0.a*a);
|
||||||
|
}
|
40
examples/common/font/fs_font_distance_field_subpixel.sc
Normal file
40
examples/common/font/fs_font_distance_field_subpixel.sc
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
$input v_color0, v_texcoord0
|
||||||
|
|
||||||
|
#include "../../common/common.sh"
|
||||||
|
|
||||||
|
SAMPLERCUBE(u_texColor, 0);
|
||||||
|
|
||||||
|
uniform float u_inverse_gamma;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
int index = int(v_texcoord0.w*4.0 + 0.5);
|
||||||
|
vec3 dx3 = dFdx(v_texcoord0.xyz);
|
||||||
|
vec3 dy3 = dFdy(v_texcoord0.xyz);
|
||||||
|
vec3 decal = 0.166667 * dx3;
|
||||||
|
vec3 sampleLeft = v_texcoord0.xyz - decal;
|
||||||
|
vec3 sampleRight = v_texcoord0.xyz + decal;
|
||||||
|
|
||||||
|
float left_dist = textureCube(u_texColor, sampleLeft).bgra[index];
|
||||||
|
float right_dist = textureCube(u_texColor, sampleRight).bgra[index];
|
||||||
|
|
||||||
|
//vec3 centerUV = 0.5 * (sampleLeft + sampleRight);
|
||||||
|
//float dist = textureCube(u_texColor, centerUV).bgra[index];
|
||||||
|
float dist = 0.5 * (left_dist + right_dist);
|
||||||
|
|
||||||
|
float dx = length(dx3);
|
||||||
|
float dy = length(dy3);
|
||||||
|
float w = 16.0*0.5*(dx+dy);
|
||||||
|
|
||||||
|
vec3 sub_color = smoothstep(0.5 -w, 0.5 + w, vec3(left_dist, dist, right_dist));
|
||||||
|
gl_FragColor.rgb = sub_color*v_color0.a;
|
||||||
|
//gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(u_inverse_gamma,u_inverse_gamma,u_inverse_gamma));
|
||||||
|
gl_FragColor.a = dist*v_color0.a;
|
||||||
|
|
||||||
|
//AR,AG,AB are the intensities gotten from the subpixel rendering engine.
|
||||||
|
//BR,BG,BB are the old background pixels.
|
||||||
|
//DR,DG,DB are the new background pixels.
|
||||||
|
//DR = A*AR*R + (1-(A*AR))*BR
|
||||||
|
//DG = A*AG*G + (1-(A*AG))*BG
|
||||||
|
//DB = A*AB*B + (1-(A*AB))*BB
|
||||||
|
}
|
17
examples/common/font/makefile
Normal file
17
examples/common/font/makefile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#
|
||||||
|
# Copyright 2013 Roy Jeremie. All rights reserved.
|
||||||
|
# License: http://www.opensource.org/licenses/BSD-2-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
BGFX_DIR=../../..
|
||||||
|
RUNTIME_DIR=$(BGFX_DIR)/examples/runtime
|
||||||
|
BUILD_DIR=../../../.build
|
||||||
|
|
||||||
|
include $(BGFX_DIR)/premake/shader.mk
|
||||||
|
|
||||||
|
rebuild:
|
||||||
|
@make -s --no-print-directory TARGET=0 clean all
|
||||||
|
@make -s --no-print-directory TARGET=1 clean all
|
||||||
|
@make -s --no-print-directory TARGET=2 clean all
|
||||||
|
@make -s --no-print-directory TARGET=3 clean all
|
||||||
|
@make -s --no-print-directory TARGET=4 clean all
|
812
examples/common/font/text_buffer_manager.cpp
Normal file
812
examples/common/font/text_buffer_manager.cpp
Normal file
@ -0,0 +1,812 @@
|
|||||||
|
/* Copyright 2013 Jeremie Roy. All rights reserved.
|
||||||
|
* License: http://www.opensource.org/licenses/BSD-2-Clause
|
||||||
|
*/
|
||||||
|
#include "text_buffer_manager.h"
|
||||||
|
#include "../cube_atlas.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stddef.h> /* offsetof */
|
||||||
|
|
||||||
|
namespace bgfx_font
|
||||||
|
{
|
||||||
|
|
||||||
|
const uint16_t MAX_TEXT_BUFFER_COUNT = 64;
|
||||||
|
|
||||||
|
long int fsize(FILE* _file)
|
||||||
|
{
|
||||||
|
long int pos = ftell(_file);
|
||||||
|
fseek(_file, 0L, SEEK_END);
|
||||||
|
long int size = ftell(_file);
|
||||||
|
fseek(_file, pos, SEEK_SET);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const bgfx::Memory* loadShader(const char* _shaderPath, const char* _shaderName)
|
||||||
|
{
|
||||||
|
char out[512];
|
||||||
|
strcpy(out, _shaderPath);
|
||||||
|
strcat(out, _shaderName);
|
||||||
|
strcat(out, ".bin");
|
||||||
|
|
||||||
|
FILE* file = fopen(out, "rb");
|
||||||
|
if (NULL != file)
|
||||||
|
{
|
||||||
|
uint32_t size = (uint32_t)fsize(file);
|
||||||
|
const bgfx::Memory* mem = bgfx::alloc(size+1);
|
||||||
|
/*size_t ignore =*/ fread(mem->data, 1, size, file);
|
||||||
|
/*BX_UNUSED(ignore);*/
|
||||||
|
fclose(file);
|
||||||
|
mem->data[mem->size-1] = '\0';
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Table from Flexible and Economical UTF-8 Decoder
|
||||||
|
// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||||
|
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
||||||
|
|
||||||
|
static const uint8_t utf8d[] = {
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
|
||||||
|
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
|
||||||
|
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
|
||||||
|
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
|
||||||
|
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
|
||||||
|
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
|
||||||
|
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
|
||||||
|
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
|
||||||
|
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
|
||||||
|
};
|
||||||
|
|
||||||
|
#define UTF8_ACCEPT 0
|
||||||
|
#define UTF8_REJECT 1
|
||||||
|
|
||||||
|
inline uint32_t utf8_decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
|
||||||
|
uint32_t type = utf8d[byte];
|
||||||
|
|
||||||
|
*codep = (*state != UTF8_ACCEPT) ?
|
||||||
|
(byte & 0x3fu) | (*codep << 6) :
|
||||||
|
(0xff >> type) & (byte);
|
||||||
|
|
||||||
|
*state = utf8d[256 + *state*16 + type];
|
||||||
|
return *state;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int utf8_strlen(uint8_t* s, size_t* count) {
|
||||||
|
uint32_t codepoint;
|
||||||
|
uint32_t state = 0;
|
||||||
|
|
||||||
|
for (*count = 0; *s; ++s)
|
||||||
|
if (!utf8_decode(&state, &codepoint, *s))
|
||||||
|
*count += 1;
|
||||||
|
|
||||||
|
return state != UTF8_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TextBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// TextBuffer is bound to a fontManager for glyph retrieval
|
||||||
|
/// @remark the ownership of the manager is not taken
|
||||||
|
TextBuffer(FontManager* fontManager);
|
||||||
|
~TextBuffer();
|
||||||
|
|
||||||
|
void setStyle(uint32_t flags = STYLE_NORMAL) { m_styleFlags = flags; }
|
||||||
|
void setTextColor(uint32_t rgba = 0x000000FF) { m_textColor = toABGR(rgba); }
|
||||||
|
void setBackgroundColor(uint32_t rgba = 0x000000FF) { m_backgroundColor = toABGR(rgba); }
|
||||||
|
|
||||||
|
void setOverlineColor(uint32_t rgba = 0x000000FF) { m_overlineColor = toABGR(rgba); }
|
||||||
|
void setUnderlineColor(uint32_t rgba = 0x000000FF) { m_underlineColor = toABGR(rgba); }
|
||||||
|
void setStrikeThroughColor(uint32_t rgba = 0x000000FF) { m_strikeThroughColor = toABGR(rgba); }
|
||||||
|
|
||||||
|
void setPenPosition(float x, float /*y*/) { m_penX = x; };// m_penY = y; }
|
||||||
|
|
||||||
|
/// return the size of the text
|
||||||
|
//Rectangle measureText(FontHandle fontHandle, const char * _string);
|
||||||
|
//Rectangle measureText(FontHandle fontHandle, const wchar_t * _string);
|
||||||
|
|
||||||
|
/// append an ASCII/utf-8 string to the buffer using current pen position and color
|
||||||
|
void appendText(FontHandle fontHandle, const char * _string);
|
||||||
|
|
||||||
|
/// append a wide char unicode string to the buffer using current pen position and color
|
||||||
|
void appendText(FontHandle fontHandle, const wchar_t * _string);
|
||||||
|
|
||||||
|
/// Clear the text buffer and reset its state (pen/color)
|
||||||
|
void clearTextBuffer();
|
||||||
|
|
||||||
|
/// get pointer to the vertex buffer to submit it to the graphic card
|
||||||
|
const uint8_t* getVertexBuffer(){ return (uint8_t*) m_vertexBuffer; }
|
||||||
|
/// number of vertex in the vertex buffer
|
||||||
|
uint32_t getVertexCount(){ return m_vertexCount; }
|
||||||
|
/// size in bytes of a vertex
|
||||||
|
uint32_t getVertexSize(){ return sizeof(TextVertex); }
|
||||||
|
|
||||||
|
/// get a pointer to the index buffer to submit it to the graphic
|
||||||
|
const uint16_t* getIndexBuffer(){ return m_indexBuffer; }
|
||||||
|
/// number of index in the index buffer
|
||||||
|
uint32_t getIndexCount(){ return m_indexCount; }
|
||||||
|
/// size in bytes of an index
|
||||||
|
uint32_t getIndexSize(){ return sizeof(uint16_t); }
|
||||||
|
|
||||||
|
uint32_t getTextColor(){ return toABGR(m_textColor); }
|
||||||
|
private:
|
||||||
|
void appendGlyph(CodePoint_t codePoint, const FontInfo& font, const GlyphInfo& glyphInfo);
|
||||||
|
void verticalCenterLastLine(float txtDecalY, float top, float bottom);
|
||||||
|
uint32_t toABGR(uint32_t rgba)
|
||||||
|
{
|
||||||
|
return (((rgba >> 0) & 0xff) << 24) |
|
||||||
|
(((rgba >> 8) & 0xff) << 16) |
|
||||||
|
(((rgba >> 16) & 0xff) << 8) |
|
||||||
|
(((rgba >> 24) & 0xff) << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const size_t MAX_BUFFERED_CHARACTERS = 8192;
|
||||||
|
|
||||||
|
uint32_t m_styleFlags;
|
||||||
|
|
||||||
|
// color states
|
||||||
|
uint32_t m_textColor;
|
||||||
|
|
||||||
|
uint32_t m_backgroundColor;
|
||||||
|
uint32_t m_overlineColor;
|
||||||
|
uint32_t m_underlineColor;
|
||||||
|
uint32_t m_strikeThroughColor;
|
||||||
|
|
||||||
|
//position states
|
||||||
|
float m_penX;
|
||||||
|
float m_penY;
|
||||||
|
|
||||||
|
float m_originX;
|
||||||
|
float m_originY;
|
||||||
|
|
||||||
|
float m_lineAscender;
|
||||||
|
float m_lineDescender;
|
||||||
|
float m_lineGap;
|
||||||
|
|
||||||
|
///
|
||||||
|
FontManager* m_fontManager;
|
||||||
|
|
||||||
|
void setVertex(size_t i, float x, float y, uint32_t rgba, uint8_t style = STYLE_NORMAL)
|
||||||
|
{
|
||||||
|
m_vertexBuffer[i].x = x;
|
||||||
|
m_vertexBuffer[i].y = y;
|
||||||
|
m_vertexBuffer[i].rgba = rgba;
|
||||||
|
m_styleBuffer[i] = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TextVertex
|
||||||
|
{
|
||||||
|
float x,y;
|
||||||
|
int16_t u,v,w,t;
|
||||||
|
uint32_t rgba;
|
||||||
|
};
|
||||||
|
|
||||||
|
TextVertex* m_vertexBuffer;
|
||||||
|
uint16_t* m_indexBuffer;
|
||||||
|
uint8_t* m_styleBuffer;
|
||||||
|
|
||||||
|
size_t m_vertexCount;
|
||||||
|
size_t m_indexCount;
|
||||||
|
size_t m_lineStartIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TextBuffer::TextBuffer(FontManager* fontManager)
|
||||||
|
{
|
||||||
|
m_styleFlags = STYLE_NORMAL;
|
||||||
|
//0xAABBGGRR
|
||||||
|
m_textColor = 0xFFFFFFFF;
|
||||||
|
m_backgroundColor = 0xFFFFFFFF;
|
||||||
|
m_backgroundColor = 0xFFFFFFFF;
|
||||||
|
m_overlineColor = 0xFFFFFFFF;
|
||||||
|
m_underlineColor = 0xFFFFFFFF;
|
||||||
|
m_strikeThroughColor = 0xFFFFFFFF;
|
||||||
|
m_penX = 0;
|
||||||
|
m_penY = 0;
|
||||||
|
m_originX = 0;
|
||||||
|
m_originY = 0;
|
||||||
|
m_lineAscender = 0;
|
||||||
|
m_lineDescender = 0;
|
||||||
|
m_lineGap = 0;
|
||||||
|
m_fontManager = fontManager;
|
||||||
|
|
||||||
|
|
||||||
|
m_vertexBuffer = new TextVertex[MAX_BUFFERED_CHARACTERS * 4];
|
||||||
|
m_indexBuffer = new uint16_t[MAX_BUFFERED_CHARACTERS * 6];
|
||||||
|
m_styleBuffer = new uint8_t[MAX_BUFFERED_CHARACTERS * 4];
|
||||||
|
m_vertexCount = 0;
|
||||||
|
m_indexCount = 0;
|
||||||
|
m_lineStartIndex = 0;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TextBuffer::~TextBuffer()
|
||||||
|
{
|
||||||
|
delete[] m_vertexBuffer;
|
||||||
|
delete[] m_indexBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBuffer::appendText(FontHandle fontHandle, const char * _string)
|
||||||
|
{
|
||||||
|
GlyphInfo glyph;
|
||||||
|
const FontInfo& font = m_fontManager->getFontInfo(fontHandle);
|
||||||
|
|
||||||
|
if(m_vertexCount == 0)
|
||||||
|
{
|
||||||
|
m_originX = m_penX;
|
||||||
|
m_originY = m_penY;
|
||||||
|
m_lineDescender = 0;// font.descender;
|
||||||
|
m_lineAscender = 0;//font.ascender;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t codepoint;
|
||||||
|
uint32_t state = 0;
|
||||||
|
|
||||||
|
for (; *_string; ++_string)
|
||||||
|
if (!utf8_decode(&state, &codepoint, *_string))
|
||||||
|
{
|
||||||
|
if(m_fontManager->getGlyphInfo(fontHandle, (CodePoint_t)codepoint, glyph))
|
||||||
|
{
|
||||||
|
appendGlyph((CodePoint_t)codepoint, font, glyph);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
assert(false && "Glyph not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//printf("U+%04X\n", codepoint);
|
||||||
|
|
||||||
|
if (state != UTF8_ACCEPT)
|
||||||
|
{
|
||||||
|
// assert(false && "The string is not well-formed");
|
||||||
|
return; //"The string is not well-formed\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBuffer::appendText(FontHandle fontHandle, const wchar_t * _string)
|
||||||
|
{
|
||||||
|
GlyphInfo glyph;
|
||||||
|
const FontInfo& font = m_fontManager->getFontInfo(fontHandle);
|
||||||
|
|
||||||
|
if(m_vertexCount == 0)
|
||||||
|
{
|
||||||
|
m_originX = m_penX;
|
||||||
|
m_originY = m_penY;
|
||||||
|
m_lineDescender = 0;// font.descender;
|
||||||
|
m_lineAscender = 0;//font.ascender;
|
||||||
|
m_lineGap = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//parse string
|
||||||
|
for( size_t i=0, end = wcslen(_string) ; i < end; ++i )
|
||||||
|
{
|
||||||
|
//if glyph cached, continue
|
||||||
|
uint32_t codePoint = _string[i];
|
||||||
|
if(m_fontManager->getGlyphInfo(fontHandle, codePoint, glyph))
|
||||||
|
{
|
||||||
|
appendGlyph(codePoint, font, glyph);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
assert(false && "Glyph not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
TextBuffer::Rectangle TextBuffer::measureText(FontHandle fontHandle, const char * _string)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TextBuffer::Rectangle TextBuffer::measureText(FontHandle fontHandle, const wchar_t * _string)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void TextBuffer::clearTextBuffer()
|
||||||
|
{
|
||||||
|
m_vertexCount = 0;
|
||||||
|
m_indexCount = 0;
|
||||||
|
m_lineStartIndex = 0;
|
||||||
|
m_lineAscender = 0;
|
||||||
|
m_lineDescender = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBuffer::appendGlyph(CodePoint_t codePoint, const FontInfo& font, const GlyphInfo& glyphInfo)
|
||||||
|
{
|
||||||
|
//handle newlines
|
||||||
|
if(codePoint == L'\n' )
|
||||||
|
{
|
||||||
|
m_penX = m_originX;
|
||||||
|
m_penY -= m_lineDescender;
|
||||||
|
m_penY += m_lineGap;
|
||||||
|
m_lineDescender = 0;
|
||||||
|
m_lineAscender = 0;
|
||||||
|
m_lineStartIndex = m_vertexCount;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( font.ascender > m_lineAscender || (font.descender < m_lineDescender) )
|
||||||
|
{
|
||||||
|
if( font.descender < m_lineDescender )
|
||||||
|
{
|
||||||
|
m_lineDescender = font.descender;
|
||||||
|
m_lineGap = font.lineGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
float txtDecals = (font.ascender - m_lineAscender);
|
||||||
|
m_lineAscender = font.ascender;
|
||||||
|
m_lineGap = font.lineGap;
|
||||||
|
|
||||||
|
m_penY += txtDecals;
|
||||||
|
verticalCenterLastLine((txtDecals), (m_penY - m_lineAscender), (m_penY - m_lineDescender+m_lineGap));
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle kerning
|
||||||
|
float kerning = 0;
|
||||||
|
/*
|
||||||
|
if( previous && markup->font->kerning )
|
||||||
|
{
|
||||||
|
kerning = texture_glyph_get_kerning( glyph, previous );
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
m_penX += kerning * font.scale;
|
||||||
|
|
||||||
|
GlyphInfo& blackGlyph = m_fontManager->getBlackGlyph();
|
||||||
|
|
||||||
|
if( m_styleFlags & STYLE_BACKGROUND && m_backgroundColor & 0xFF000000)
|
||||||
|
{
|
||||||
|
float x0 = ( m_penX - kerning );
|
||||||
|
float y0 = ( m_penY - m_lineAscender);
|
||||||
|
float x1 = ( (float)x0 + (glyphInfo.advance_x));
|
||||||
|
float y1 = ( m_penY - m_lineDescender + m_lineGap );
|
||||||
|
|
||||||
|
m_fontManager->getAtlas()->packUV(blackGlyph.regionIndex, (uint8_t*)m_vertexBuffer,sizeof(TextVertex) *m_vertexCount + offsetof(TextVertex, u), sizeof(TextVertex));
|
||||||
|
|
||||||
|
setVertex(m_vertexCount+0, x0, y0, m_backgroundColor,STYLE_BACKGROUND);
|
||||||
|
setVertex(m_vertexCount+1, x0, y1, m_backgroundColor,STYLE_BACKGROUND);
|
||||||
|
setVertex(m_vertexCount+2, x1, y1, m_backgroundColor,STYLE_BACKGROUND);
|
||||||
|
setVertex(m_vertexCount+3, x1, y0, m_backgroundColor,STYLE_BACKGROUND);
|
||||||
|
|
||||||
|
m_indexBuffer[m_indexCount + 0] = m_vertexCount+0;
|
||||||
|
m_indexBuffer[m_indexCount + 1] = m_vertexCount+1;
|
||||||
|
m_indexBuffer[m_indexCount + 2] = m_vertexCount+2;
|
||||||
|
m_indexBuffer[m_indexCount + 3] = m_vertexCount+0;
|
||||||
|
m_indexBuffer[m_indexCount + 4] = m_vertexCount+2;
|
||||||
|
m_indexBuffer[m_indexCount + 5] = m_vertexCount+3;
|
||||||
|
m_vertexCount += 4;
|
||||||
|
m_indexCount += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_styleFlags & STYLE_UNDERLINE && m_underlineColor & 0xFF000000)
|
||||||
|
{
|
||||||
|
float x0 = ( m_penX - kerning );
|
||||||
|
float y0 = (m_penY - m_lineDescender/2 );
|
||||||
|
float x1 = ( (float)x0 + (glyphInfo.advance_x));
|
||||||
|
float y1 = y0+font.underline_thickness;
|
||||||
|
|
||||||
|
m_fontManager->getAtlas()->packUV(blackGlyph.regionIndex, (uint8_t*)m_vertexBuffer,sizeof(TextVertex) *m_vertexCount + offsetof(TextVertex, u), sizeof(TextVertex));
|
||||||
|
|
||||||
|
setVertex(m_vertexCount+0, x0, y0, m_underlineColor,STYLE_UNDERLINE);
|
||||||
|
setVertex(m_vertexCount+1, x0, y1, m_underlineColor,STYLE_UNDERLINE);
|
||||||
|
setVertex(m_vertexCount+2, x1, y1, m_underlineColor,STYLE_UNDERLINE);
|
||||||
|
setVertex(m_vertexCount+3, x1, y0, m_underlineColor,STYLE_UNDERLINE);
|
||||||
|
|
||||||
|
m_indexBuffer[m_indexCount + 0] = m_vertexCount+0;
|
||||||
|
m_indexBuffer[m_indexCount + 1] = m_vertexCount+1;
|
||||||
|
m_indexBuffer[m_indexCount + 2] = m_vertexCount+2;
|
||||||
|
m_indexBuffer[m_indexCount + 3] = m_vertexCount+0;
|
||||||
|
m_indexBuffer[m_indexCount + 4] = m_vertexCount+2;
|
||||||
|
m_indexBuffer[m_indexCount + 5] = m_vertexCount+3;
|
||||||
|
m_vertexCount += 4;
|
||||||
|
m_indexCount += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_styleFlags & STYLE_OVERLINE && m_overlineColor & 0xFF000000)
|
||||||
|
{
|
||||||
|
float x0 = ( m_penX - kerning );
|
||||||
|
float y0 = (m_penY - font.ascender );
|
||||||
|
float x1 = ( (float)x0 + (glyphInfo.advance_x));
|
||||||
|
float y1 = y0+font.underline_thickness;
|
||||||
|
|
||||||
|
m_fontManager->getAtlas()->packUV(blackGlyph.regionIndex, (uint8_t*)m_vertexBuffer,sizeof(TextVertex) *m_vertexCount + offsetof(TextVertex, u), sizeof(TextVertex));
|
||||||
|
|
||||||
|
setVertex(m_vertexCount+0, x0, y0, m_overlineColor,STYLE_OVERLINE);
|
||||||
|
setVertex(m_vertexCount+1, x0, y1, m_overlineColor,STYLE_OVERLINE);
|
||||||
|
setVertex(m_vertexCount+2, x1, y1, m_overlineColor,STYLE_OVERLINE);
|
||||||
|
setVertex(m_vertexCount+3, x1, y0, m_overlineColor,STYLE_OVERLINE);
|
||||||
|
|
||||||
|
m_indexBuffer[m_indexCount + 0] = m_vertexCount+0;
|
||||||
|
m_indexBuffer[m_indexCount + 1] = m_vertexCount+1;
|
||||||
|
m_indexBuffer[m_indexCount + 2] = m_vertexCount+2;
|
||||||
|
m_indexBuffer[m_indexCount + 3] = m_vertexCount+0;
|
||||||
|
m_indexBuffer[m_indexCount + 4] = m_vertexCount+2;
|
||||||
|
m_indexBuffer[m_indexCount + 5] = m_vertexCount+3;
|
||||||
|
m_vertexCount += 4;
|
||||||
|
m_indexCount += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( m_styleFlags & STYLE_STRIKE_THROUGH && m_strikeThroughColor & 0xFF000000)
|
||||||
|
{
|
||||||
|
float x0 = ( m_penX - kerning );
|
||||||
|
float y0 = (m_penY - font.ascender/3 );
|
||||||
|
float x1 = ( (float)x0 + (glyphInfo.advance_x) );
|
||||||
|
float y1 = y0+font.underline_thickness;
|
||||||
|
|
||||||
|
m_fontManager->getAtlas()->packUV(blackGlyph.regionIndex, (uint8_t*)m_vertexBuffer,sizeof(TextVertex) *m_vertexCount + offsetof(TextVertex, u), sizeof(TextVertex));
|
||||||
|
|
||||||
|
setVertex(m_vertexCount+0, x0, y0, m_strikeThroughColor,STYLE_STRIKE_THROUGH);
|
||||||
|
setVertex(m_vertexCount+1, x0, y1, m_strikeThroughColor,STYLE_STRIKE_THROUGH);
|
||||||
|
setVertex(m_vertexCount+2, x1, y1, m_strikeThroughColor,STYLE_STRIKE_THROUGH);
|
||||||
|
setVertex(m_vertexCount+3, x1, y0, m_strikeThroughColor,STYLE_STRIKE_THROUGH);
|
||||||
|
|
||||||
|
m_indexBuffer[m_indexCount + 0] = m_vertexCount+0;
|
||||||
|
m_indexBuffer[m_indexCount + 1] = m_vertexCount+1;
|
||||||
|
m_indexBuffer[m_indexCount + 2] = m_vertexCount+2;
|
||||||
|
m_indexBuffer[m_indexCount + 3] = m_vertexCount+0;
|
||||||
|
m_indexBuffer[m_indexCount + 4] = m_vertexCount+2;
|
||||||
|
m_indexBuffer[m_indexCount + 5] = m_vertexCount+3;
|
||||||
|
m_vertexCount += 4;
|
||||||
|
m_indexCount += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//handle glyph
|
||||||
|
float x0_precise = m_penX + (glyphInfo.offset_x);
|
||||||
|
float x0 = ( x0_precise);
|
||||||
|
float y0 = ( m_penY + (glyphInfo.offset_y));
|
||||||
|
float x1 = ( x0 + glyphInfo.width );
|
||||||
|
float y1 = ( y0 + glyphInfo.height );
|
||||||
|
|
||||||
|
m_fontManager->getAtlas()->packUV(glyphInfo.regionIndex, (uint8_t*)m_vertexBuffer, sizeof(TextVertex) *m_vertexCount + offsetof(TextVertex, u), sizeof(TextVertex));
|
||||||
|
|
||||||
|
setVertex(m_vertexCount+0, x0, y0, m_textColor);
|
||||||
|
setVertex(m_vertexCount+1, x0, y1, m_textColor);
|
||||||
|
setVertex(m_vertexCount+2, x1, y1, m_textColor);
|
||||||
|
setVertex(m_vertexCount+3, x1, y0, m_textColor);
|
||||||
|
|
||||||
|
m_indexBuffer[m_indexCount + 0] = m_vertexCount+0;
|
||||||
|
m_indexBuffer[m_indexCount + 1] = m_vertexCount+1;
|
||||||
|
m_indexBuffer[m_indexCount + 2] = m_vertexCount+2;
|
||||||
|
m_indexBuffer[m_indexCount + 3] = m_vertexCount+0;
|
||||||
|
m_indexBuffer[m_indexCount + 4] = m_vertexCount+2;
|
||||||
|
m_indexBuffer[m_indexCount + 5] = m_vertexCount+3;
|
||||||
|
m_vertexCount += 4;
|
||||||
|
m_indexCount += 6;
|
||||||
|
|
||||||
|
//TODO see what to do when doing subpixel rendering
|
||||||
|
m_penX += glyphInfo.advance_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBuffer::verticalCenterLastLine(float dy, float top, float bottom)
|
||||||
|
{
|
||||||
|
for( size_t i=m_lineStartIndex; i < m_vertexCount; i+=4 )
|
||||||
|
{
|
||||||
|
if( m_styleBuffer[i] == STYLE_BACKGROUND)
|
||||||
|
{
|
||||||
|
m_vertexBuffer[i+0].y = top;
|
||||||
|
m_vertexBuffer[i+1].y = bottom;
|
||||||
|
m_vertexBuffer[i+2].y = bottom;
|
||||||
|
m_vertexBuffer[i+3].y = top;
|
||||||
|
}else{
|
||||||
|
m_vertexBuffer[i+0].y += dy;
|
||||||
|
m_vertexBuffer[i+1].y += dy;
|
||||||
|
m_vertexBuffer[i+2].y += dy;
|
||||||
|
m_vertexBuffer[i+3].y += dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ****************************************************************
|
||||||
|
|
||||||
|
TextBufferManager::TextBufferManager(FontManager* fontManager):m_fontManager(fontManager), m_textBufferHandles(MAX_TEXT_BUFFER_COUNT)
|
||||||
|
{
|
||||||
|
m_textBuffers = new BufferCache[MAX_TEXT_BUFFER_COUNT];
|
||||||
|
}
|
||||||
|
|
||||||
|
TextBufferManager::~TextBufferManager()
|
||||||
|
{
|
||||||
|
assert(m_textBufferHandles.getNumHandles() == 0 && "All the text buffers must be destroyed before destroying the manager");
|
||||||
|
delete[] m_textBuffers;
|
||||||
|
|
||||||
|
bgfx::destroyUniform(m_u_texColor);
|
||||||
|
bgfx::destroyUniform(m_u_inverse_gamma);
|
||||||
|
|
||||||
|
bgfx::destroyProgram(m_basicProgram);
|
||||||
|
bgfx::destroyProgram(m_distanceProgram);
|
||||||
|
bgfx::destroyProgram(m_distanceSubpixelProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::init(const char* shaderPath)
|
||||||
|
{
|
||||||
|
m_vertexDecl.begin();
|
||||||
|
m_vertexDecl.add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float);
|
||||||
|
m_vertexDecl.add(bgfx::Attrib::TexCoord0, 4, bgfx::AttribType::Int16, true);
|
||||||
|
m_vertexDecl.add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true);
|
||||||
|
m_vertexDecl.end();
|
||||||
|
|
||||||
|
m_u_texColor = bgfx::createUniform("u_texColor", bgfx::UniformType::Uniform1iv);
|
||||||
|
m_u_inverse_gamma = bgfx::createUniform("u_inverse_gamma", bgfx::UniformType::Uniform1f);
|
||||||
|
|
||||||
|
const bgfx::Memory* mem;
|
||||||
|
mem = loadShader(shaderPath, "vs_font_basic");
|
||||||
|
bgfx::VertexShaderHandle vsh = bgfx::createVertexShader(mem);
|
||||||
|
mem = loadShader(shaderPath, "fs_font_basic");
|
||||||
|
bgfx::FragmentShaderHandle fsh = bgfx::createFragmentShader(mem);
|
||||||
|
m_basicProgram = bgfx::createProgram(vsh, fsh);
|
||||||
|
bgfx::destroyVertexShader(vsh);
|
||||||
|
bgfx::destroyFragmentShader(fsh);
|
||||||
|
|
||||||
|
mem = loadShader(shaderPath, "vs_font_distance_field");
|
||||||
|
vsh = bgfx::createVertexShader(mem);
|
||||||
|
mem = loadShader(shaderPath, "fs_font_distance_field");
|
||||||
|
fsh = bgfx::createFragmentShader(mem);
|
||||||
|
m_distanceProgram = bgfx::createProgram(vsh, fsh);
|
||||||
|
bgfx::destroyVertexShader(vsh);
|
||||||
|
bgfx::destroyFragmentShader(fsh);
|
||||||
|
|
||||||
|
mem = loadShader(shaderPath, "vs_font_distance_field_subpixel");
|
||||||
|
vsh = bgfx::createVertexShader(mem);
|
||||||
|
mem = loadShader(shaderPath, "fs_font_distance_field_subpixel");
|
||||||
|
fsh = bgfx::createFragmentShader(mem);
|
||||||
|
m_distanceSubpixelProgram = bgfx::createProgram(vsh, fsh);
|
||||||
|
bgfx::destroyVertexShader(vsh);
|
||||||
|
bgfx::destroyFragmentShader(fsh);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextBufferHandle TextBufferManager::createTextBuffer(FontType _type, BufferType bufferType)
|
||||||
|
{
|
||||||
|
uint16_t textIdx = m_textBufferHandles.alloc();
|
||||||
|
BufferCache& bc = m_textBuffers[textIdx];
|
||||||
|
|
||||||
|
bc.textBuffer = new TextBuffer(m_fontManager);
|
||||||
|
bc.fontType = _type;
|
||||||
|
bc.bufferType = bufferType;
|
||||||
|
bc.indexBufferHandle = bgfx::invalidHandle;
|
||||||
|
bc.vertexBufferHandle = bgfx::invalidHandle;
|
||||||
|
|
||||||
|
TextBufferHandle ret = {textIdx};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::destroyTextBuffer(TextBufferHandle handle)
|
||||||
|
{
|
||||||
|
assert( bgfx::invalidHandle != handle.idx);
|
||||||
|
|
||||||
|
BufferCache& bc = m_textBuffers[handle.idx];
|
||||||
|
m_textBufferHandles.free(handle.idx);
|
||||||
|
delete bc.textBuffer;
|
||||||
|
bc.textBuffer = NULL;
|
||||||
|
|
||||||
|
if(bc.vertexBufferHandle == bgfx::invalidHandle ) return;
|
||||||
|
|
||||||
|
switch(bc.bufferType)
|
||||||
|
{
|
||||||
|
case STATIC:
|
||||||
|
{
|
||||||
|
bgfx::IndexBufferHandle ibh;
|
||||||
|
bgfx::VertexBufferHandle vbh;
|
||||||
|
ibh.idx = bc.indexBufferHandle;
|
||||||
|
vbh.idx = bc.vertexBufferHandle;
|
||||||
|
bgfx::destroyIndexBuffer(ibh);
|
||||||
|
bgfx::destroyVertexBuffer(vbh);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case DYNAMIC:
|
||||||
|
bgfx::DynamicIndexBufferHandle ibh;
|
||||||
|
bgfx::DynamicVertexBufferHandle vbh;
|
||||||
|
ibh.idx = bc.indexBufferHandle;
|
||||||
|
vbh.idx = bc.vertexBufferHandle;
|
||||||
|
bgfx::destroyDynamicIndexBuffer(ibh);
|
||||||
|
bgfx::destroyDynamicVertexBuffer(vbh);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case TRANSIENT: //naturally destroyed
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::submitTextBuffer(TextBufferHandle _handle, uint8_t _id, int32_t _depth)
|
||||||
|
{
|
||||||
|
assert(bgfx::invalidHandle != _handle.idx);
|
||||||
|
BufferCache& bc = m_textBuffers[_handle.idx];
|
||||||
|
|
||||||
|
size_t indexSize = bc.textBuffer->getIndexCount() * bc.textBuffer->getIndexSize();
|
||||||
|
size_t vertexSize = bc.textBuffer->getVertexCount() * bc.textBuffer->getVertexSize();
|
||||||
|
const bgfx::Memory* mem;
|
||||||
|
|
||||||
|
bgfx::setTexture(0, m_u_texColor, m_fontManager->getAtlas()->getTextureHandle());
|
||||||
|
float inverse_gamme = 1.0f/2.2f;
|
||||||
|
bgfx::setUniform(m_u_inverse_gamma, &inverse_gamme);
|
||||||
|
|
||||||
|
switch (bc.fontType)
|
||||||
|
{
|
||||||
|
case FONT_TYPE_ALPHA:
|
||||||
|
bgfx::setProgram(m_basicProgram);
|
||||||
|
bgfx::setState( BGFX_STATE_RGB_WRITE | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA) );
|
||||||
|
break;
|
||||||
|
case FONT_TYPE_DISTANCE:
|
||||||
|
bgfx::setProgram(m_distanceProgram);
|
||||||
|
bgfx::setState( BGFX_STATE_RGB_WRITE | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA) );
|
||||||
|
break;
|
||||||
|
case FONT_TYPE_DISTANCE_SUBPIXEL:
|
||||||
|
bgfx::setProgram(m_distanceSubpixelProgram);
|
||||||
|
bgfx::setState( BGFX_STATE_RGB_WRITE |BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_FACTOR, BGFX_STATE_BLEND_INV_SRC_COLOR) , bc.textBuffer->getTextColor());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(bc.bufferType)
|
||||||
|
{
|
||||||
|
case STATIC:
|
||||||
|
{
|
||||||
|
bgfx::IndexBufferHandle ibh;
|
||||||
|
bgfx::VertexBufferHandle vbh;
|
||||||
|
|
||||||
|
if(bc.vertexBufferHandle == bgfx::invalidHandle)
|
||||||
|
{
|
||||||
|
mem = bgfx::alloc(indexSize);
|
||||||
|
memcpy(mem->data, bc.textBuffer->getIndexBuffer(), indexSize);
|
||||||
|
ibh = bgfx::createIndexBuffer(mem);
|
||||||
|
|
||||||
|
mem = bgfx::alloc(vertexSize);
|
||||||
|
memcpy(mem->data, bc.textBuffer->getVertexBuffer(), vertexSize);
|
||||||
|
vbh = bgfx::createVertexBuffer(mem, m_vertexDecl);
|
||||||
|
|
||||||
|
bc.indexBufferHandle = ibh.idx ;
|
||||||
|
bc.vertexBufferHandle = vbh.idx;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
ibh.idx = bc.indexBufferHandle;
|
||||||
|
vbh.idx = bc.vertexBufferHandle;
|
||||||
|
}
|
||||||
|
bgfx::setVertexBuffer(vbh, bc.textBuffer->getVertexCount());
|
||||||
|
bgfx::setIndexBuffer(ibh, bc.textBuffer->getIndexCount());
|
||||||
|
}break;
|
||||||
|
case DYNAMIC:
|
||||||
|
{
|
||||||
|
bgfx::DynamicIndexBufferHandle ibh;
|
||||||
|
bgfx::DynamicVertexBufferHandle vbh;
|
||||||
|
|
||||||
|
if(bc.vertexBufferHandle == bgfx::invalidHandle)
|
||||||
|
{
|
||||||
|
mem = bgfx::alloc(indexSize);
|
||||||
|
memcpy(mem->data, bc.textBuffer->getIndexBuffer(), indexSize);
|
||||||
|
ibh = bgfx::createDynamicIndexBuffer(mem);
|
||||||
|
|
||||||
|
mem = bgfx::alloc(vertexSize);
|
||||||
|
memcpy(mem->data, bc.textBuffer->getVertexBuffer(), vertexSize);
|
||||||
|
vbh = bgfx::createDynamicVertexBuffer(mem, m_vertexDecl);
|
||||||
|
|
||||||
|
bc.indexBufferHandle = ibh.idx ;
|
||||||
|
bc.vertexBufferHandle = vbh.idx;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
ibh.idx = bc.indexBufferHandle;
|
||||||
|
vbh.idx = bc.vertexBufferHandle;
|
||||||
|
|
||||||
|
static int i=0;
|
||||||
|
//if(i++ < 5)
|
||||||
|
{
|
||||||
|
mem = bgfx::alloc(indexSize);
|
||||||
|
memcpy(mem->data, bc.textBuffer->getIndexBuffer(), indexSize);
|
||||||
|
bgfx::updateDynamicIndexBuffer(ibh, mem);
|
||||||
|
|
||||||
|
mem = bgfx::alloc(vertexSize);
|
||||||
|
memcpy(mem->data, bc.textBuffer->getVertexBuffer(), vertexSize);
|
||||||
|
bgfx::updateDynamicVertexBuffer(vbh, mem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bgfx::setVertexBuffer(vbh, bc.textBuffer->getVertexCount());
|
||||||
|
bgfx::setIndexBuffer(ibh, bc.textBuffer->getIndexCount());
|
||||||
|
|
||||||
|
}break;
|
||||||
|
case TRANSIENT:
|
||||||
|
{
|
||||||
|
bgfx::TransientIndexBuffer tib;
|
||||||
|
bgfx::TransientVertexBuffer tvb;
|
||||||
|
bgfx::allocTransientIndexBuffer(&tib, bc.textBuffer->getIndexCount());
|
||||||
|
bgfx::allocTransientVertexBuffer(&tvb, bc.textBuffer->getVertexCount(), m_vertexDecl);
|
||||||
|
memcpy(tib.data, bc.textBuffer->getIndexBuffer(), indexSize);
|
||||||
|
memcpy(tvb.data, bc.textBuffer->getVertexBuffer(), vertexSize);
|
||||||
|
bgfx::setVertexBuffer(&tvb, bc.textBuffer->getVertexCount());
|
||||||
|
bgfx::setIndexBuffer(&tib, bc.textBuffer->getIndexCount());
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bgfx::submit(_id, _depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::submitTextBufferMask(TextBufferHandle /*_handle*/, uint32_t /*_viewMask*/, int32_t /*_depth*/)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::setStyle(TextBufferHandle _handle, uint32_t flags )
|
||||||
|
{
|
||||||
|
assert( _handle.idx != bgfx::invalidHandle);
|
||||||
|
BufferCache& bc = m_textBuffers[_handle.idx];
|
||||||
|
bc.textBuffer->setStyle(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::setTextColor(TextBufferHandle _handle, uint32_t rgba )
|
||||||
|
{
|
||||||
|
assert( _handle.idx != bgfx::invalidHandle);
|
||||||
|
BufferCache& bc = m_textBuffers[_handle.idx];
|
||||||
|
bc.textBuffer->setTextColor(rgba);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::setBackgroundColor(TextBufferHandle _handle, uint32_t rgba )
|
||||||
|
{
|
||||||
|
assert( _handle.idx != bgfx::invalidHandle);
|
||||||
|
BufferCache& bc = m_textBuffers[_handle.idx];
|
||||||
|
bc.textBuffer->setBackgroundColor(rgba);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::setOverlineColor(TextBufferHandle _handle, uint32_t rgba )
|
||||||
|
{
|
||||||
|
assert( _handle.idx != bgfx::invalidHandle);
|
||||||
|
BufferCache& bc = m_textBuffers[_handle.idx];
|
||||||
|
bc.textBuffer->setOverlineColor(rgba);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::setUnderlineColor(TextBufferHandle _handle, uint32_t rgba )
|
||||||
|
{
|
||||||
|
assert( _handle.idx != bgfx::invalidHandle);
|
||||||
|
BufferCache& bc = m_textBuffers[_handle.idx];
|
||||||
|
bc.textBuffer->setUnderlineColor(rgba);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::setStrikeThroughColor(TextBufferHandle _handle, uint32_t rgba )
|
||||||
|
{
|
||||||
|
assert( _handle.idx != bgfx::invalidHandle);
|
||||||
|
BufferCache& bc = m_textBuffers[_handle.idx];
|
||||||
|
bc.textBuffer->setStrikeThroughColor(rgba);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::setPenPosition(TextBufferHandle _handle, float x, float y)
|
||||||
|
{
|
||||||
|
assert( _handle.idx != bgfx::invalidHandle);
|
||||||
|
BufferCache& bc = m_textBuffers[_handle.idx];
|
||||||
|
bc.textBuffer->setPenPosition(x,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::appendText(TextBufferHandle _handle, FontHandle fontHandle, const char * _string)
|
||||||
|
{
|
||||||
|
assert( _handle.idx != bgfx::invalidHandle);
|
||||||
|
BufferCache& bc = m_textBuffers[_handle.idx];
|
||||||
|
bc.textBuffer->appendText(fontHandle, _string);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::appendText(TextBufferHandle _handle, FontHandle fontHandle, const wchar_t * _string)
|
||||||
|
{
|
||||||
|
assert( _handle.idx != bgfx::invalidHandle);
|
||||||
|
BufferCache& bc = m_textBuffers[_handle.idx];
|
||||||
|
bc.textBuffer->appendText(fontHandle, _string);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferManager::clearTextBuffer(TextBufferHandle _handle)
|
||||||
|
{
|
||||||
|
assert( _handle.idx != bgfx::invalidHandle);
|
||||||
|
BufferCache& bc = m_textBuffers[_handle.idx];
|
||||||
|
bc.textBuffer->clearTextBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
90
examples/common/font/text_buffer_manager.h
Normal file
90
examples/common/font/text_buffer_manager.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/* Copyright 2013 Jeremie Roy. All rights reserved.
|
||||||
|
* License: http://www.opensource.org/licenses/BSD-2-Clause
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "font_manager.h"
|
||||||
|
|
||||||
|
namespace bgfx_font
|
||||||
|
{
|
||||||
|
|
||||||
|
BGFX_HANDLE(TextBufferHandle);
|
||||||
|
|
||||||
|
/// type of vertex and index buffer to use with a TextBuffer
|
||||||
|
enum BufferType
|
||||||
|
{
|
||||||
|
STATIC,
|
||||||
|
DYNAMIC ,
|
||||||
|
TRANSIENT
|
||||||
|
};
|
||||||
|
|
||||||
|
/// special style effect (can be combined)
|
||||||
|
enum TextStyleFlags
|
||||||
|
{
|
||||||
|
STYLE_NORMAL = 0,
|
||||||
|
STYLE_OVERLINE = 1,
|
||||||
|
STYLE_UNDERLINE = 1<<1,
|
||||||
|
STYLE_STRIKE_THROUGH = 1<<2,
|
||||||
|
STYLE_BACKGROUND = 1<<3,
|
||||||
|
};
|
||||||
|
|
||||||
|
class TextBuffer;
|
||||||
|
class TextBufferManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TextBufferManager(FontManager* fontManager = NULL);
|
||||||
|
~TextBufferManager();
|
||||||
|
|
||||||
|
void init(const char* shaderPath);
|
||||||
|
|
||||||
|
TextBufferHandle createTextBuffer(FontType type, BufferType bufferType);
|
||||||
|
void destroyTextBuffer(TextBufferHandle handle);
|
||||||
|
void submitTextBuffer(TextBufferHandle handle, uint8_t id, int32_t depth = 0);
|
||||||
|
void submitTextBufferMask(TextBufferHandle handle, uint32_t viewMask, int32_t depth = 0);
|
||||||
|
|
||||||
|
void setStyle(TextBufferHandle handle, uint32_t flags = STYLE_NORMAL);
|
||||||
|
void setTextColor(TextBufferHandle handle, uint32_t rgba = 0x000000FF);
|
||||||
|
void setBackgroundColor(TextBufferHandle handle, uint32_t rgba = 0x000000FF);
|
||||||
|
|
||||||
|
void setOverlineColor(TextBufferHandle handle, uint32_t rgba = 0x000000FF);
|
||||||
|
void setUnderlineColor(TextBufferHandle handle, uint32_t rgba = 0x000000FF);
|
||||||
|
void setStrikeThroughColor(TextBufferHandle handle, uint32_t rgba = 0x000000FF);
|
||||||
|
|
||||||
|
void setPenPosition(TextBufferHandle handle, float x, float y);
|
||||||
|
|
||||||
|
/// append an ASCII/utf-8 string to the buffer using current pen position and color
|
||||||
|
void appendText(TextBufferHandle _handle, FontHandle fontHandle, const char * _string);
|
||||||
|
|
||||||
|
/// append a wide char unicode string to the buffer using current pen position and color
|
||||||
|
void appendText(TextBufferHandle _handle, FontHandle fontHandle, const wchar_t * _string);
|
||||||
|
|
||||||
|
/// Clear the text buffer and reset its state (pen/color)
|
||||||
|
void clearTextBuffer(TextBufferHandle _handle);
|
||||||
|
|
||||||
|
/// return the size of the text
|
||||||
|
//Rectangle measureText(FontHandle fontHandle, const char * _string);
|
||||||
|
//Rectangle measureText(FontHandle fontHandle, const wchar_t * _string);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct BufferCache
|
||||||
|
{
|
||||||
|
uint16_t indexBufferHandle;
|
||||||
|
uint16_t vertexBufferHandle;
|
||||||
|
TextBuffer* textBuffer;
|
||||||
|
BufferType bufferType;
|
||||||
|
FontType fontType;
|
||||||
|
};
|
||||||
|
|
||||||
|
BufferCache* m_textBuffers;
|
||||||
|
bx::HandleAlloc m_textBufferHandles;
|
||||||
|
FontManager* m_fontManager;
|
||||||
|
bgfx::VertexDecl m_vertexDecl;
|
||||||
|
bgfx::UniformHandle m_u_texColor;
|
||||||
|
bgfx::UniformHandle m_u_inverse_gamma;
|
||||||
|
//shaders program
|
||||||
|
bgfx::ProgramHandle m_basicProgram;
|
||||||
|
bgfx::ProgramHandle m_distanceProgram;
|
||||||
|
bgfx::ProgramHandle m_distanceSubpixelProgram;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
9
examples/common/font/varying.def.sc
Normal file
9
examples/common/font/varying.def.sc
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
vec2 a_position : POSITION;
|
||||||
|
vec4 a_color0 : COLOR0;
|
||||||
|
vec4 a_texcoord0 : TEXCOORD0;
|
||||||
|
|
||||||
|
vec4 v_color0 : COLOR0 = vec4(1.0, 0.0, 0.0, 1.0);
|
||||||
|
vec4 v_texcoord0 : TEXCOORD0 = vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
vec4 v_sampleLeft : TEXCOORD1 = vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
vec4 v_sampleRight : TEXCOORD2 = vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
|
11
examples/common/font/vs_font_basic.sc
Normal file
11
examples/common/font/vs_font_basic.sc
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
$input a_position, a_color0, a_texcoord0
|
||||||
|
$output v_color0, v_texcoord0
|
||||||
|
|
||||||
|
#include "../../common/common.sh"
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = mul(u_modelViewProj, vec4(a_position, 0.0, 1.0) );
|
||||||
|
v_texcoord0 = a_texcoord0;
|
||||||
|
v_color0 = a_color0;
|
||||||
|
}
|
11
examples/common/font/vs_font_distance_field.sc
Normal file
11
examples/common/font/vs_font_distance_field.sc
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
$input a_position, a_color0, a_texcoord0
|
||||||
|
$output v_color0, v_texcoord0
|
||||||
|
|
||||||
|
#include "../../common/common.sh"
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = mul(u_modelViewProj, vec4(a_position, 0.0, 1.0) );
|
||||||
|
v_texcoord0 = a_texcoord0;
|
||||||
|
v_color0 = a_color0;
|
||||||
|
}
|
15
examples/common/font/vs_font_distance_field_subpixel.sc
Normal file
15
examples/common/font/vs_font_distance_field_subpixel.sc
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
$input a_position, a_color0, a_texcoord0
|
||||||
|
$output v_color0, v_texcoord0
|
||||||
|
|
||||||
|
#include "../../common/common.sh"
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = mul(u_modelViewProj, vec4(a_position, 0.0, 1.0) );
|
||||||
|
v_texcoord0 = a_texcoord0;
|
||||||
|
v_color0 = a_color0;
|
||||||
|
|
||||||
|
//vec3 decal = dFdx(a_texcoord0.xyz);
|
||||||
|
//v_sampleLeft = a_texcoord0 + decal;
|
||||||
|
//v_sampleRight = a_texcoord0 - decal;
|
||||||
|
}
|
@ -13,3 +13,4 @@ rebuild:
|
|||||||
@make -s --no-print-directory rebuild -C 07-callback
|
@make -s --no-print-directory rebuild -C 07-callback
|
||||||
@make -s --no-print-directory rebuild -C 08-update
|
@make -s --no-print-directory rebuild -C 08-update
|
||||||
@make -s --no-print-directory rebuild -C 09-hdr
|
@make -s --no-print-directory rebuild -C 09-hdr
|
||||||
|
@make -s --no-print-directory rebuild -C common/font
|
||||||
|
BIN
examples/runtime/shaders/dx11/fs_font_basic.bin
Normal file
BIN
examples/runtime/shaders/dx11/fs_font_basic.bin
Normal file
Binary file not shown.
BIN
examples/runtime/shaders/dx11/fs_font_distance_field.bin
Normal file
BIN
examples/runtime/shaders/dx11/fs_font_distance_field.bin
Normal file
Binary file not shown.
Binary file not shown.
BIN
examples/runtime/shaders/dx11/vs_font_basic.bin
Normal file
BIN
examples/runtime/shaders/dx11/vs_font_basic.bin
Normal file
Binary file not shown.
BIN
examples/runtime/shaders/dx11/vs_font_distance_field.bin
Normal file
BIN
examples/runtime/shaders/dx11/vs_font_distance_field.bin
Normal file
Binary file not shown.
Binary file not shown.
BIN
examples/runtime/shaders/dx9/fs_font_basic.bin
Normal file
BIN
examples/runtime/shaders/dx9/fs_font_basic.bin
Normal file
Binary file not shown.
BIN
examples/runtime/shaders/dx9/fs_font_distance_field.bin
Normal file
BIN
examples/runtime/shaders/dx9/fs_font_distance_field.bin
Normal file
Binary file not shown.
BIN
examples/runtime/shaders/dx9/fs_font_distance_field_subpixel.bin
Normal file
BIN
examples/runtime/shaders/dx9/fs_font_distance_field_subpixel.bin
Normal file
Binary file not shown.
BIN
examples/runtime/shaders/dx9/vs_font_basic.bin
Normal file
BIN
examples/runtime/shaders/dx9/vs_font_basic.bin
Normal file
Binary file not shown.
BIN
examples/runtime/shaders/dx9/vs_font_distance_field.bin
Normal file
BIN
examples/runtime/shaders/dx9/vs_font_distance_field.bin
Normal file
Binary file not shown.
BIN
examples/runtime/shaders/dx9/vs_font_distance_field_subpixel.bin
Normal file
BIN
examples/runtime/shaders/dx9/vs_font_distance_field_subpixel.bin
Normal file
Binary file not shown.
BIN
examples/runtime/shaders/gles/fs_font_basic.bin
Normal file
BIN
examples/runtime/shaders/gles/fs_font_basic.bin
Normal file
Binary file not shown.
BIN
examples/runtime/shaders/gles/fs_font_distance_field.bin
Normal file
BIN
examples/runtime/shaders/gles/fs_font_distance_field.bin
Normal file
Binary file not shown.
Binary file not shown.
BIN
examples/runtime/shaders/gles/vs_font_basic.bin
Normal file
BIN
examples/runtime/shaders/gles/vs_font_basic.bin
Normal file
Binary file not shown.
BIN
examples/runtime/shaders/gles/vs_font_distance_field.bin
Normal file
BIN
examples/runtime/shaders/gles/vs_font_distance_field.bin
Normal file
Binary file not shown.
Binary file not shown.
BIN
examples/runtime/shaders/glsl/fs_font_basic.bin
Normal file
BIN
examples/runtime/shaders/glsl/fs_font_basic.bin
Normal file
Binary file not shown.
BIN
examples/runtime/shaders/glsl/fs_font_distance_field.bin
Normal file
BIN
examples/runtime/shaders/glsl/fs_font_distance_field.bin
Normal file
Binary file not shown.
Binary file not shown.
BIN
examples/runtime/shaders/glsl/vs_font_basic.bin
Normal file
BIN
examples/runtime/shaders/glsl/vs_font_basic.bin
Normal file
Binary file not shown.
BIN
examples/runtime/shaders/glsl/vs_font_distance_field.bin
Normal file
BIN
examples/runtime/shaders/glsl/vs_font_distance_field.bin
Normal file
Binary file not shown.
Binary file not shown.
@ -116,6 +116,8 @@ exampleProject("06-bump", "ffb23e6c-167b-11e2-81df-94c4dd6a022f")
|
|||||||
exampleProject("07-callback", "acc53bbc-52f0-11e2-9781-ad8edd4b7d02")
|
exampleProject("07-callback", "acc53bbc-52f0-11e2-9781-ad8edd4b7d02")
|
||||||
exampleProject("08-update", "e011e246-5862-11e2-b202-b7cb257a7926")
|
exampleProject("08-update", "e011e246-5862-11e2-b202-b7cb257a7926")
|
||||||
exampleProject("09-hdr", "969a4626-67ee-11e2-9726-9023267a7926")
|
exampleProject("09-hdr", "969a4626-67ee-11e2-9726-9023267a7926")
|
||||||
|
exampleProject("10-font_alpha" , "EF6FD5B3-B52A-41C2-A257-9DFE709AF9E1")
|
||||||
|
exampleProject("11-font_distance_field", "F4E6F96F-3DAA-4C68-8DF8-BF2A3ECD9092")
|
||||||
dofile "makedisttex.lua"
|
dofile "makedisttex.lua"
|
||||||
dofile "shaderc.lua"
|
dofile "shaderc.lua"
|
||||||
dofile "texturec.lua"
|
dofile "texturec.lua"
|
||||||
|
Loading…
Reference in New Issue
Block a user