GRAPHICS: Add a TTF font class using FreeType2.

This commit is contained in:
Johannes Schickel 2011-07-25 22:54:32 +02:00 committed by Willem Jan Palenstijn
parent 0ceeee8148
commit 843b9f9665
5 changed files with 580 additions and 0 deletions

View file

@ -121,4 +121,8 @@ const char *gScummVMFeatures = ""
#ifdef USE_FAAD
"AAC "
#endif
#ifdef USE_FREETYPE2
"FreeType2 "
#endif
;

90
configure vendored
View file

@ -145,6 +145,7 @@ _fluidsynth=auto
_opengl=auto
_opengles=auto
_readline=auto
_freetype2=auto
_taskbar=yes
_updates=no
_libunity=auto
@ -188,7 +189,9 @@ _win32path="c:/scummvm"
_aos4path="Games:ScummVM"
_staticlibpath=/sw
_sdlconfig=sdl-config
_freetypeconfig=freetype-config
_sdlpath="$PATH"
_freetypepath="$PATH"
_nasmpath="$PATH"
NASMFLAGS=""
NASM=""
@ -383,6 +386,40 @@ find_sdlconfig() {
fi
}
#
# Determine freetype-config
#
find_freetypeconfig() {
echo_n "Looking for freetype-config... "
freetypeconfigs="$_freetypeconfig"
_freetypeconfig=
IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="$SEPARATOR"
for path_dir in $_freetypepath; do
#reset separator to parse freetypeconfigs
IFS=":"
for freetypeconfig in $freetypeconfigs; do
if test -f "$path_dir/$freetypeconfig" ; then
_freetypeconfig="$path_dir/$freetypeconfig"
echo $_freetypeconfig
# Save the prefix
_freetypepath=$path_dir
if test `basename $path_dir` = bin ; then
_freetypepath=`dirname $path_dir`
fi
# break at first freetype-config found in path
break 2
fi
done
done
IFS="$ac_save_ifs"
if test -z "$_freetypeconfig"; then
echo "none found!"
fi
}
#
# Determine extension used for executables
#
@ -846,6 +883,9 @@ Optional Libraries:
--with-sdl-prefix=DIR Prefix where the sdl-config script is
installed (optional)
--with-freetype-prefix=DIR Prefix where the freetype-config script is
installed (optional)
--with-nasm-prefix=DIR Prefix where nasm executable is installed (optional)
--disable-nasm disable assembly language optimizations [autodetect]
@ -905,6 +945,8 @@ for ac_option in $@; do
--disable-fluidsynth) _fluidsynth=no ;;
--enable-readline) _readline=yes ;;
--disable-readline) _readline=no ;;
--enable-freetype2) _freetype2=yes ;;
--disable-freetype2) _freetype2=no ;;
--enable-taskbar) _taskbar=yes ;;
--disable-taskbar) _taskbar=no ;;
--enable-updates) _updates=yes ;;
@ -1042,6 +1084,10 @@ for ac_option in $@; do
arg=`echo $ac_option | cut -d '=' -f 2`
_sdlpath="$arg:$arg/bin"
;;
--with-freetype2-prefix=*)
arg=`echo $ac_option | cut -d '=' -f 2`
_freetypepath="$arg:$arg/bin"
;;
--with-nasm-prefix=*)
arg=`echo $ac_option | cut -d '=' -f 2`
_nasmpath="$arg:$arg/bin"
@ -3412,6 +3458,50 @@ define_in_config_h_if_yes "$_libunity" 'USE_TASKBAR_UNITY'
fi
echo "$_libunity"
#
# Check for FreeType2 to be present
#
if test "$_freetype2" != "no"; then
# Look for the freetype-config script
find_freetypeconfig
if test -z "$_freetypeconfig"; then
_freetype2=no
else
FREETYPE2_LIBS=`$_freetypeconfig --prefix="$_freetypepath" --libs`
FREETYPE2_CFLAGS=`$_freetypeconfig --prefix="$_freetypepath" --cflags`
if test "$_freetype2" = "auto"; then
_freetype2=no
cat > $TMPC << EOF
#include <ft2build.h>
#include FT_FREETYPE_H
int main(int argc, char *argv[]) {
FT_Library library;
FT_Error error = FT_Init_FreeType(&library);
FT_Done_FreeType(library);
}
EOF
cc_check $FREETYPE2_CFLAGS $FREETYPE2_LIBS && _freetype2=yes
fi
if test "$_freetype2" = "yes"; then
LIBS="$LIBS $FREETYPE2_LIBS"
INCLUDES="$INCLUDES $FREETYPE2_CFLAGS"
fi
fi
fi
echocheck "FreeType2"
echo "$_freetype2"
define_in_config_h_if_yes "$_freetype2" "USE_FREETYPE2"
#
# Check for OpenGL (ES)
#

