commit x64 compilation from lulu cause the other branch dont seems to compile properly at home
This commit is contained in:
377
include/framework/font/font_renderer.cpp
Normal file
377
include/framework/font/font_renderer.cpp
Normal file
@ -0,0 +1,377 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include "font/font_renderer.h"
|
||||
#include "picture/pict.h"
|
||||
#include "ascii/parser.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::AsciiParser;
|
||||
|
||||
float FontRenderer::default_text_tracking = 0;
|
||||
float FontRenderer::default_text_heading = 0;
|
||||
|
||||
//
|
||||
struct GS::SubString
|
||||
{
|
||||
const char *entry;
|
||||
int char_count;
|
||||
int space_count;
|
||||
int width; // 26.6
|
||||
int height;
|
||||
|
||||
void Reset(const char *string)
|
||||
{
|
||||
entry = string;
|
||||
char_count = 0;
|
||||
space_count = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
};
|
||||
|
||||
int substring_count = 0;
|
||||
SubString substring_array[1024];
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char *FontRenderer::CheckCommand(const char *string, Command &command)
|
||||
{
|
||||
command.code = CommandNone;
|
||||
if (!string[0] || !string[1])
|
||||
return string;
|
||||
|
||||
if ((string[0] == '~') && (string[1] == '~'))
|
||||
{
|
||||
//----------------------------------------------------------------------
|
||||
#define PARSE_COMPONENT(_C_, _M_)\
|
||||
{\
|
||||
string = SkipSpace(string + 1, eos);\
|
||||
command.vector._C_ = (float)String::atoi(string);\
|
||||
string = NextEntry(string, eos);\
|
||||
if (string[0] != (_M_))\
|
||||
{\
|
||||
command.code = CommandParseError;\
|
||||
__LOG_E__ << "Error parsing text command components.\n";\
|
||||
return NULL;\
|
||||
}\
|
||||
}
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
const char *eos = string + std::strlen(string);
|
||||
|
||||
if (!strncmp("Color(", string + 2, 6) || !strncmp("COLOR(", string + 2, 6)) // [EJ] 1st may: range is [0;255]
|
||||
{
|
||||
command.code = CommandColor;
|
||||
string += 7;
|
||||
|
||||
const char *eop = Find(string, eos, ')');
|
||||
if (!eop)
|
||||
{
|
||||
command.code = CommandParseError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PARSE_COMPONENT(x, ',');
|
||||
PARSE_COMPONENT(y, ',');
|
||||
PARSE_COMPONENT(z, ',');
|
||||
PARSE_COMPONENT(w, ')');
|
||||
string = eop + 1;
|
||||
}
|
||||
else if (!strncmp("Size(", string + 2, 5) || !strncmp("SIZE(", string + 2, 5))
|
||||
{
|
||||
command.code = CommandSize;
|
||||
|
||||
string += 6;
|
||||
|
||||
const char *eop = Find(string, eos, ')');
|
||||
if (!eop)
|
||||
{
|
||||
command.code = CommandParseError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PARSE_COMPONENT(x, ')');
|
||||
string = eop + 1;
|
||||
}
|
||||
}
|
||||
return string;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char *FontRenderer::FetchSubString(const char *string, SubString &substring, TextState &state, int max_width, int max_char)
|
||||
{
|
||||
if (!string)
|
||||
return NULL;
|
||||
|
||||
// Jump over leading spaces.
|
||||
forever
|
||||
{
|
||||
if (string[0] != ' ')
|
||||
break;
|
||||
|
||||
if (!string[0] || (string[0] == '\n'))
|
||||
{
|
||||
substring.Reset(NULL);
|
||||
return string;
|
||||
}
|
||||
string++;
|
||||
}
|
||||
|
||||
// Start sub-string.
|
||||
substring.Reset(string);
|
||||
int previous_codepoint = 0;
|
||||
|
||||
state.font->SetPixelSize(state.GetSize());
|
||||
max_width <<= 6;
|
||||
|
||||
// Word cut point.
|
||||
bool rollback_available = false;
|
||||
SubString rollback_substring;
|
||||
const char *rollback_string = NULL;
|
||||
|
||||
int tracking = int(state.GetTracking() * 64.f);
|
||||
|
||||
forever
|
||||
{
|
||||
Command command;
|
||||
if ((string = CheckCommand(string, command)) == 0)
|
||||
break;
|
||||
|
||||
if (command.code == CommandNone)
|
||||
{
|
||||
if (string[0] == '\n')
|
||||
{
|
||||
string++;
|
||||
break;
|
||||
}
|
||||
if ((string[0] == '\\') && (string[1] == 'n'))
|
||||
{
|
||||
string += 2;
|
||||
break;
|
||||
}
|
||||
if (string[0] == 0)
|
||||
break;
|
||||
if ((max_char > 0) && (substring.char_count == max_char))
|
||||
break;
|
||||
|
||||
// Get glyph.
|
||||
uint codepoint;
|
||||
int codelength = String::Utf8toUtf32((const uchar *)string, &codepoint);
|
||||
|
||||
state.font->LoadGlyph(codepoint, false);
|
||||
|
||||
// Retrieve glyph formatting informations.
|
||||
int advance = state.font->GetAdvance();
|
||||
int kerning = (state.font->HasKerning() && (previous_codepoint != 0)) ? state.font->GetKerning(previous_codepoint, codepoint) : 0;
|
||||
|
||||
// Width constraint.
|
||||
if ((max_width > 0) && ((substring.width + advance) >= max_width))
|
||||
{
|
||||
rollback_available = true;
|
||||
break;
|
||||
}
|
||||
|
||||
substring.width += advance + kerning + tracking;
|
||||
substring.height = Types::Max(state.font->GetHeight(), substring.height);
|
||||
|
||||
// Count space.
|
||||
if (string[0] == ' ')
|
||||
{
|
||||
substring.space_count++;
|
||||
|
||||
// Store the word rollback position.
|
||||
Command dummy_command;
|
||||
CheckCommand(string, dummy_command);
|
||||
|
||||
if ((dummy_command.code == CommandNone) && (string[1] != ' '))
|
||||
{
|
||||
rollback_substring = substring;
|
||||
rollback_string = string;
|
||||
}
|
||||
}
|
||||
substring.char_count++;
|
||||
|
||||
// Next glyph.
|
||||
string += codelength ? codelength : 1;
|
||||
previous_codepoint = codepoint;
|
||||
}
|
||||
else
|
||||
switch (command.code)
|
||||
{
|
||||
case CommandColor: // Irrelevant when not composing.
|
||||
break;
|
||||
|
||||
case CommandSize:
|
||||
state.SetSize((int)command.vector.x);
|
||||
state.font->SetPixelSize(state.GetSize());
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// Rollback to the last word position.
|
||||
if (rollback_available && rollback_string)
|
||||
{
|
||||
substring = rollback_substring;
|
||||
string = rollback_string;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void FontRenderer::DrawSubString(SubString &line, Picture &output, TextState &state, const iRect &_out_rect, const iRect &clip_rect, int justification)
|
||||
{
|
||||
const char *string = line.entry;
|
||||
state.font->SetPixelSize(state.GetSize());
|
||||
|
||||
iRect out_rect(_out_rect);
|
||||
uint previous_codepoint = 0;
|
||||
|
||||
int tracking = int(state.GetTracking() * 64.f);
|
||||
|
||||
for (int n = 0; n < line.char_count; ++n)
|
||||
{
|
||||
Command command;
|
||||
string = CheckCommand(string, command);
|
||||
|
||||
if (command.code == CommandNone)
|
||||
{
|
||||
// Get glyph.
|
||||
uint codepoint;
|
||||
int codelength = String::Utf8toUtf32((const uchar *)string, &codepoint);
|
||||
|
||||
state.font->LoadGlyph(codepoint, true);
|
||||
|
||||
// Retrieve glyph formatting informations.
|
||||
int advance = state.font->GetAdvance();
|
||||
int kerning = (state.font->HasKerning() && (previous_codepoint != 0)) ? state.font->GetKerning(previous_codepoint, codepoint) : 0;
|
||||
|
||||
// Render.
|
||||
int px = out_rect.sx >> 6;
|
||||
int py = (out_rect.sy + (line.height * 3) / 4) >> 6; // FIXME smells the hack... at best!
|
||||
|
||||
state.font->RenderCurrentGlyph(output, iPoint(px, py), clip_rect, state.color);
|
||||
|
||||
out_rect.sx += advance + kerning + tracking;
|
||||
|
||||
// Next glyph.
|
||||
string += codelength;
|
||||
previous_codepoint = codepoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (command.code)
|
||||
{
|
||||
case CommandColor:
|
||||
state.color = command.vector ;/// 255.f;
|
||||
break;
|
||||
|
||||
case CommandSize:
|
||||
state.SetSize((int)command.vector.x);
|
||||
state.font->SetPixelSize(state.GetSize());
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
--n;
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
iRect FontRenderer::Format(const char *txt, const TextState &in_state, const iRect &out_rect)
|
||||
{
|
||||
if (!in_state.font)
|
||||
return iRect (0, 0, 0, 0);
|
||||
|
||||
// Create working state.
|
||||
TextState state = in_state;
|
||||
|
||||
// Setup formatting rules.
|
||||
int max_width = (state.format == TextState::Line) ? -1 : out_rect.GetWidth(),
|
||||
max_char = (state.format != TextState::Column) ? -1 : state.column_width;
|
||||
|
||||
// Fetch all substrings.
|
||||
substring_count = 0;
|
||||
while (txt && txt[0])
|
||||
txt = FetchSubString(txt, substring_array[substring_count++], state, max_width, max_char);
|
||||
|
||||
// Create full text rectangle.
|
||||
iRect text_rect;
|
||||
text_rect.Set(0, 0, 0, 0);
|
||||
|
||||
int leading = int(state.GetLeading() * 64);
|
||||
|
||||
for (int n = 0; n < substring_count; ++n)
|
||||
{
|
||||
if (text_rect.ex < substring_array[n].width)
|
||||
text_rect.ex = substring_array[n].width;
|
||||
text_rect.ey += substring_array[n].height + leading;
|
||||
}
|
||||
if (substring_count > 0)
|
||||
text_rect.ey -= leading;
|
||||
|
||||
text_rect.ex = text_rect.ex / 64;
|
||||
text_rect.ey = text_rect.ey / 64;
|
||||
return text_rect;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
iRect FontRenderer::Compose(Picture &output, const char *txt, const TextState &in_state, const iRect &out_rect, const iRect &clip_rect)
|
||||
{
|
||||
if (in_state.font.IsNull() || (output.GetPixelFormat().GetBpp() != 32))
|
||||
return iRect(0, 0, 0, 0);
|
||||
|
||||
// Create working state.
|
||||
TextState state = in_state;
|
||||
int leading = int(state.GetLeading() * 64);
|
||||
|
||||
// Draw substrings.
|
||||
iRect work_out_rect(out_rect * 64);
|
||||
|
||||
for (int n = 0; n < substring_count; ++n)
|
||||
{
|
||||
iRect line_rect(work_out_rect);
|
||||
line_rect.ex = line_rect.sx + substring_array[n].width;
|
||||
|
||||
int offset = 0, justification = 0;
|
||||
|
||||
switch (state.alignment)
|
||||
{
|
||||
case TextState::Left:
|
||||
offset = out_rect.sx * 64 - line_rect.sx;
|
||||
break;
|
||||
case TextState::Center:
|
||||
offset = (out_rect.GetWidth() * 64 - line_rect.GetWidth()) / 2;
|
||||
break;
|
||||
case TextState::Right:
|
||||
offset = out_rect.ex * 64 - line_rect.ex;
|
||||
break;
|
||||
|
||||
case TextState::Justify:
|
||||
if (n < (substring_count - 1))
|
||||
justification = (out_rect.GetWidth() * 64 - line_rect.GetWidth()) / substring_array[n].space_count;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
line_rect.sx += offset;
|
||||
|
||||
DrawSubString(substring_array[n], output, state, line_rect, clip_rect, justification);
|
||||
work_out_rect.sy += substring_array[n].height + leading;
|
||||
}
|
||||
return out_rect;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user