Initial commit
This commit is contained in:
126
engines/ags/shared/font/ags_font_renderer.h
Normal file
126
engines/ags/shared/font/ags_font_renderer.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AGS_SHARED_FONT_AGS_FONT_RENDERER_H
|
||||
#define AGS_SHARED_FONT_AGS_FONT_RENDERER_H
|
||||
|
||||
#include "common/std/utility.h"
|
||||
#include "ags/shared/core/types.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
class BITMAP;
|
||||
|
||||
class IAGSFontRenderer {
|
||||
public:
|
||||
virtual bool LoadFromDisk(int fontNumber, int fontSize) = 0;
|
||||
virtual void FreeMemory(int fontNumber) = 0;
|
||||
virtual bool SupportsExtendedCharacters(int fontNumber) = 0;
|
||||
virtual int GetTextWidth(const char *text, int fontNumber) = 0;
|
||||
// Get actual height of the given line of text
|
||||
virtual int GetTextHeight(const char *text, int fontNumber) = 0;
|
||||
virtual void RenderText(const char *text, int fontNumber, BITMAP *destination, int x, int y, int colour) = 0;
|
||||
virtual void AdjustYCoordinateForFont(int *ycoord, int fontNumber) = 0;
|
||||
virtual void EnsureTextValidForFont(char *text, int fontNumber) = 0;
|
||||
protected:
|
||||
IAGSFontRenderer() {}
|
||||
~IAGSFontRenderer() {}
|
||||
};
|
||||
|
||||
// Extended font renderer interface.
|
||||
// WARNING: this interface is exposed for plugins and declared for the second time in agsplugin.h
|
||||
class IAGSFontRenderer2 : public IAGSFontRenderer {
|
||||
public:
|
||||
// Returns engine API version this font renderer complies to.
|
||||
// Must not be lower than 26 (this interface was added at API v26).
|
||||
virtual int GetVersion() = 0;
|
||||
// Returns an arbitrary renderer name; this is for informational
|
||||
// purposes only.
|
||||
virtual const char *GetRendererName() = 0;
|
||||
// Returns given font's name (if available).
|
||||
virtual const char *GetFontName(int fontNumber) = 0;
|
||||
// Returns the given font's height: that is the maximal vertical size
|
||||
// that the font glyphs may occupy.
|
||||
virtual int GetFontHeight(int fontNumber) = 0;
|
||||
// Returns the given font's linespacing;
|
||||
// is allowed to return 0, telling that no specific linespacing
|
||||
// is assigned for this font.
|
||||
virtual int GetLineSpacing(int fontNumber) = 0;
|
||||
|
||||
protected:
|
||||
IAGSFontRenderer2() {}
|
||||
~IAGSFontRenderer2() {}
|
||||
};
|
||||
|
||||
// Font render params, mainly for dealing with various compatibility issues.
|
||||
struct FontRenderParams {
|
||||
// Font's render multiplier
|
||||
int SizeMultiplier = 1;
|
||||
int LoadMode = 0; // contains font flags from FFLG_LOADMODEMASK
|
||||
};
|
||||
|
||||
// Describes loaded font's properties
|
||||
struct FontMetrics {
|
||||
// Nominal font's height, equals to the game-requested size of the font.
|
||||
// This may or not be equal to font's face height; sometimes a font cannot
|
||||
// be scaled exactly to particular size, and then nominal height appears different
|
||||
// (usually - smaller) than the real one.
|
||||
int NominalHeight = 0;
|
||||
// Real font's height, equals to reported ascender + descender.
|
||||
// This is what you normally think as a font's height.
|
||||
int RealHeight = 0;
|
||||
// Compatible height, equals to either NominalHeight or RealHeight,
|
||||
// selected depending on the game settings.
|
||||
// This property is used in calculating linespace, etc.
|
||||
int CompatHeight = 0;
|
||||
// Maximal vertical extent of a font (top; bottom), this tells the actual
|
||||
// graphical bounds that may be occupied by font's glyphs.
|
||||
// In a "proper" font this extent is (0; RealHeight-1), but "bad" fonts may
|
||||
// have individual glyphs exceeding these bounds, in both directions.
|
||||
// Note that "top" may be negative!
|
||||
std::pair<int, int> VExtent;
|
||||
|
||||
inline int ExtentHeight() const { return VExtent.second - VExtent.first; }
|
||||
};
|
||||
|
||||
// The strictly internal font renderer interface, not to use in plugin API.
|
||||
// Contains methods necessary for built-in font renderers.
|
||||
class IAGSFontRendererInternal : public IAGSFontRenderer2 {
|
||||
public:
|
||||
// Tells if this is a bitmap font (otherwise it's a vector font)
|
||||
virtual bool IsBitmapFont() = 0;
|
||||
// Load font, applying extended font rendering parameters
|
||||
virtual bool LoadFromDiskEx(int fontNumber, int fontSize, AGS::Shared::String *src_filename,
|
||||
const FontRenderParams *params, FontMetrics *metrics) = 0;
|
||||
// Fill FontMetrics struct; note that it may be left cleared if this is not supported
|
||||
virtual void GetFontMetrics(int fontNumber, FontMetrics *metrics) = 0;
|
||||
// Perform any necessary adjustments when the AA mode is toggled
|
||||
virtual void AdjustFontForAntiAlias(int fontNumber, bool aa_mode) = 0;
|
||||
|
||||
protected:
|
||||
IAGSFontRendererInternal() {}
|
||||
~IAGSFontRendererInternal() {}
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
568
engines/ags/shared/font/fonts.cpp
Normal file
568
engines/ags/shared/font/fonts.cpp
Normal file
@@ -0,0 +1,568 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/std/algorithm.h"
|
||||
#include "ags/lib/alfont/alfont.h"
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/ac/common.h" // set_our_eip
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/shared/font/fonts.h"
|
||||
#include "ags/shared/font/ttf_font_renderer.h"
|
||||
#include "ags/shared/font/wfn_font_renderer.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/shared/gui/gui_defines.h" // MAXLINE
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
FontInfo::FontInfo()
|
||||
: Flags(0)
|
||||
, Size(0)
|
||||
, SizeMultiplier(1)
|
||||
, Outline(FONT_OUTLINE_NONE)
|
||||
, YOffset(0)
|
||||
, LineSpacing(0)
|
||||
, AutoOutlineStyle(kSquared)
|
||||
, AutoOutlineThickness(0) {
|
||||
}
|
||||
|
||||
|
||||
void init_font_renderer() {
|
||||
alfont_init();
|
||||
alfont_text_mode(-1);
|
||||
}
|
||||
|
||||
void shutdown_font_renderer() {
|
||||
set_our_eip(9919);
|
||||
alfont_exit();
|
||||
}
|
||||
|
||||
void adjust_y_coordinate_for_text(int *ypos, size_t fontnum) {
|
||||
if (fontnum >= _GP(fonts).size() || !_GP(fonts)[fontnum].Renderer)
|
||||
return;
|
||||
_GP(fonts)[fontnum].Renderer->AdjustYCoordinateForFont(ypos, fontnum);
|
||||
}
|
||||
|
||||
bool font_first_renderer_loaded() {
|
||||
return _GP(fonts).size() > 0 && _GP(fonts)[0].Renderer != nullptr;
|
||||
}
|
||||
|
||||
bool is_font_loaded(size_t fontNumber) {
|
||||
return fontNumber < _GP(fonts).size() && _GP(fonts)[fontNumber].Renderer != nullptr;
|
||||
}
|
||||
|
||||
// Finish font's initialization
|
||||
static void font_post_init(size_t fontNumber) {
|
||||
Font &font = _GP(fonts)[fontNumber];
|
||||
// If no font height property was provided, then try several methods,
|
||||
// depending on which interface is available
|
||||
if (font.Metrics.NominalHeight == 0 && font.Renderer) {
|
||||
int height = 0;
|
||||
if (font.Renderer2)
|
||||
height = font.Renderer2->GetFontHeight(fontNumber);
|
||||
if (height <= 0) {
|
||||
// With the old renderer we have to rely on GetTextHeight;
|
||||
// the implementations of GetTextHeight are allowed to return varied
|
||||
// results depending on the text parameter.
|
||||
// We use special line of text to get more or less reliable font height.
|
||||
const char *height_test_string = "ZHwypgfjqhkilIK";
|
||||
height = font.Renderer->GetTextHeight(height_test_string, fontNumber);
|
||||
}
|
||||
|
||||
font.Metrics.NominalHeight = std::max(0, height);
|
||||
font.Metrics.RealHeight = font.Metrics.NominalHeight;
|
||||
font.Metrics.VExtent = std::make_pair(0, font.Metrics.RealHeight);
|
||||
}
|
||||
// Use either nominal or real pixel height to define font's logical height
|
||||
// and default linespacing; logical height = nominal height is compatible with the old games
|
||||
font.Metrics.CompatHeight = (font.Info.Flags & FFLG_REPORTNOMINALHEIGHT) != 0 ?
|
||||
font.Metrics.NominalHeight : font.Metrics.RealHeight;
|
||||
|
||||
if (font.Info.Outline != FONT_OUTLINE_AUTO) {
|
||||
font.Info.AutoOutlineThickness = 0;
|
||||
}
|
||||
|
||||
// If no linespacing property was provided, then try several methods,
|
||||
// depending on which interface is available
|
||||
font.LineSpacingCalc = font.Info.LineSpacing;
|
||||
if (font.Info.LineSpacing == 0) {
|
||||
int linespacing = 0;
|
||||
if (font.Renderer2)
|
||||
linespacing = font.Renderer2->GetLineSpacing(fontNumber);
|
||||
if (linespacing > 0) {
|
||||
font.LineSpacingCalc = linespacing;
|
||||
} else {
|
||||
// Calculate default linespacing from the font height + outline thickness.
|
||||
font.Info.Flags |= FFLG_DEFLINESPACING;
|
||||
font.LineSpacingCalc = font.Metrics.CompatHeight + 2 * font.Info.AutoOutlineThickness;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void font_replace_renderer(size_t fontNumber, IAGSFontRenderer* renderer, IAGSFontRenderer2* renderer2) {
|
||||
_GP(fonts)[fontNumber].Renderer = renderer;
|
||||
_GP(fonts)[fontNumber].Renderer2 = renderer2;
|
||||
// If this is one of our built-in font renderers, then correctly
|
||||
// reinitialize interfaces and font metrics
|
||||
if ((renderer == &_GP(ttfRenderer)) || (renderer == &_GP(wfnRenderer))) {
|
||||
_GP(fonts)[fontNumber].RendererInt = static_cast<IAGSFontRendererInternal*>(renderer);
|
||||
_GP(fonts)[fontNumber].RendererInt->GetFontMetrics(fontNumber, &_GP(fonts)[fontNumber].Metrics);
|
||||
} else {
|
||||
// Otherwise, this is probably coming from plugin
|
||||
_GP(fonts)[fontNumber].RendererInt = nullptr;
|
||||
_GP(fonts)[fontNumber].Metrics = FontMetrics(); // reset to defaults
|
||||
}
|
||||
font_post_init(fontNumber);
|
||||
}
|
||||
|
||||
|
||||
IAGSFontRenderer *font_replace_renderer(size_t fontNumber, IAGSFontRenderer *renderer) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return nullptr;
|
||||
IAGSFontRenderer* old_render = _GP(fonts)[fontNumber].Renderer;
|
||||
font_replace_renderer(fontNumber, renderer, nullptr);
|
||||
return old_render;
|
||||
}
|
||||
|
||||
IAGSFontRenderer *font_replace_renderer(size_t fontNumber, IAGSFontRenderer2 *renderer) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return nullptr;
|
||||
IAGSFontRenderer* old_render = _GP(fonts)[fontNumber].Renderer;
|
||||
font_replace_renderer(fontNumber, renderer, renderer);
|
||||
return old_render;
|
||||
}
|
||||
|
||||
void font_recalc_metrics(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return;
|
||||
_GP(fonts)[fontNumber].Metrics = FontMetrics();
|
||||
font_post_init(fontNumber);
|
||||
}
|
||||
|
||||
bool is_bitmap_font(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].RendererInt)
|
||||
return false;
|
||||
return _GP(fonts)[fontNumber].RendererInt->IsBitmapFont();
|
||||
}
|
||||
|
||||
bool font_supports_extended_characters(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return false;
|
||||
return _GP(fonts)[fontNumber].Renderer->SupportsExtendedCharacters(fontNumber);
|
||||
}
|
||||
|
||||
const char *get_font_name(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer2)
|
||||
return "";
|
||||
const char *name = _GP(fonts)[fontNumber].Renderer2->GetFontName(fontNumber);
|
||||
return name ? name : "";
|
||||
}
|
||||
|
||||
int get_font_flags(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].Info.Flags;
|
||||
}
|
||||
|
||||
void ensure_text_valid_for_font(char *text, size_t fontnum) {
|
||||
if (fontnum >= _GP(fonts).size() || !_GP(fonts)[fontnum].Renderer)
|
||||
return;
|
||||
_GP(fonts)[fontnum].Renderer->EnsureTextValidForFont(text, fontnum);
|
||||
}
|
||||
|
||||
int get_font_scaling_mul(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].Info.SizeMultiplier;
|
||||
}
|
||||
|
||||
int get_text_width(const char *texx, size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].Renderer->GetTextWidth(texx, fontNumber);
|
||||
}
|
||||
|
||||
int get_text_width_outlined(const char *text, size_t font_number) {
|
||||
if (font_number >= _GP(fonts).size() || !_GP(fonts)[font_number].Renderer)
|
||||
return 0;
|
||||
if (text == nullptr || text[0] == 0) // we ignore outline width since the text is empty
|
||||
return 0;
|
||||
int self_width = _GP(fonts)[font_number].Renderer->GetTextWidth(text, font_number);
|
||||
int outline = _GP(fonts)[font_number].Info.Outline;
|
||||
if (outline < 0 || static_cast<size_t>(outline) > _GP(fonts).size()) { // FONT_OUTLINE_AUTO or FONT_OUTLINE_NONE
|
||||
return self_width + 2 * _GP(fonts)[font_number].Info.AutoOutlineThickness;
|
||||
}
|
||||
int outline_width = _GP(fonts)[outline].Renderer->GetTextWidth(text, outline);
|
||||
return MAX(self_width, outline_width);
|
||||
}
|
||||
|
||||
int get_text_height(const char *text, size_t font_number) {
|
||||
if (font_number >= _GP(fonts).size() || !_GP(fonts)[font_number].Renderer)
|
||||
return 0;
|
||||
return _GP(fonts)[font_number].Renderer->GetTextHeight(text, font_number);
|
||||
}
|
||||
|
||||
int get_font_outline(size_t font_number) {
|
||||
if (font_number >= _GP(fonts).size())
|
||||
return FONT_OUTLINE_NONE;
|
||||
return _GP(fonts)[font_number].Info.Outline;
|
||||
}
|
||||
|
||||
int get_font_outline_thickness(size_t font_number) {
|
||||
if (font_number >= _GP(fonts).size())
|
||||
return 0;
|
||||
return _GP(fonts)[font_number].Info.AutoOutlineThickness;
|
||||
}
|
||||
|
||||
void set_font_outline(size_t font_number, int outline_type,
|
||||
enum FontInfo::AutoOutlineStyle style, int thickness) {
|
||||
if (font_number >= _GP(fonts).size())
|
||||
return;
|
||||
_GP(fonts)[font_number].Info.Outline = outline_type;
|
||||
_GP(fonts)[font_number].Info.AutoOutlineStyle = style;
|
||||
_GP(fonts)[font_number].Info.AutoOutlineThickness = thickness;
|
||||
}
|
||||
|
||||
bool is_font_antialiased(size_t font_number) {
|
||||
if (font_number >= _GP(fonts).size())
|
||||
return false;
|
||||
return ShouldAntiAliasText() && !is_bitmap_font(font_number);
|
||||
}
|
||||
|
||||
int get_font_height(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].Metrics.CompatHeight;
|
||||
}
|
||||
|
||||
int get_font_height_outlined(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return 0;
|
||||
int self_height = _GP(fonts)[fontNumber].Metrics.CompatHeight;
|
||||
int outline = _GP(fonts)[fontNumber].Info.Outline;
|
||||
if (outline < 0 || static_cast<size_t>(outline) > _GP(fonts).size()) { // FONT_OUTLINE_AUTO or FONT_OUTLINE_NONE
|
||||
return self_height + 2 * _GP(fonts)[fontNumber].Info.AutoOutlineThickness;
|
||||
}
|
||||
int outline_height = _GP(fonts)[outline].Metrics.CompatHeight;
|
||||
return MAX(self_height, outline_height);
|
||||
}
|
||||
|
||||
int get_font_surface_height(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].Metrics.ExtentHeight();
|
||||
}
|
||||
|
||||
std::pair<int, int> get_font_surface_extent(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return std::make_pair(0, 0);
|
||||
return _GP(fonts)[fontNumber].Metrics.VExtent;
|
||||
}
|
||||
|
||||
int get_font_linespacing(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].LineSpacingCalc;
|
||||
}
|
||||
|
||||
void set_font_linespacing(size_t fontNumber, int spacing) {
|
||||
if (fontNumber < _GP(fonts).size()) {
|
||||
_GP(fonts)[fontNumber].Info.Flags &= ~FFLG_DEFLINESPACING;
|
||||
_GP(fonts)[fontNumber].Info.LineSpacing = spacing;
|
||||
_GP(fonts)[fontNumber].LineSpacingCalc = spacing;
|
||||
}
|
||||
}
|
||||
|
||||
int get_text_lines_height(size_t fontNumber, size_t numlines) {
|
||||
if (fontNumber >= _GP(fonts).size() || numlines == 0)
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].LineSpacingCalc * (numlines - 1) +
|
||||
(_GP(fonts)[fontNumber].Metrics.CompatHeight +
|
||||
2 * _GP(fonts)[fontNumber].Info.AutoOutlineThickness);
|
||||
}
|
||||
|
||||
int get_text_lines_surf_height(size_t fontNumber, size_t numlines) {
|
||||
if (fontNumber >= _GP(fonts).size() || numlines == 0)
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].LineSpacingCalc * (numlines - 1) +
|
||||
(_GP(fonts)[fontNumber].Metrics.RealHeight +
|
||||
2 * _GP(fonts)[fontNumber].Info.AutoOutlineThickness);
|
||||
}
|
||||
|
||||
// Replaces AGS-specific linebreak tags with common '\n'
|
||||
void unescape_script_string(const char *cstr, std::vector<char> &out) {
|
||||
out.clear();
|
||||
// Handle the special case of the first char
|
||||
if (cstr[0] == '[') {
|
||||
out.push_back('\n');
|
||||
cstr++;
|
||||
}
|
||||
// Replace all other occurrences as they're found
|
||||
// NOTE: we do not need to decode utf8 here, because
|
||||
// we are only searching for low-code ascii chars.
|
||||
const char *off;
|
||||
for (off = cstr; *off; ++off) {
|
||||
if (*off != '[') continue;
|
||||
if (*(off - 1) == '\\') {
|
||||
// convert \[ into [
|
||||
out.insert(out.end(), cstr, off - 1);
|
||||
out.push_back('[');
|
||||
} else {
|
||||
// convert [ into \n
|
||||
out.insert(out.end(), cstr, off);
|
||||
out.push_back('\n');
|
||||
}
|
||||
cstr = off + 1;
|
||||
}
|
||||
out.insert(out.end(), cstr, off + 1);
|
||||
}
|
||||
|
||||
// Break up the text into lines
|
||||
size_t split_lines(const char *todis, SplitLines &lines, int wii, int fonnt, size_t max_lines) {
|
||||
// NOTE: following hack accommodates for the legacy math mistake in split_lines.
|
||||
// It's hard to tell how crucial it is for the game looks, so research may be needed.
|
||||
// TODO: IMHO this should rely not on game format, but script API level, because it
|
||||
// defines necessary adjustments to game scripts. If you want to fix this, find a way to
|
||||
// pass this flag here all the way from game.options[OPT_BASESCRIPTAPI] (or game format).
|
||||
//
|
||||
// if (game.options[OPT_BASESCRIPTAPI] < $Your current version$)
|
||||
wii -= 1;
|
||||
|
||||
lines.Reset();
|
||||
unescape_script_string(todis, lines.LineBuf);
|
||||
char *theline = &lines.LineBuf.front();
|
||||
|
||||
char *scan_ptr = theline;
|
||||
char *prev_ptr = theline;
|
||||
char *last_whitespace = nullptr;
|
||||
while (1) {
|
||||
char *split_at = nullptr;
|
||||
|
||||
if (*scan_ptr == 0) {
|
||||
// end of the text, add the last line if necessary
|
||||
if (scan_ptr > theline) {
|
||||
lines.Add(theline);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (*scan_ptr == ' ')
|
||||
last_whitespace = scan_ptr;
|
||||
|
||||
// force end of line with the \n character
|
||||
if (*scan_ptr == '\n') {
|
||||
split_at = scan_ptr;
|
||||
// otherwise, see if we are too wide
|
||||
} else {
|
||||
// temporarily terminate the line in the *next* char and test its width
|
||||
char *next_ptr = scan_ptr;
|
||||
ugetx(&next_ptr);
|
||||
const int next_chwas = ugetc(next_ptr);
|
||||
*next_ptr = 0;
|
||||
|
||||
if (get_text_width_outlined(theline, fonnt) > wii) {
|
||||
// line is too wide, order the split
|
||||
if (last_whitespace)
|
||||
// revert to the last whitespace
|
||||
split_at = last_whitespace;
|
||||
else
|
||||
// single very wide word, display as much as possible
|
||||
split_at = prev_ptr;
|
||||
}
|
||||
|
||||
// restore the character that was there before
|
||||
usetc(next_ptr, next_chwas);
|
||||
}
|
||||
|
||||
if (split_at == nullptr) {
|
||||
prev_ptr = scan_ptr;
|
||||
ugetx(&scan_ptr);
|
||||
} else {
|
||||
// check if even one char cannot fit...
|
||||
if (split_at == theline && !((*theline == ' ') || (*theline == '\n'))) {
|
||||
// cannot split with current width restriction
|
||||
lines.Reset();
|
||||
break;
|
||||
}
|
||||
// add this line; do the temporary terminator trick again
|
||||
const int next_chwas = ugetc(split_at);
|
||||
*split_at = 0;
|
||||
lines.Add(theline);
|
||||
usetc(split_at, next_chwas);
|
||||
// check if too many lines
|
||||
if (lines.Count() >= max_lines) {
|
||||
lines[lines.Count() - 1].Append("...");
|
||||
break;
|
||||
}
|
||||
// the next line starts from the split point
|
||||
theline = split_at;
|
||||
// skip the space or new line that caused the line break
|
||||
if ((*theline == ' ') || (*theline == '\n'))
|
||||
theline++;
|
||||
scan_ptr = theline;
|
||||
prev_ptr = theline;
|
||||
last_whitespace = nullptr;
|
||||
}
|
||||
}
|
||||
return lines.Count();
|
||||
}
|
||||
|
||||
void wouttextxy(Shared::Bitmap *ds, int xxx, int yyy, size_t fontNumber, color_t text_color, const char *texx) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return;
|
||||
yyy += _GP(fonts)[fontNumber].Info.YOffset;
|
||||
if (yyy > ds->GetClip().Bottom)
|
||||
return; // each char is clipped but this speeds it up
|
||||
|
||||
if (_GP(fonts)[fontNumber].Renderer != nullptr) {
|
||||
if (text_color == makeacol32(255, 0, 255, 255)) { // transparent color (magenta)
|
||||
// WORKAROUND: Some Allegro routines are not implemented and alfont treats some magenta texts as invisible
|
||||
// even if the alpha channel is fully opaque
|
||||
// Slightly change the value if the game uses that color for fonts, so that they don't turn invisible
|
||||
debug(0, "Overriding transparent text color!");
|
||||
text_color--;
|
||||
}
|
||||
_GP(fonts)[fontNumber].Renderer->RenderText(texx, fontNumber, (BITMAP *)ds->GetAllegroBitmap(), xxx, yyy, text_color);
|
||||
}
|
||||
}
|
||||
|
||||
void set_fontinfo(size_t fontNumber, const FontInfo &finfo) {
|
||||
if (fontNumber < _GP(fonts).size() && _GP(fonts)[fontNumber].Renderer) {
|
||||
_GP(fonts)[fontNumber].Info = finfo;
|
||||
font_post_init(fontNumber);
|
||||
}
|
||||
}
|
||||
|
||||
FontInfo get_fontinfo(size_t font_number) {
|
||||
if (font_number < _GP(fonts).size())
|
||||
return _GP(fonts)[font_number].Info;
|
||||
return FontInfo();
|
||||
}
|
||||
|
||||
// Loads a font from disk
|
||||
bool load_font_size(size_t fontNumber, const FontInfo &font_info) {
|
||||
if (_GP(fonts).size() <= fontNumber)
|
||||
_GP(fonts).resize(fontNumber + 1);
|
||||
else
|
||||
wfreefont(fontNumber);
|
||||
FontRenderParams params;
|
||||
params.SizeMultiplier = font_info.SizeMultiplier;
|
||||
params.LoadMode = (font_info.Flags & FFLG_LOADMODEMASK);
|
||||
FontMetrics metrics;
|
||||
|
||||
Font &font = _GP(fonts)[fontNumber];
|
||||
String src_filename;
|
||||
if (_GP(ttfRenderer).LoadFromDiskEx(fontNumber, font_info.Size, &src_filename, ¶ms, &metrics)) {
|
||||
font.Renderer = &_GP(ttfRenderer);
|
||||
font.Renderer2 = &_GP(ttfRenderer);
|
||||
font.RendererInt = &_GP(ttfRenderer);
|
||||
} else if (_GP(wfnRenderer).LoadFromDiskEx(fontNumber, font_info.Size, &src_filename, ¶ms, &metrics)) {
|
||||
font.Renderer = &_GP(wfnRenderer);
|
||||
font.Renderer2 = &_GP(wfnRenderer);
|
||||
font.RendererInt = &_GP(wfnRenderer);
|
||||
}
|
||||
|
||||
if (!font.Renderer)
|
||||
return false;
|
||||
|
||||
font.Info = font_info;
|
||||
font.Metrics = metrics;
|
||||
font_post_init(fontNumber);
|
||||
|
||||
Debug::Printf("Loaded font %d: %s, req size: %d; nominal h: %d, real h: %d, extent: %d,%d",
|
||||
fontNumber, src_filename.GetCStr(), font_info.Size, font.Metrics.NominalHeight, font.Metrics.RealHeight,
|
||||
font.Metrics.VExtent.first, font.Metrics.VExtent.second);
|
||||
return true;
|
||||
}
|
||||
|
||||
void wgtprintf(Shared::Bitmap *ds, int xxx, int yyy, size_t fontNumber, color_t text_color, char *fmt, ...) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return;
|
||||
|
||||
char tbuffer[2000];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(tbuffer, sizeof(tbuffer), fmt, ap);
|
||||
va_end(ap);
|
||||
wouttextxy(ds, xxx, yyy, fontNumber, text_color, tbuffer);
|
||||
}
|
||||
|
||||
void alloc_font_outline_buffers(size_t font_number,
|
||||
Bitmap **text_stencil, Bitmap **outline_stencil,
|
||||
int text_width, int text_height, int color_depth) {
|
||||
if (font_number >= _GP(fonts).size())
|
||||
return;
|
||||
Font &f = _GP(fonts)[font_number];
|
||||
const int thick = 2 * f.Info.AutoOutlineThickness;
|
||||
if (f.TextStencil.IsNull() || (f.TextStencil.GetColorDepth() != color_depth) ||
|
||||
(f.TextStencil.GetWidth() < text_width) || (f.TextStencil.GetHeight() < text_height)) {
|
||||
int sw = f.TextStencil.IsNull() ? 0 : f.TextStencil.GetWidth();
|
||||
int sh = f.TextStencil.IsNull() ? 0 : f.TextStencil.GetHeight();
|
||||
sw = MAX(text_width, sw);
|
||||
sh = MAX(text_height, sh);
|
||||
f.TextStencil.Create(sw, sh, color_depth);
|
||||
f.OutlineStencil.Create(sw, sh + thick, color_depth);
|
||||
f.TextStencilSub.CreateSubBitmap(&f.TextStencil, RectWH(Size(text_width, text_height)));
|
||||
f.OutlineStencilSub.CreateSubBitmap(&f.OutlineStencil, RectWH(Size(text_width, text_height + thick)));
|
||||
} else {
|
||||
f.TextStencilSub.ResizeSubBitmap(text_width, text_height);
|
||||
f.OutlineStencilSub.ResizeSubBitmap(text_width, text_height + thick);
|
||||
}
|
||||
*text_stencil = &f.TextStencilSub;
|
||||
*outline_stencil = &f.OutlineStencilSub;
|
||||
}
|
||||
|
||||
void adjust_fonts_for_render_mode(bool aa_mode) {
|
||||
for (size_t i = 0; i < _GP(fonts).size(); ++i) {
|
||||
if (_GP(fonts)[i].RendererInt != nullptr)
|
||||
_GP(fonts)[i].RendererInt->AdjustFontForAntiAlias(i, aa_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void wfreefont(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return;
|
||||
|
||||
_GP(fonts)[fontNumber].TextStencilSub.Destroy();
|
||||
_GP(fonts)[fontNumber].OutlineStencilSub.Destroy();
|
||||
_GP(fonts)[fontNumber].TextStencil.Destroy();
|
||||
_GP(fonts)[fontNumber].OutlineStencil.Destroy();
|
||||
|
||||
if (_GP(fonts)[fontNumber].Renderer != nullptr)
|
||||
_GP(fonts)[fontNumber].Renderer->FreeMemory(fontNumber);
|
||||
|
||||
_GP(fonts)[fontNumber].Renderer = nullptr;
|
||||
}
|
||||
|
||||
void free_all_fonts() {
|
||||
for (size_t i = 0; i < _GP(fonts).size(); ++i) {
|
||||
if (_GP(fonts)[i].Renderer != nullptr)
|
||||
_GP(fonts)[i].Renderer->FreeMemory(i);
|
||||
}
|
||||
_GP(fonts).clear();
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
194
engines/ags/shared/font/fonts.h
Normal file
194
engines/ags/shared/font/fonts.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AGS_SHARED_FONT_FONTS_H
|
||||
#define AGS_SHARED_FONT_FONTS_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
#include "ags/shared/font/ags_font_renderer.h"
|
||||
#include "ags/shared/gfx/allegro_bitmap.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
class IAGSFontRenderer;
|
||||
class IAGSFontRenderer2;
|
||||
class IAGSFontRendererInternal;
|
||||
struct FontInfo;
|
||||
struct FontRenderParams;
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
struct Font {
|
||||
// Classic font renderer interface
|
||||
IAGSFontRenderer *Renderer = nullptr;
|
||||
// Extended font renderer interface (optional)
|
||||
IAGSFontRenderer2 *Renderer2 = nullptr;
|
||||
// Internal interface (only for built-in renderers)
|
||||
IAGSFontRendererInternal *RendererInt = nullptr;
|
||||
FontInfo Info;
|
||||
// Values received from the renderer and saved for the reference
|
||||
FontMetrics Metrics;
|
||||
// Precalculated linespacing, based on font properties and compat settings
|
||||
int LineSpacingCalc = 0;
|
||||
|
||||
// Outline buffers
|
||||
Bitmap TextStencil, TextStencilSub;
|
||||
Bitmap OutlineStencil, OutlineStencilSub;
|
||||
|
||||
Font() {}
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
using namespace AGS;
|
||||
|
||||
void init_font_renderer();
|
||||
void shutdown_font_renderer();
|
||||
void adjust_y_coordinate_for_text(int *ypos, size_t fontnum);
|
||||
IAGSFontRenderer *font_replace_renderer(size_t fontNumber, IAGSFontRenderer *renderer);
|
||||
IAGSFontRenderer *font_replace_renderer(size_t fontNumber, IAGSFontRenderer2 *renderer);
|
||||
void font_recalc_metrics(size_t fontNumber);
|
||||
bool font_first_renderer_loaded();
|
||||
bool is_font_loaded(size_t fontNumber);
|
||||
bool is_bitmap_font(size_t fontNumber);
|
||||
bool font_supports_extended_characters(size_t fontNumber);
|
||||
// Get font's name, if it's available, otherwise returns empty string
|
||||
const char *get_font_name(size_t fontNumber);
|
||||
// Get a collection of FFLG_* flags corresponding to this font
|
||||
int get_font_flags(size_t fontNumber);
|
||||
// TODO: with changes to WFN font renderer that implemented safe rendering of
|
||||
// strings containing invalid chars (since 3.3.1) this function is not
|
||||
// important, except for (maybe) few particular cases.
|
||||
// Furthermore, its use complicated things, because AGS could modify some texts
|
||||
// at random times (usually - drawing routines).
|
||||
// Need to check whether it is safe to completely remove it.
|
||||
void ensure_text_valid_for_font(char *text, size_t fontnum);
|
||||
// Get font's scaling multiplier
|
||||
int get_font_scaling_mul(size_t fontNumber);
|
||||
// Calculate actual width of a line of text
|
||||
int get_text_width(const char *texx, size_t fontNumber);
|
||||
// Get the maximal width of the line of text, with corresponding outlining
|
||||
int get_text_width_outlined(const char *text, size_t font_number);
|
||||
// Get the maximal height of the line of text;
|
||||
// note that this won't be a nominal font's height, but the max of each met glyph's graphical height.
|
||||
int get_text_height(const char *text, size_t font_number);
|
||||
// Get font's height; this value is used for logical arrangement of UI elements;
|
||||
// note that this is a "formal" font height, that may have different value
|
||||
// depending on compatibility mode (used when running old games);
|
||||
int get_font_height(size_t fontNumber);
|
||||
// Get the maximal height of the given font, with corresponding outlining
|
||||
int get_font_height_outlined(size_t fontNumber);
|
||||
// Get font's surface height: this always returns the height enough to accommodate
|
||||
// font letters on a bitmap or a texture; the distinction is needed for compatibility reasons
|
||||
int get_font_surface_height(size_t fontNumber);
|
||||
// Get font's maximal graphical extent: this means the farthest vertical positions of glyphs,
|
||||
// relative to the "pen" position. Besides letting to calculate the surface height,
|
||||
// this information also lets to detect if some of the glyphs may appear above y0.
|
||||
std::pair<int, int> get_font_surface_extent(size_t fontNumber);
|
||||
// Get font's line spacing
|
||||
int get_font_linespacing(size_t fontNumber);
|
||||
// Set font's line spacing
|
||||
void set_font_linespacing(size_t fontNumber, int spacing);
|
||||
// Get font's outline type
|
||||
int get_font_outline(size_t font_number);
|
||||
// Get font's automatic outline thickness (if set)
|
||||
int get_font_outline_thickness(size_t font_number);
|
||||
// Gets the total maximal height of the given number of lines printed with the given font;
|
||||
// note that this uses formal font height, for compatibility purposes
|
||||
int get_text_lines_height(size_t fontNumber, size_t numlines);
|
||||
// Gets the height of a graphic surface enough to accommodate this number of text lines;
|
||||
// note this accounts for the real pixel font height
|
||||
int get_text_lines_surf_height(size_t fontNumber, size_t numlines);
|
||||
// Set font's outline type
|
||||
void set_font_outline(size_t font_number, int outline_type,
|
||||
enum FontInfo::AutoOutlineStyle style = FontInfo::kSquared, int thickness = 1);
|
||||
bool is_font_antialiased(size_t font_number);
|
||||
// Outputs a single line of text on the defined position on bitmap, using defined font, color and parameters
|
||||
void wouttextxy(Shared::Bitmap *ds, int xxx, int yyy, size_t fontNumber, color_t text_color, const char *texx);
|
||||
// Assigns FontInfo to the font
|
||||
void set_fontinfo(size_t fontNumber, const FontInfo &finfo);
|
||||
// Gets full information about the font
|
||||
FontInfo get_fontinfo(size_t font_number);
|
||||
// Loads a font from disk
|
||||
bool load_font_size(size_t fontNumber, const FontInfo &font_info); void wgtprintf(Shared::Bitmap *ds, int xxx, int yyy, size_t fontNumber, color_t text_color, char *fmt, ...);
|
||||
// Allocates two outline stencil buffers, or returns previously creates ones;
|
||||
// these buffers are owned by the font, they should not be deleted by the caller.
|
||||
void alloc_font_outline_buffers(size_t font_number,
|
||||
Shared::Bitmap **text_stencil, Shared::Bitmap **outline_stencil,
|
||||
int text_width, int text_height, int color_depth);
|
||||
// Perform necessary adjustments on all fonts in case the text render mode changed (anti-aliasing etc)
|
||||
void adjust_fonts_for_render_mode(bool aa_mode);
|
||||
// Free particular font's data
|
||||
void wfreefont(size_t fontNumber);
|
||||
// Free all fonts data
|
||||
void free_all_fonts();
|
||||
|
||||
// Tells if the text should be antialiased when possible
|
||||
bool ShouldAntiAliasText();
|
||||
|
||||
// SplitLines class represents a list of lines and is meant to reduce
|
||||
// subsequent memory (de)allocations if used often during game loops
|
||||
// and drawing. For that reason it is not equivalent to std::vector,
|
||||
// but keeps constructed String buffers intact for most time.
|
||||
// TODO: implement proper strings pool.
|
||||
class SplitLines {
|
||||
public:
|
||||
inline size_t Count() const {
|
||||
return _count;
|
||||
}
|
||||
inline const Shared::String &operator[](size_t i) const {
|
||||
return _pool[i];
|
||||
}
|
||||
inline Shared::String &operator[](size_t i) {
|
||||
return _pool[i];
|
||||
}
|
||||
inline void Clear() {
|
||||
_pool.clear();
|
||||
_count = 0;
|
||||
}
|
||||
inline void Reset() {
|
||||
_count = 0;
|
||||
}
|
||||
inline void Add(const char *cstr) {
|
||||
if (_pool.size() == _count) _pool.resize(_count + 1);
|
||||
_pool[_count++].SetString(cstr);
|
||||
}
|
||||
|
||||
// An auxiliary line processing buffer
|
||||
std::vector<char> LineBuf;
|
||||
|
||||
private:
|
||||
std::vector<Shared::String> _pool;
|
||||
size_t _count; // actual number of lines in use
|
||||
};
|
||||
|
||||
// Break up the text into lines restricted by the given width;
|
||||
// returns number of lines, or 0 if text cannot be split well to fit in this width
|
||||
size_t split_lines(const char *texx, SplitLines &lines, int width, int fontNumber, size_t max_lines = -1);
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
187
engines/ags/shared/font/ttf_font_renderer.cpp
Normal file
187
engines/ags/shared/font/ttf_font_renderer.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ags/shared/font/ttf_font_renderer.h"
|
||||
#include "ags/lib/alfont/alfont.h"
|
||||
#include "ags/shared/core/platform.h"
|
||||
#include "ags/shared/ac/game_version.h"
|
||||
#include "ags/globals.h"
|
||||
#include "ags/shared/core/asset_manager.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
#include "ags/shared/font/fonts.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
// ***** TTF RENDERER *****
|
||||
void TTFFontRenderer::AdjustYCoordinateForFont(int *ycoord, int /*fontNumber*/) {
|
||||
// TTF fonts already have space at the top, so try to remove the gap
|
||||
// TODO: adding -1 was here before (check the comment above),
|
||||
// but how universal is this "space at the top"?
|
||||
// Also, why is this function used only in one case of text rendering?
|
||||
// Review this after we upgrade the font library.
|
||||
ycoord[0]--;
|
||||
}
|
||||
|
||||
void TTFFontRenderer::EnsureTextValidForFont(char * /*text*/, int /*fontNumber*/) {
|
||||
// do nothing, TTF can handle all characters
|
||||
}
|
||||
|
||||
int TTFFontRenderer::GetTextWidth(const char *text, int fontNumber) {
|
||||
return alfont_text_length(_fontData[fontNumber].AlFont, text);
|
||||
}
|
||||
|
||||
int TTFFontRenderer::GetTextHeight(const char * /*text*/, int fontNumber) {
|
||||
return alfont_get_font_real_height(_fontData[fontNumber].AlFont);
|
||||
}
|
||||
|
||||
void TTFFontRenderer::RenderText(const char *text, int fontNumber, BITMAP *destination, int x, int y, int colour) {
|
||||
if (y > destination->cb) // optimisation
|
||||
return;
|
||||
|
||||
// Y - 1 because it seems to get drawn down a bit
|
||||
if ((ShouldAntiAliasText()) && (bitmap_color_depth(destination) > 8))
|
||||
alfont_textout_aa(destination, _fontData[fontNumber].AlFont, text, x, y - 1, colour);
|
||||
else
|
||||
alfont_textout(destination, _fontData[fontNumber].AlFont, text, x, y - 1, colour);
|
||||
}
|
||||
|
||||
bool TTFFontRenderer::LoadFromDisk(int fontNumber, int fontSize) {
|
||||
return LoadFromDiskEx(fontNumber, fontSize, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool TTFFontRenderer::IsBitmapFont() {
|
||||
return false;
|
||||
}
|
||||
|
||||
static int GetAlfontFlags(int load_mode) {
|
||||
int flags = ALFONT_FLG_FORCE_RESIZE | ALFONT_FLG_SELECT_NOMINAL_SZ;
|
||||
// Compatibility: font ascender is always adjusted to the formal font's height;
|
||||
// EXCEPTION: not if it's a game made before AGS 3.4.1 with TTF anti-aliasing
|
||||
// (the reason is uncertain, but this is to emulate old engine's behavior).
|
||||
if (((load_mode & FFLG_ASCENDERFIXUP) != 0) &&
|
||||
!(ShouldAntiAliasText() && (_G(loaded_game_file_version) < kGameVersion_341)))
|
||||
flags |= ALFONT_FLG_ASCENDER_EQ_HEIGHT;
|
||||
// Precalculate real glyphs extent (will make loading fonts relatively slower)
|
||||
flags |= ALFONT_FLG_PRECALC_MAX_CBOX;
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Loads a TTF font of a certain size
|
||||
static ALFONT_FONT *LoadTTF(const String &filename, int fontSize, int alfont_flags) {
|
||||
std::unique_ptr<Stream> reader(_GP(AssetMgr)->OpenAsset(filename));
|
||||
if (!reader)
|
||||
return nullptr;
|
||||
|
||||
const size_t lenof = reader->GetLength();
|
||||
std::vector<char> buf; buf.resize(lenof);
|
||||
reader->Read(&buf.front(), lenof);
|
||||
reader.reset();
|
||||
|
||||
ALFONT_FONT *alfptr = alfont_load_font_from_mem(&buf.front(), lenof);
|
||||
if (!alfptr)
|
||||
return nullptr;
|
||||
alfont_set_font_size_ex(alfptr, fontSize, alfont_flags);
|
||||
return alfptr;
|
||||
}
|
||||
|
||||
// Fill the FontMetrics struct from the given ALFONT
|
||||
static void FillMetrics(ALFONT_FONT *alfptr, FontMetrics *metrics) {
|
||||
metrics->NominalHeight = alfont_get_font_height(alfptr);
|
||||
metrics->RealHeight = alfont_get_font_real_height(alfptr);
|
||||
metrics->CompatHeight = metrics->NominalHeight; // just set to default here
|
||||
alfont_get_font_real_vextent(alfptr, &metrics->VExtent.first, &metrics->VExtent.second);
|
||||
// fixup vextent to be *not less* than realheight
|
||||
metrics->VExtent.first = std::min(0, metrics->VExtent.first);
|
||||
metrics->VExtent.second = std::max(metrics->RealHeight, metrics->VExtent.second);
|
||||
}
|
||||
|
||||
bool TTFFontRenderer::LoadFromDiskEx(int fontNumber, int fontSize, String *src_filename,
|
||||
const FontRenderParams *params, FontMetrics *metrics) {
|
||||
String filename = String::FromFormat("agsfnt%d.ttf", fontNumber);
|
||||
if (fontSize <= 0)
|
||||
fontSize = 8; // compatibility fix
|
||||
assert(params);
|
||||
FontRenderParams f_params = params ? *params : FontRenderParams();
|
||||
if (f_params.SizeMultiplier > 1)
|
||||
fontSize *= f_params.SizeMultiplier;
|
||||
|
||||
ALFONT_FONT *alfptr = LoadTTF(filename, fontSize,
|
||||
GetAlfontFlags(f_params.LoadMode));
|
||||
if (!alfptr)
|
||||
return false;
|
||||
|
||||
_fontData[fontNumber].AlFont = alfptr;
|
||||
_fontData[fontNumber].Params = f_params;
|
||||
if (src_filename)
|
||||
*src_filename = filename;
|
||||
if (metrics)
|
||||
FillMetrics(alfptr, metrics);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *TTFFontRenderer::GetFontName(int fontNumber) {
|
||||
return alfont_get_name(_fontData[fontNumber].AlFont);
|
||||
}
|
||||
|
||||
int TTFFontRenderer::GetFontHeight(int fontNumber) {
|
||||
return alfont_get_font_real_height(_fontData[fontNumber].AlFont);
|
||||
}
|
||||
|
||||
void TTFFontRenderer::GetFontMetrics(int fontNumber, FontMetrics *metrics) {
|
||||
FillMetrics(_fontData[fontNumber].AlFont, metrics);
|
||||
}
|
||||
|
||||
void TTFFontRenderer::AdjustFontForAntiAlias(int fontNumber, bool /*aa_mode*/) {
|
||||
if (_G(loaded_game_file_version) < kGameVersion_341) {
|
||||
ALFONT_FONT *alfptr = _fontData[fontNumber].AlFont;
|
||||
const FontRenderParams ¶ms = _fontData[fontNumber].Params;
|
||||
int old_height = alfont_get_font_height(alfptr);
|
||||
alfont_set_font_size_ex(alfptr, old_height, GetAlfontFlags(params.LoadMode));
|
||||
}
|
||||
}
|
||||
|
||||
void TTFFontRenderer::FreeMemory(int fontNumber) {
|
||||
alfont_destroy_font(_fontData[fontNumber].AlFont);
|
||||
_fontData.erase(fontNumber);
|
||||
}
|
||||
|
||||
bool TTFFontRenderer::MeasureFontOfPointSize(const String &filename, int size_pt, FontMetrics *metrics) {
|
||||
ALFONT_FONT *alfptr = LoadTTF(filename, size_pt, ALFONT_FLG_FORCE_RESIZE | ALFONT_FLG_SELECT_NOMINAL_SZ);
|
||||
if (!alfptr)
|
||||
return false;
|
||||
FillMetrics(alfptr, metrics);
|
||||
alfont_destroy_font(alfptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TTFFontRenderer::MeasureFontOfPixelHeight(const String &filename, int pixel_height, FontMetrics *metrics) {
|
||||
ALFONT_FONT *alfptr = LoadTTF(filename, pixel_height, ALFONT_FLG_FORCE_RESIZE);
|
||||
if (!alfptr)
|
||||
return false;
|
||||
FillMetrics(alfptr, metrics);
|
||||
alfont_destroy_font(alfptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
82
engines/ags/shared/font/ttf_font_renderer.h
Normal file
82
engines/ags/shared/font/ttf_font_renderer.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AGS_SHARED_FONT_TTF_FONT_RENDERER_H
|
||||
#define AGS_SHARED_FONT_TTF_FONT_RENDERER_H
|
||||
|
||||
#include "common/std/map.h"
|
||||
#include "ags/shared/font/ags_font_renderer.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
struct ALFONT_FONT;
|
||||
|
||||
class TTFFontRenderer : public IAGSFontRendererInternal {
|
||||
public:
|
||||
virtual ~TTFFontRenderer() {}
|
||||
|
||||
// IAGSFontRenderer implementation
|
||||
bool LoadFromDisk(int fontNumber, int fontSize) override;
|
||||
void FreeMemory(int fontNumber) override;
|
||||
bool SupportsExtendedCharacters(int /*fontNumber*/) override {
|
||||
return true;
|
||||
}
|
||||
int GetTextWidth(const char *text, int fontNumber) override;
|
||||
int GetTextHeight(const char *text, int fontNumber) override;
|
||||
void RenderText(const char *text, int fontNumber, BITMAP *destination, int x, int y, int colour) override;
|
||||
void AdjustYCoordinateForFont(int *ycoord, int fontNumber) override;
|
||||
void EnsureTextValidForFont(char *text, int fontNumber) override;
|
||||
|
||||
// IAGSFontRenderer2 implementation
|
||||
int GetVersion() override { return 26; /* first compatible engine API version */ }
|
||||
const char *GetRendererName() override { return "TTFFontRenderer"; }
|
||||
const char *GetFontName(int fontNumber) override;
|
||||
int GetFontHeight(int fontNumber) override;
|
||||
int GetLineSpacing(int fontNumber) override { return 0; /* no specific spacing */ }
|
||||
|
||||
// IAGSFontRendererInternal implementation
|
||||
bool IsBitmapFont() override;
|
||||
bool LoadFromDiskEx(int fontNumber, int fontSize, AGS::Shared::String *src_filename,
|
||||
const FontRenderParams *params, FontMetrics *metrics) override;
|
||||
void GetFontMetrics(int fontNumber, FontMetrics *metrics) override;
|
||||
void AdjustFontForAntiAlias(int fontNumber, bool aa_mode) override;
|
||||
|
||||
//
|
||||
// Utility functions
|
||||
//
|
||||
// Try load the TTF font using provided point size, and report its metrics
|
||||
static bool MeasureFontOfPointSize(const AGS::Shared::String &filename, int size_pt, FontMetrics *metrics);
|
||||
// Try load the TTF font, find the point size which results in pixel height
|
||||
// as close to the requested as possible; report its metrics
|
||||
static bool MeasureFontOfPixelHeight(const AGS::Shared::String &filename, int pixel_height, FontMetrics *metrics);
|
||||
|
||||
private:
|
||||
struct FontData {
|
||||
ALFONT_FONT *AlFont;
|
||||
FontRenderParams Params;
|
||||
};
|
||||
std::map<int, FontData> _fontData;
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
196
engines/ags/shared/font/wfn_font.cpp
Normal file
196
engines/ags/shared/font/wfn_font.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/std/algorithm.h"
|
||||
#include "ags/shared/font/wfn_font.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/shared/util/memory.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
static const char *WFN_FILE_SIGNATURE = "WGT Font File ";
|
||||
static const size_t WFN_FILE_SIG_LENGTH = 15;
|
||||
static const size_t MinCharDataSize = sizeof(uint16_t) * 2;
|
||||
|
||||
WFNChar::WFNChar()
|
||||
: Width(0)
|
||||
, Height(0)
|
||||
, Data(nullptr) {
|
||||
}
|
||||
|
||||
void WFNChar::RestrictToBytes(size_t bytes) {
|
||||
if (bytes < GetRequiredPixelSize())
|
||||
Height = static_cast<uint16_t>(bytes / GetRowByteCount());
|
||||
}
|
||||
|
||||
const WFNChar &WFNFont::GetChar(uint16_t code) const {
|
||||
return code < _refs.size() ? *_refs[code] : _G(emptyChar);
|
||||
}
|
||||
|
||||
void WFNFont::Clear() {
|
||||
_refs.clear();
|
||||
_items.clear();
|
||||
_pixelData.clear();
|
||||
}
|
||||
|
||||
WFNError WFNFont::ReadFromFile(Stream *in, const soff_t data_size) {
|
||||
Clear();
|
||||
|
||||
const soff_t used_data_size = data_size > 0 ? data_size : in->GetLength();
|
||||
|
||||
// Read font header
|
||||
char sig[WFN_FILE_SIG_LENGTH];
|
||||
in->Read(sig, WFN_FILE_SIG_LENGTH);
|
||||
if (strncmp(sig, WFN_FILE_SIGNATURE, WFN_FILE_SIG_LENGTH) != 0) {
|
||||
Debug::Printf(kDbgMsg_Error, "\tWFN: bad format signature");
|
||||
return kWFNErr_BadSignature; // bad format
|
||||
}
|
||||
|
||||
const soff_t table_addr = static_cast<uint16_t>(in->ReadInt16()); // offset table relative address
|
||||
if (table_addr < (soff_t)(WFN_FILE_SIG_LENGTH + sizeof(uint16_t)) || table_addr >= used_data_size) {
|
||||
Debug::Printf(kDbgMsg_Error, "\tWFN: bad table address: %llu (%llu - %llu)", static_cast<int64>(table_addr),
|
||||
static_cast<int64>(WFN_FILE_SIG_LENGTH + sizeof(uint16_t)), static_cast<int64>(used_data_size));
|
||||
return kWFNErr_BadTableAddress; // bad table address
|
||||
}
|
||||
|
||||
const soff_t offset_table_size = used_data_size - table_addr;
|
||||
const soff_t raw_data_offset = WFN_FILE_SIG_LENGTH + sizeof(uint16_t);
|
||||
const size_t total_char_data = static_cast<size_t>(table_addr - raw_data_offset);
|
||||
const size_t char_count = static_cast<size_t>(offset_table_size / sizeof(uint16_t));
|
||||
|
||||
// We process character data in three steps:
|
||||
// 1. For every character store offset of character item, excluding
|
||||
// duplicates.
|
||||
// 2. Allocate memory for character items and pixel array and copy
|
||||
// appropriate data; test for possible format corruption.
|
||||
// 3. Create array of references from characters to items; same item may be
|
||||
// referenced by many characters.
|
||||
WFNError err = kWFNErr_NoError;
|
||||
|
||||
if (total_char_data == 0u || char_count == 0u)
|
||||
return kWFNErr_NoError; // no items
|
||||
|
||||
// Read character data array
|
||||
std::vector<uint8_t> raw_data; raw_data.resize(total_char_data);
|
||||
in->Read(&raw_data.front(), total_char_data);
|
||||
|
||||
// Read offset table
|
||||
std::vector<uint16_t> offset_table; offset_table.resize(char_count);
|
||||
in->ReadArrayOfInt16(reinterpret_cast<int16_t *>(&offset_table.front()), char_count);
|
||||
|
||||
// Read all referenced offsets in an unsorted vector
|
||||
std::vector<uint16_t> offs;
|
||||
offs.reserve(char_count); // reserve max possible offsets
|
||||
for (size_t i = 0; i < char_count; ++i) {
|
||||
const uint16_t off = offset_table[i];
|
||||
if (off < raw_data_offset || (soff_t)(off + MinCharDataSize) > table_addr) {
|
||||
Debug::Printf("\tWFN: character %d -- bad item offset: %d (%d - %d, +%d)",
|
||||
i, off, raw_data_offset, table_addr, MinCharDataSize);
|
||||
err = kWFNErr_HasBadCharacters; // warn about potentially corrupt format
|
||||
continue; // bad character offset
|
||||
}
|
||||
offs.push_back(off);
|
||||
}
|
||||
// sort offsets vector and remove any duplicates
|
||||
std::sort(offs.begin(), offs.end());
|
||||
#if AGS_PLATFORM_SCUMMVM
|
||||
// TODO: See if this works correctly
|
||||
std::unique(offs.begin(), offs.end());
|
||||
#else
|
||||
std::vector<uint16_t>(offs.begin(), std::unique(offs.begin(), offs.end())).swap(offs);
|
||||
#endif
|
||||
|
||||
// Now that we know number of valid character items, parse and store character data
|
||||
WFNChar init_ch;
|
||||
_items.resize(offs.size());
|
||||
size_t total_pixel_size = 0;
|
||||
for (size_t i = 0; i < _items.size(); ++i) {
|
||||
const uint8_t *p_data = &raw_data[offs[i] - raw_data_offset];
|
||||
init_ch.Width = Memory::ReadInt16LE(p_data);
|
||||
init_ch.Height = Memory::ReadInt16LE(p_data + sizeof(uint16_t));
|
||||
total_pixel_size += init_ch.GetRequiredPixelSize();
|
||||
_items[i] = init_ch;
|
||||
}
|
||||
|
||||
// Now that we know actual size of pixels in use, create pixel data array;
|
||||
// since the items are sorted, the pixel data will be stored sequentially as well.
|
||||
// At this point offs and _items have related elements in the same order.
|
||||
_pixelData.resize(total_pixel_size);
|
||||
std::vector<uint8_t>::iterator pixel_it = _pixelData.begin(); // write ptr
|
||||
for (size_t i = 0; i < _items.size(); ++i) {
|
||||
const size_t pixel_data_size = _items[i].GetRequiredPixelSize();
|
||||
if (pixel_data_size == 0) {
|
||||
Debug::Printf("\tWFN: item at off %d -- null size", offs[i]);
|
||||
err = kWFNErr_HasBadCharacters;
|
||||
continue; // just an empty character
|
||||
}
|
||||
const uint16_t raw_off = offs[i] - raw_data_offset + MinCharDataSize; // offset in raw array
|
||||
size_t src_size = pixel_data_size;
|
||||
if (i + 1 != _items.size() && (soff_t)(raw_off + src_size) > offs[i + 1] - raw_data_offset) { // character pixel data overlaps next character
|
||||
Debug::Printf("\tWFN: item at off %d -- pixel data overlaps next known item (at %d, +%d)",
|
||||
offs[i], offs[i + 1], MinCharDataSize + src_size);
|
||||
err = kWFNErr_HasBadCharacters; // warn about potentially corrupt format
|
||||
src_size = offs[i + 1] - offs[i] - MinCharDataSize;
|
||||
}
|
||||
|
||||
if (raw_off + src_size > total_char_data) { // character pixel data overflow buffer
|
||||
Debug::Printf("\tWFN: item at off %d -- pixel data exceeds available data (at %d, +%d)",
|
||||
offs[i], table_addr, MinCharDataSize + src_size);
|
||||
err = kWFNErr_HasBadCharacters; // warn about potentially corrupt format
|
||||
src_size = total_char_data - raw_off;
|
||||
}
|
||||
_items[i].RestrictToBytes(src_size);
|
||||
|
||||
assert(pixel_it + pixel_data_size <= _pixelData.end()); // should not normally fail
|
||||
Common::copy(raw_data.begin() + raw_off, raw_data.begin() + (raw_off + src_size), pixel_it);
|
||||
_items[i].Data = &(*pixel_it);
|
||||
pixel_it += pixel_data_size;
|
||||
}
|
||||
|
||||
// Create final reference array
|
||||
_refs.resize(char_count);
|
||||
for (size_t i = 0; i < char_count; ++i) {
|
||||
const uint16_t off = offset_table[i];
|
||||
// if bad character offset - reference empty character
|
||||
if (off < raw_data_offset || (soff_t)(off + MinCharDataSize) > table_addr) {
|
||||
_refs[i] = &_G(emptyChar);
|
||||
} else {
|
||||
// in usual case the offset table references items in strict order
|
||||
if (i < _items.size() && offs[i] == off)
|
||||
_refs[i] = &_items[i];
|
||||
else {
|
||||
// we know beforehand that such item must exist
|
||||
std::vector<uint16_t>::const_iterator at = std::lower_bound(offs.begin(), offs.end(), off);
|
||||
assert(at != offs.end() && *at == off && // should not normally fail
|
||||
at - offs.begin() >= 0 && static_cast<size_t>(at - offs.begin()) < _items.size());
|
||||
_refs[i] = &_items[at - offs.begin()]; // set up reference to item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
108
engines/ags/shared/font/wfn_font.h
Normal file
108
engines/ags/shared/font/wfn_font.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// WFNFont - an immutable AGS font object.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// WFN format:
|
||||
// - signature ( 15 )
|
||||
// - offsets table offset ( 2 )
|
||||
// - characters table (for unknown number of char items):
|
||||
// - width ( 2 )
|
||||
// - height ( 2 )
|
||||
// - pixel bits ( (width / 8 + 1) * height )
|
||||
// - any unknown data
|
||||
// - offsets table (for X chars):
|
||||
// - character offset ( 2 )
|
||||
//
|
||||
// NOTE: unfortunately, at the moment the format does not provide means to
|
||||
// know the number of supported characters for certain, and the size of the
|
||||
// data (file) is used to determine that.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_FONT_WFN_FONT_H
|
||||
#define AGS_SHARED_FONT_WFN_FONT_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/core/types.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
enum WFNError {
|
||||
kWFNErr_NoError,
|
||||
kWFNErr_BadSignature,
|
||||
kWFNErr_BadTableAddress,
|
||||
kWFNErr_HasBadCharacters
|
||||
};
|
||||
|
||||
struct WFNChar {
|
||||
uint16_t Width;
|
||||
uint16_t Height;
|
||||
const uint8_t *Data;
|
||||
|
||||
WFNChar();
|
||||
|
||||
inline size_t GetRowByteCount() const {
|
||||
return (Width + 7) / 8;
|
||||
}
|
||||
|
||||
inline size_t GetRequiredPixelSize() const {
|
||||
return GetRowByteCount() * Height;
|
||||
}
|
||||
|
||||
// Ensure character's width & height fit in given number of pixel bytes
|
||||
void RestrictToBytes(size_t bytes);
|
||||
};
|
||||
|
||||
|
||||
class WFNFont {
|
||||
public:
|
||||
inline uint16_t GetCharCount() const {
|
||||
return static_cast<uint16_t>(_refs.size());
|
||||
}
|
||||
|
||||
// Get WFN character for the given code; if the character is missing, returns empty character
|
||||
const WFNChar &GetChar(uint16_t code) const;
|
||||
|
||||
void Clear();
|
||||
// Reads WFNFont object, using data_size bytes from stream; if data_size = 0,
|
||||
// the available stream's length is used instead. Returns error code.
|
||||
WFNError ReadFromFile(AGS::Shared::Stream *in, const soff_t data_size = 0);
|
||||
|
||||
protected:
|
||||
std::vector<const WFNChar *> _refs; // reference array, contains pointers to elements of _items
|
||||
std::vector<WFNChar> _items; // actual character items
|
||||
std::vector<uint8_t> _pixelData; // pixel data array
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
163
engines/ags/shared/font/wfn_font_renderer.cpp
Normal file
163
engines/ags/shared/font/wfn_font_renderer.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ags/shared/font/wfn_font_renderer.h"
|
||||
#include "ags/shared/ac/common.h" // our_eip
|
||||
#include "ags/shared/core/asset_manager.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/shared/font/wfn_font.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
void WFNFontRenderer::AdjustYCoordinateForFont(int *ycoord, int fontNumber) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
void WFNFontRenderer::EnsureTextValidForFont(char *text, int fontNumber) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
int WFNFontRenderer::GetTextWidth(const char *text, int fontNumber) {
|
||||
const WFNFont *font = _fontData[fontNumber].Font;
|
||||
const FontRenderParams ¶ms = _fontData[fontNumber].Params;
|
||||
int text_width = 0;
|
||||
|
||||
for (int code = ugetxc(&text); code; code = ugetxc(&text)) {
|
||||
text_width += font->GetChar(code).Width;
|
||||
}
|
||||
return text_width * params.SizeMultiplier;
|
||||
}
|
||||
|
||||
int WFNFontRenderer::GetTextHeight(const char *text, int fontNumber) {
|
||||
const WFNFont *font = _fontData[fontNumber].Font;
|
||||
const FontRenderParams ¶ms = _fontData[fontNumber].Params;
|
||||
int max_height = 0;
|
||||
|
||||
for (int code = ugetxc(&text); code; code = ugetxc(&text)) {
|
||||
const uint16_t height = font->GetChar(code).Height;
|
||||
max_height = std::max(max_height, static_cast<int>(height));
|
||||
}
|
||||
return max_height * params.SizeMultiplier;
|
||||
}
|
||||
|
||||
static int RenderChar(Bitmap *ds, const int at_x, const int at_y, Rect clip,
|
||||
const WFNChar &wfn_char, const int scale, const color_t text_color);
|
||||
|
||||
void WFNFontRenderer::RenderText(const char *text, int fontNumber, BITMAP *destination, int x, int y, int colour) {
|
||||
int oldeip = get_our_eip();
|
||||
set_our_eip(415);
|
||||
|
||||
const WFNFont *font = _fontData[fontNumber].Font;
|
||||
const FontRenderParams ¶ms = _fontData[fontNumber].Params;
|
||||
Bitmap ds(destination, true);
|
||||
|
||||
// NOTE: allegro's putpixel ignores clipping (optimization),
|
||||
// so we'll have to accommodate for that ourselves
|
||||
Rect clip = ds.GetClip();
|
||||
for (int code = ugetxc(&text); code; code = ugetxc(&text))
|
||||
x += RenderChar(&ds, x, y, clip, font->GetChar(code), params.SizeMultiplier, colour);
|
||||
|
||||
set_our_eip(oldeip);
|
||||
}
|
||||
|
||||
static int RenderChar(Bitmap *ds, const int at_x, const int at_y, Rect clip,
|
||||
const WFNChar &wfn_char, const int scale, const color_t text_color) {
|
||||
const int width = wfn_char.Width;
|
||||
const int height = wfn_char.Height;
|
||||
const unsigned char *actdata = wfn_char.Data;
|
||||
const int bytewid = wfn_char.GetRowByteCount();
|
||||
|
||||
int sx = std::max(at_x, clip.Left), ex = clip.Right + 1;
|
||||
int sy = std::max(at_y, clip.Top), ey = clip.Bottom + 1;
|
||||
int sw = std::max(0, clip.Left - at_x);
|
||||
int sh = std::max(0, clip.Top - at_y);
|
||||
for (int h = sh, y = sy; h < height && y < ey; ++h, y += scale) {
|
||||
for (int w = sw, x = sx; w < width && x < ex; ++w, x += scale) {
|
||||
if (((actdata[h * bytewid + (w / 8)] & (0x80 >> (w % 8))) != 0)) {
|
||||
if (scale > 1) {
|
||||
ds->FillRect(RectWH(x, y, scale, scale), text_color);
|
||||
} else {
|
||||
ds->PutPixel(x, y, text_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return width * scale;
|
||||
}
|
||||
|
||||
bool WFNFontRenderer::LoadFromDisk(int fontNumber, int fontSize) {
|
||||
return LoadFromDiskEx(fontNumber, fontSize, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool WFNFontRenderer::IsBitmapFont() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WFNFontRenderer::LoadFromDiskEx(int fontNumber, int /*fontSize*/, String *src_filename,
|
||||
const FontRenderParams *params, FontMetrics *metrics) {
|
||||
String file_name;
|
||||
Stream *ffi = nullptr;
|
||||
|
||||
file_name.Format("agsfnt%d.wfn", fontNumber);
|
||||
ffi = _GP(AssetMgr)->OpenAsset(file_name);
|
||||
if (ffi == nullptr) {
|
||||
// actual font not found, try font 0 instead
|
||||
// FIXME: this should not be done here in this font renderer implementation,
|
||||
// but somewhere outside, when whoever calls this method
|
||||
file_name = "agsfnt0.wfn";
|
||||
ffi = _GP(AssetMgr)->OpenAsset(file_name);
|
||||
if (ffi == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
WFNFont *font = new WFNFont();
|
||||
WFNError err = font->ReadFromFile(ffi);
|
||||
delete ffi;
|
||||
if (err == kWFNErr_HasBadCharacters)
|
||||
Debug::Printf(kDbgMsg_Warn, "WARNING: font '%s' has mistakes in data format, some characters may be displayed incorrectly", file_name.GetCStr());
|
||||
else if (err != kWFNErr_NoError) {
|
||||
delete font;
|
||||
return false;
|
||||
}
|
||||
_fontData[fontNumber].Font = font;
|
||||
_fontData[fontNumber].Params = params ? *params : FontRenderParams();
|
||||
if (src_filename)
|
||||
*src_filename = file_name;
|
||||
if (metrics)
|
||||
*metrics = FontMetrics();
|
||||
return true;
|
||||
}
|
||||
|
||||
void WFNFontRenderer::FreeMemory(int fontNumber) {
|
||||
delete _fontData[fontNumber].Font;
|
||||
_fontData.erase(fontNumber);
|
||||
}
|
||||
|
||||
bool WFNFontRenderer::SupportsExtendedCharacters(int fontNumber) {
|
||||
return _fontData[fontNumber].Font->GetCharCount() > 128;
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
71
engines/ags/shared/font/wfn_font_renderer.h
Normal file
71
engines/ags/shared/font/wfn_font_renderer.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AGS_SHARED_FONT_WFN_FONT_RENDERER_H
|
||||
#define AGS_SHARED_FONT_WFN_FONT_RENDERER_H
|
||||
|
||||
#include "common/std/map.h"
|
||||
#include "ags/lib/std.h"
|
||||
#include "ags/shared/font/ags_font_renderer.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
class WFNFont;
|
||||
|
||||
class WFNFontRenderer : public IAGSFontRendererInternal {
|
||||
public:
|
||||
// IAGSFontRenderer implementation
|
||||
virtual ~WFNFontRenderer() {}
|
||||
|
||||
bool LoadFromDisk(int fontNumber, int fontSize) override;
|
||||
void FreeMemory(int fontNumber) override;
|
||||
bool SupportsExtendedCharacters(int fontNumber) override;
|
||||
int GetTextWidth(const char *text, int fontNumber) override;
|
||||
int GetTextHeight(const char *text, int fontNumber) override;
|
||||
void RenderText(const char *text, int fontNumber, BITMAP *destination, int x, int y, int colour) override;
|
||||
void AdjustYCoordinateForFont(int *ycoord, int fontNumber) override;
|
||||
void EnsureTextValidForFont(char *text, int fontNumber) override;
|
||||
|
||||
// IAGSFontRenderer2 implementation
|
||||
int GetVersion() override { return 26; /* first compatible engine API version */ }
|
||||
const char *GetRendererName() override { return "WFNFontRenderer"; }
|
||||
const char *GetFontName(int /*fontNumber*/) override { return ""; }
|
||||
int GetFontHeight(int fontNumber) override { return 0; /* TODO? */ }
|
||||
int GetLineSpacing(int fontNumber) override { return 0; /* no specific spacing */ }
|
||||
|
||||
// IAGSFontRendererInternal implementation
|
||||
bool IsBitmapFont() override;
|
||||
bool LoadFromDiskEx(int fontNumber, int fontSize, AGS::Shared::String *src_filename,
|
||||
const FontRenderParams *params, FontMetrics *metrics) override;
|
||||
void GetFontMetrics(int fontNumber, FontMetrics *metrics) override { *metrics = FontMetrics(); }
|
||||
void AdjustFontForAntiAlias(int /*fontNumber*/, bool /*aa_mode*/) override { /* do nothing */ }
|
||||
|
||||
private:
|
||||
struct FontData {
|
||||
WFNFont *Font;
|
||||
FontRenderParams Params;
|
||||
};
|
||||
std::map<int, FontData> _fontData;
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user