aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base/version.cpp4
-rwxr-xr-xconfigure90
-rw-r--r--graphics/fonts/ttf.cpp443
-rw-r--r--graphics/fonts/ttf.h42
-rw-r--r--graphics/module.mk1
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
;
diff --git a/configure b/configure
index ab8259d067..27f56da6da 100755
--- a/configure
+++ b/configure
@@ -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 \