integration of font and text system

This commit is contained in:
Jeremie Roy 2013-04-22 22:42:11 +02:00
parent d8bd7419f9
commit 7c7612a971
45 changed files with 120631 additions and 0 deletions

169
3rdparty/freetype/FTL.TXT vendored Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

View 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;
}

View 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;
}

View 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;
}
}
}

View 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;
};}

View 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;
}
}

View 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;
};
}

View 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);
}

View 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);
}

View 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
}

View 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

View 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();
}
}

View 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;
};
}

View 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);

View 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;
}

View 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;
}

View 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;
}

View File

@ -13,3 +13,4 @@ rebuild:
@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 09-hdr
@make -s --no-print-directory rebuild -C common/font

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -116,6 +116,8 @@ exampleProject("06-bump", "ffb23e6c-167b-11e2-81df-94c4dd6a022f")
exampleProject("07-callback", "acc53bbc-52f0-11e2-9781-ad8edd4b7d02")
exampleProject("08-update", "e011e246-5862-11e2-b202-b7cb257a7926")
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 "shaderc.lua"
dofile "texturec.lua"