443
graphics/fonts/ttf.cpp Normal file
View file

@ -0,0 +1,443 @@
/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
// Since FreeType2 includes files, which contain forbidden symbols, we need to
// allow all symbols here.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "common/scummsys.h"
#ifdef USE_FREETYPE2
#include "graphics/fonts/ttf.h"
#include "graphics/font.h"
#include "graphics/surface.h"
#include "common/singleton.h"
#include "common/stream.h"
#include "common/hashmap.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
namespace Graphics {
namespace {
inline int ftFloor26_6(FT_Pos x) {
return x / 64;
}
inline int ftCeil26_6(FT_Pos x) {
return (x + 63) / 64;
}
} // End of anonymous namespace
class TTFLibrary : public Common::Singleton<TTFLibrary> {
public:
TTFLibrary();
~TTFLibrary();
/**
* Check whether FreeType2 is initialized properly.
*/
bool isInitialized() const { return _initialized; }
bool loadFont(const uint8 *file, const uint32 size, FT_Face &face);
void closeFont(FT_Face &face);
private:
FT_Library _library;
bool _initialized;
};
#define g_ttf ::Graphics::TTFLibrary::instance()
TTFLibrary::TTFLibrary() : _library(), _initialized(false) {
if (!FT_Init_FreeType(&_library))
_initialized = true;
}
TTFLibrary::~TTFLibrary() {
if (_initialized) {
FT_Done_FreeType(_library);
_initialized = false;
}
}
bool TTFLibrary::loadFont(const uint8 *file, const uint32 size, FT_Face &face) {
assert(_initialized);
return (FT_New_Memory_Face(_library, file, size, 0, &face) == 0);
}
void TTFLibrary::closeFont(FT_Face &face) {
assert(_initialized);
FT_Done_Face(face);
}
class TTFFont : public Font {
public:
TTFFont();
virtual ~TTFFont();
bool load(Common::SeekableReadStream &stream, int size, bool monochrome);
virtual int getFontHeight() const;
virtual int getMaxCharWidth() const;
virtual int getCharWidth(byte chr) const;
virtual void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const;
private:
bool _initialized;
FT_Face _face;
uint8 *_ttfFile;
uint32 _size;
int _width, _height;
int _ascent, _descent;
struct Glyph {
Surface image;
int xOffset, yOffset;
int advance;
};
bool cacheGlyph(Glyph &glyph, byte chr);
typedef Common::HashMap<byte, Glyph> GlyphCache;
GlyphCache _glyphs;
bool _monochrome;
};
TTFFont::TTFFont()
: _initialized(false), _face(), _ttfFile(0), _size(0), _width(0), _height(0), _ascent(0),
_descent(0), _glyphs(), _monochrome(false) {
}
TTFFont::~TTFFont() {
if (_initialized) {
g_ttf.closeFont(_face);
delete[] _ttfFile;
_ttfFile = 0;
for (GlyphCache::iterator i = _glyphs.begin(), end = _glyphs.end(); i != end; ++i)
i->_value.image.free();
_initialized = false;
}
}
bool TTFFont::load(Common::SeekableReadStream &stream, int size, bool monochrome) {
if (!g_ttf.isInitialized())
return false;
_size = stream.size();
if (!_size)
return false;
_ttfFile = new uint8[_size];
assert(_ttfFile);
if (stream.read(_ttfFile, _size) != _size) {
delete[] _ttfFile;
_ttfFile = 0;
return false;
}
if (!g_ttf.loadFont(_ttfFile, _size, _face)) {
delete[] _ttfFile;
_ttfFile = 0;
return false;
}
// We only support scalable fonts.
if (!FT_IS_SCALABLE(_face)) {
delete[] _ttfFile;
_ttfFile = 0;
g_ttf.closeFont(_face);
return false;
}
if (FT_Set_Char_Size(_face, 0, size * 64, 0, 0)) {
delete[] _ttfFile;
_ttfFile = 0;
return false;
}
_monochrome = monochrome;
FT_Fixed yScale = _face->size->metrics.y_scale;
_ascent = ftCeil26_6(FT_MulFix(_face->ascender, yScale));
_descent = ftCeil26_6(FT_MulFix(_face->descender, yScale));
_width = ftCeil26_6(FT_MulFix(_face->max_advance_width, _face->size->metrics.x_scale));
_height = _ascent - _descent + 1;
// Load all ISO-8859-1 characters.
for (uint i = 0; i < 256; ++i)
cacheGlyph(_glyphs[i], i);
_initialized = (_glyphs.size() != 0);
return _initialized;
}
int TTFFont::getFontHeight() const {
return _height;
}
int TTFFont::getMaxCharWidth() const {
return _width;
}
int TTFFont::getCharWidth(byte chr) const {
GlyphCache::const_iterator glyphEntry = _glyphs.find(chr);
if (glyphEntry == _glyphs.end())
return 0;
else
return glyphEntry->_value.advance;
}
namespace {
template<typename ColorType>
void renderGlyph(uint8 *dstPos, const int dstPitch, const uint8 *srcPos, const int srcPitch, const int w, const int h, ColorType color, const PixelFormat &dstFormat) {
uint8 sR, sG, sB;
dstFormat.colorToRGB(color, sR, sG, sB);
for (int y = 0; y < h; ++y) {
ColorType *rDst = (ColorType *)dstPos;
const uint8 *src = srcPos;
for (int x = 0; x < w; ++x) {
if (*src == 255) {
*rDst = color;
} else if (*src) {
const uint8 a = *src;
uint8 dR, dG, dB;
dstFormat.colorToRGB(*rDst, dR, dG, dB);
dR = ((255 - a) * dR + a * sR) / 255;
dG = ((255 - a) * dG + a * sG) / 255;
dB = ((255 - a) * dB + a * sB) / 255;
*rDst = dstFormat.RGBToColor(dR, dG, dB);
}
++rDst;
++src;
}
dstPos += dstPitch;
srcPos += srcPitch;
}
}
} // End of anonymous namespace
void TTFFont::drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const {
GlyphCache::const_iterator glyphEntry = _glyphs.find(chr);
if (glyphEntry == _glyphs.end())
return;
const Glyph &glyph = glyphEntry->_value;
x += glyph.xOffset;
y += glyph.yOffset;
if (x > dst->w)
return;
if (y > dst->h)
return;
int w = glyph.image.w;
int h = glyph.image.h;
const uint8 *srcPos = (const uint8 *)glyph.image.getBasePtr(0, 0);
uint8 *dstPos = (uint8 *)dst->getBasePtr(x, y);
// Make sure we are not drawing outside the screen bounds
if (x < 0) {
srcPos -= x;
w += x;
x = 0;
}
if (x + w > dst->w)
w = dst->w - x;
if (w <= 0)
return;
if (y < 0) {
srcPos += y * glyph.image.pitch;
h += y;
y = 0;
}
if (y + h > dst->h)
h = dst->h - y;
if (h <= 0)
return;
if (dst->format.bytesPerPixel == 1) {
for (int cy = 0; cy < h; ++cy) {
uint8 *rDst = dstPos;
const uint8 *src = srcPos;
for (int cx = 0; cx < w; ++cx) {
// We assume a 1Bpp mode is a color indexed mode, thus we can
// not take advantage of anti-aliasing here.
if (*src >= 0x80)
*rDst = color;
++rDst;
++src;
}
dstPos += dst->pitch;
srcPos += glyph.image.pitch;
}
} else if (dst->format.bytesPerPixel == 2) {
renderGlyph<uint16>(dstPos, dst->pitch, srcPos, glyph.image.pitch, w, h, color, dst->format);
} else if (dst->format.bytesPerPixel == 4) {
renderGlyph<uint32>(dstPos, dst->pitch, srcPos, glyph.image.pitch, w, h, color, dst->format);
}
}
bool TTFFont::cacheGlyph(Glyph &glyph, byte chr) {
FT_UInt slot = FT_Get_Char_Index(_face, chr);
if (!slot)
return false;
// We use the light target and render mode to improve the looks of the
// glyphs. It is most noticable in FreeSansBold.ttf, where otherwise the
// 't' glyph looks like it is cut off on the right side.
if (FT_Load_Glyph(_face, slot, (_monochrome ? FT_LOAD_MONOCHROME : FT_LOAD_TARGET_LIGHT)))
return false;
if (FT_Render_Glyph(_face->glyph, (_monochrome ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_LIGHT)))
return false;
if (_face->glyph->format != FT_GLYPH_FORMAT_BITMAP)
return false;
FT_Glyph_Metrics &metrics = _face->glyph->metrics;
glyph.xOffset = ftFloor26_6(metrics.horiBearingX);
int xMax = glyph.xOffset + ftCeil26_6(metrics.width);
glyph.yOffset = _ascent - ftFloor26_6(metrics.horiBearingY);
glyph.advance = ftCeil26_6(metrics.horiAdvance);
// In case we got a negative xMin we adjust that, this might make some
// characters make a bit odd, but it's the only way we can assure no
// invalid memory writes with the current font API
if (glyph.xOffset < 0) {
xMax -= glyph.xOffset;
glyph.xOffset = 0;
if (xMax > glyph.advance)
glyph.advance = xMax;
}
const FT_Bitmap &bitmap = _face->glyph->bitmap;
glyph.image.create(bitmap.width, bitmap.rows, PixelFormat::createFormatCLUT8());
const uint8 *src = bitmap.buffer;
int srcPitch = bitmap.pitch;
if (srcPitch < 0) {
src += (bitmap.rows - 1) * srcPitch;
srcPitch = -srcPitch;
}
uint8 *dst = (uint8 *)glyph.image.getBasePtr(0, 0);
memset(dst, 0, glyph.image.h * glyph.image.pitch);
switch (bitmap.pixel_mode) {
case FT_PIXEL_MODE_MONO:
for (int y = 0; y < bitmap.rows; ++y) {
const uint8 *curSrc = src;
uint8 mask = 0;
for (int x = 0; x < bitmap.width; ++x) {
if ((x % 8) == 0)
mask = *curSrc++;
if (mask & 0x80)
*dst = 255;
mask <<= 1;
++dst;
}
src += srcPitch;
}
break;
case FT_PIXEL_MODE_GRAY:
for (int y = 0; y < bitmap.rows; ++y) {
memcpy(dst, src, bitmap.width);
dst += glyph.image.pitch;
src += srcPitch;
}
break;
default:
warning("TTFFont::cacheGlyph: Unsupported pixel mode %d", bitmap.pixel_mode);
return false;
}
return true;
}
Font *loadTTFFont(Common::SeekableReadStream &stream, int size, bool monochrome) {
TTFFont *font = new TTFFont();
if (!font->load(stream, size, monochrome)) {
delete font;
return 0;
}
return font;
}
} // End of namespace Graphics
namespace Common {
DECLARE_SINGLETON(Graphics::TTFLibrary);
} // End of namespace Common
#endif

42
graphics/fonts/ttf.h Normal file
View file

@ -0,0 +1,42 @@
/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef GRAPHICS_FONTS_TTF_H
#define GRAPHICS_FONTS_TTF_H
#include "common/scummsys.h"
#ifdef USE_FREETYPE2
#include "common/stream.h"
namespace Graphics {
class Font;
Font *loadTTFFont(Common::SeekableReadStream &stream, int size, bool monochrome = false);
} // End of namespace Graphics
#endif
#endif

View file

@ -9,6 +9,7 @@ MODULE_OBJS := \
fonts/consolefont.o \
fonts/newfont_big.o \
fonts/newfont.o \
fonts/ttf.o \
fonts/winfont.o \
iff.o \
imagedec.o \