diff options
-rw-r--r-- | base/version.cpp | 4 | ||||
-rwxr-xr-x | configure | 90 | ||||
-rw-r--r-- | graphics/fonts/ttf.cpp | 443 | ||||
-rw-r--r-- | graphics/fonts/ttf.h | 42 | ||||
-rw-r--r-- | graphics/module.mk | 1 |
5 files changed, 580 insertions, 0 deletions
diff --git a/base/version.cpp b/base/version.cpp index a068f2b141..7943552418 100644 --- a/base/version.cpp +++ b/base/version.cpp @@ -121,4 +121,8 @@ const char *gScummVMFeatures = "" #ifdef USE_FAAD "AAC " #endif + +#ifdef USE_FREETYPE2 + "FreeType2 " +#endif ; @@ -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="" @@ -384,6 +387,40 @@ find_sdlconfig() { } # +# 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 # get_system_exe_extension() { @@ -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" @@ -3413,6 +3459,50 @@ 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) # echocheck "OpenGL" diff --git a/graphics/fonts/ttf.cpp b/graphics/fonts/ttf.cpp new file mode 100644 index 0000000000..8a6b87c000 --- /dev/null +++ b/graphics/fonts/ttf.cpp @@ -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 + diff --git a/graphics/fonts/ttf.h b/graphics/fonts/ttf.h new file mode 100644 index 0000000000..f5349883e9 --- /dev/null +++ b/graphics/fonts/ttf.h @@ -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 + diff --git a/graphics/module.mk b/graphics/module.mk index 02c88d98ba..1e84b2425d 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -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 \ |