From 8edb90a719d09c01353105f7cceff237421b55e9 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 28 Sep 2014 16:00:40 -0300 Subject: [PATCH] Added support for loading BMFont bitmap font files with love.graphics.newFont (see issue #190.) Some esoteric BMFont features are not supported. Moved the Font module Lua wrapper code out of the freetype implementation folder. --HG-- branch : minor --- CMakeLists.txt | 6 +- .../love-framework.xcodeproj/project.pbxproj | 14 +- src/modules/filesystem/physfs/Filesystem.cpp | 2 +- src/modules/filesystem/physfs/Filesystem.h | 2 +- src/modules/font/BMFontRasterizer.cpp | 317 ++++++++++++++++++ src/modules/font/BMFontRasterizer.h | 85 +++++ src/modules/font/Font.h | 20 +- src/modules/font/ImageRasterizer.cpp | 3 +- src/modules/font/freetype/Font.cpp | 36 +- src/modules/font/freetype/Font.h | 21 +- .../font/freetype/TrueTypeRasterizer.cpp | 11 +- .../font/freetype/TrueTypeRasterizer.h | 12 +- src/modules/font/freetype/wrap_Font.cpp | 128 ------- src/modules/font/wrap_Font.cpp | 209 ++++++++++++ src/modules/font/{freetype => }/wrap_Font.h | 12 +- src/modules/graphics/opengl/wrap_Graphics.cpp | 27 +- src/scripts/graphics.lua | 4 +- src/scripts/graphics.lua.h | 4 +- 18 files changed, 714 insertions(+), 199 deletions(-) create mode 100644 src/modules/font/BMFontRasterizer.cpp create mode 100644 src/modules/font/BMFontRasterizer.h delete mode 100644 src/modules/font/freetype/wrap_Font.cpp create mode 100644 src/modules/font/wrap_Font.cpp rename src/modules/font/{freetype => }/wrap_Font.h (84%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 21fa939bd..7485832d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -218,6 +218,8 @@ source_group("modules\\filesystem\\physfs" FILES ${LOVE_SRC_MODULE_FILESYSTEM_PH # set(LOVE_SRC_MODULE_FONT_ROOT + src/modules/font/BMFontRasterizer.cpp + src/modules/font/BMFontRasterizer.h src/modules/font/Font.h src/modules/font/GlyphData.cpp src/modules/font/GlyphData.h @@ -225,6 +227,8 @@ set(LOVE_SRC_MODULE_FONT_ROOT src/modules/font/ImageRasterizer.h src/modules/font/Rasterizer.cpp src/modules/font/Rasterizer.h + src/modules/font/wrap_Font.cpp + src/modules/font/wrap_Font.h src/modules/font/wrap_GlyphData.cpp src/modules/font/wrap_GlyphData.h src/modules/font/wrap_Rasterizer.cpp @@ -236,8 +240,6 @@ set(LOVE_SRC_MODULE_FONT_FREETYPE src/modules/font/freetype/Font.h src/modules/font/freetype/TrueTypeRasterizer.cpp src/modules/font/freetype/TrueTypeRasterizer.h - src/modules/font/freetype/wrap_Font.cpp - src/modules/font/freetype/wrap_Font.h ) set(LOVE_SRC_MODULE_FONT diff --git a/platform/macosx/love-framework.xcodeproj/project.pbxproj b/platform/macosx/love-framework.xcodeproj/project.pbxproj index fec890329..d40cffc25 100644 --- a/platform/macosx/love-framework.xcodeproj/project.pbxproj +++ b/platform/macosx/love-framework.xcodeproj/project.pbxproj @@ -277,6 +277,8 @@ FA7AA59317F6AC1F00704BE2 /* wrap_Mesh.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7AA59117F6AC1F00704BE2 /* wrap_Mesh.h */; }; FA7C937A16DCC6C2006F2BEE /* wrap_Math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA7C937516DCC6C2006F2BEE /* wrap_Math.cpp */; }; FA7C937B16DCC6C2006F2BEE /* wrap_Math.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7C937616DCC6C2006F2BEE /* wrap_Math.h */; }; + FA9556CB19D3741A00F29A4C /* BMFontRasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA9556C919D3741A00F29A4C /* BMFontRasterizer.cpp */; }; + FA9556CC19D3741A00F29A4C /* BMFontRasterizer.h in Headers */ = {isa = PBXBuildFile; fileRef = FA9556CA19D3741A00F29A4C /* BMFontRasterizer.h */; }; FA9B492A1875EFB900201DA9 /* Texture.h in Headers */ = {isa = PBXBuildFile; fileRef = FA9B49281875EFB900201DA9 /* Texture.h */; }; FA9B4A0816E1578300074F42 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA9B4A0716E1578300074F42 /* SDL2.framework */; }; FA9FC0B0173D6E3E005027FF /* wrap_Window.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA9FC0AE173D6E3E005027FF /* wrap_Window.cpp */; }; @@ -846,6 +848,8 @@ FA7AA59117F6AC1F00704BE2 /* wrap_Mesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Mesh.h; sourceTree = ""; }; FA7C937516DCC6C2006F2BEE /* wrap_Math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Math.cpp; sourceTree = ""; }; FA7C937616DCC6C2006F2BEE /* wrap_Math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Math.h; sourceTree = ""; }; + FA9556C919D3741A00F29A4C /* BMFontRasterizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BMFontRasterizer.cpp; sourceTree = ""; }; + FA9556CA19D3741A00F29A4C /* BMFontRasterizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BMFontRasterizer.h; sourceTree = ""; }; FA9B49281875EFB900201DA9 /* Texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Texture.h; sourceTree = ""; }; FA9B4A0716E1578300074F42 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = /Library/Frameworks/SDL2.framework; sourceTree = ""; }; FA9FC0AE173D6E3E005027FF /* wrap_Window.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Window.cpp; sourceTree = ""; }; @@ -1038,8 +1042,6 @@ 615E0F450F2B7BA25EE679A6 /* Font.h */, 0C30420F7FD3038C721223A5 /* TrueTypeRasterizer.cpp */, 0B727D416262392743091BC3 /* TrueTypeRasterizer.h */, - 232D67C67BEE54B776420682 /* wrap_Font.cpp */, - 202650DE4F267CD3082A1B30 /* wrap_Font.h */, ); path = freetype; sourceTree = ""; @@ -1165,6 +1167,8 @@ 196B6B2E1BC81F47771E6467 /* font */ = { isa = PBXGroup; children = ( + FA9556C919D3741A00F29A4C /* BMFontRasterizer.cpp */, + FA9556CA19D3741A00F29A4C /* BMFontRasterizer.h */, 2E9B5D9926034F9172215D5E /* Font.h */, 12BB56F127170B3709670896 /* freetype */, 74003CB27FA762A021183AD5 /* GlyphData.cpp */, @@ -1173,6 +1177,8 @@ 36465ABA28FB06F4333C3F07 /* ImageRasterizer.h */, 1B1C4E4D288A1D2F29E57B1B /* Rasterizer.cpp */, 08D24B70441A2496160C0849 /* Rasterizer.h */, + 232D67C67BEE54B776420682 /* wrap_Font.cpp */, + 202650DE4F267CD3082A1B30 /* wrap_Font.h */, 1B4E22F1388E2B2E76E3377B /* wrap_GlyphData.cpp */, 3547706F2E7D43212CB40D04 /* wrap_GlyphData.h */, 11745DE315E859F71E881D76 /* wrap_Rasterizer.cpp */, @@ -2071,6 +2077,7 @@ FA0A4BF5182E1AD600E1E4D2 /* MotorJoint.h in Headers */, FA5FDC841788D548002F0ED2 /* unix.h in Headers */, FA0A4BF1182E0C2800E1E4D2 /* b2MotorJoint.h in Headers */, + FA9556CC19D3741A00F29A4C /* BMFontRasterizer.h in Headers */, FA5FDC851788D548002F0ED2 /* utility.h in Headers */, FAF6704D18184FF800DBDEEA /* wuff.h in Headers */, FA0F0D5C19A2CFEB0010A75B /* gladfuncs.hpp in Headers */, @@ -2113,7 +2120,7 @@ 08FB7793FE84155DC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0510; + LastUpgradeCheck = 0600; }; buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "love-framework" */; compatibilityVersion = "Xcode 3.2"; @@ -2360,6 +2367,7 @@ FAF272A616E3D44400CC193A /* LuaThread.cpp in Sources */, FA0A4BF0182E0C2800E1E4D2 /* b2MotorJoint.cpp in Sources */, FAF272A816E3D44400CC193A /* ThreadModule.cpp in Sources */, + FA9556CB19D3741A00F29A4C /* BMFontRasterizer.cpp in Sources */, FA01BE1E1878E35B00640047 /* wrap_Texture.cpp in Sources */, FAF272AA16E3D44400CC193A /* wrap_Channel.cpp in Sources */, FAF272AC16E3D44400CC193A /* wrap_LuaThread.cpp in Sources */, diff --git a/src/modules/filesystem/physfs/Filesystem.cpp b/src/modules/filesystem/physfs/Filesystem.cpp index 6e9a6e5ab..cf395aec7 100644 --- a/src/modules/filesystem/physfs/Filesystem.cpp +++ b/src/modules/filesystem/physfs/Filesystem.cpp @@ -474,7 +474,7 @@ bool Filesystem::remove(const char *file) return true; } -Data *Filesystem::read(const char *filename, int64 size) const +FileData *Filesystem::read(const char *filename, int64 size) const { File file(filename); diff --git a/src/modules/filesystem/physfs/Filesystem.h b/src/modules/filesystem/physfs/Filesystem.h index 3a13a1f57..647f15fe2 100644 --- a/src/modules/filesystem/physfs/Filesystem.h +++ b/src/modules/filesystem/physfs/Filesystem.h @@ -202,7 +202,7 @@ public: * @param filename The name of the file to read from. * @param size The size in bytes of the data to read. **/ - Data *read(const char *filename, int64 size = File::ALL) const; + FileData *read(const char *filename, int64 size = File::ALL) const; /** * Write data to a file. diff --git a/src/modules/font/BMFontRasterizer.cpp b/src/modules/font/BMFontRasterizer.cpp new file mode 100644 index 000000000..a7234f8c8 --- /dev/null +++ b/src/modules/font/BMFontRasterizer.cpp @@ -0,0 +1,317 @@ +/** + * Copyright (c) 2006-2014 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +// LOVE +#include "BMFontRasterizer.h" +#include "filesystem/physfs/Filesystem.h" +#include "image/Image.h" + +// C++ +#include +#include + +// C +#include +#include + +namespace love +{ +namespace font +{ + +namespace +{ + +/** + * Helper class for parsing lines in BMFont definition files. + * NOTE: Does not properly handle multi-value attributes (e.g. 'padding' or + * 'spacing'.) + **/ +class BMFontLine +{ +public: + + BMFontLine(const std::string &line); + + const std::string &getTag() const { return tag; } + + int getAttributeInt(const char *name) const; + std::string getAttributeString(const char *name) const; + +private: + + std::string tag; + std::map attributes; + +}; + +// This is not entirely robust... +BMFontLine::BMFontLine(const std::string &line) +{ + // The tag name should always be at the start of the line. + tag = line.substr(0, line.find(' ')); + + size_t startpos = 0; + + while (startpos < line.length()) + { + // Find the next '=', which indicates a key-value pair. + size_t fpos = line.find('=', startpos); + if (fpos == std::string::npos || fpos + 1 >= line.length()) + break; + + // The key should be between a space character and the '='. + size_t keystart = line.rfind(' ', fpos); + if (keystart == std::string::npos) + break; + + keystart++; + + std::string key = line.substr(keystart, fpos - keystart); + + size_t valstart = fpos + 1; + size_t valend = valstart + 1; + + if (line[valstart] == '"') + { + // Values can be surrounded by quotes (a literal string.) + valstart++; + valend = line.find('"', valstart) - 1; + } + else + { + // Otherwise look for the next space character after the '='. + valend = line.find(' ', valstart + 1) - 1; + } + + valend = std::min(valend, line.length() - 1); + + attributes[key] = line.substr(valstart, valend - valstart + 1); + + startpos = valend + 1; + } +} + +int BMFontLine::getAttributeInt(const char *name) const +{ + auto it = attributes.find(name); + if (it == attributes.end()) + return 0; + + return (int) strtol(it->second.c_str(), nullptr, 10); +} + +std::string BMFontLine::getAttributeString(const char *name) const +{ + auto it = attributes.find(name); + if (it == attributes.end()) + return ""; + + return it->second; +} + +} // anonymous namespace + + +BMFontRasterizer::BMFontRasterizer(love::filesystem::FileData *fontdef, const std::vector &imagelist) + : fontSize(0) + , unicode(false) + , lineHeight(0) +{ + const std::string &filename = fontdef->getFilename(); + + size_t separatorpos = filename.rfind('/'); + if (separatorpos != std::string::npos) + fontFolder = filename.substr(0, separatorpos); + + // The parseConfig function will try to load any missing page images. + for (size_t i = 0; i < imagelist.size(); i++) + images[i] = imagelist[i]; + + std::string configtext((const char *) fontdef->getData(), fontdef->getSize()); + + parseConfig(configtext); +} + +BMFontRasterizer::~BMFontRasterizer() +{ +} + +void BMFontRasterizer::parseConfig(const std::string &configtext) +{ + std::stringstream ss(configtext); + std::string line; + + while (std::getline(ss, line)) + { + BMFontLine cline(line); + + const std::string &tag = cline.getTag(); + + if (tag == "info") + { + fontSize = cline.getAttributeInt("size"); + unicode = cline.getAttributeInt("unicode") > 0; + } + else if (tag == "common") + { + lineHeight = cline.getAttributeInt("lineHeight"); + metrics.ascent = cline.getAttributeInt("base"); + metrics.height = lineHeight; + } + else if (tag == "page") + { + size_t pageindex = (size_t) cline.getAttributeInt("id"); + std::string filename = cline.getAttributeString("file"); + + // The file name is relative to the font file's folder. + if (!fontFolder.empty()) + filename = fontFolder + "/" + filename; + + // Load the page file from disk into an ImageData, if necessary. + if (images[pageindex].get() == nullptr) + { + using namespace love::filesystem::physfs; + + Filesystem *filesystem = Module::getInstance(Module::M_FILESYSTEM); + image::Image *imagemodule = Module::getInstance(Module::M_IMAGE); + + if (!filesystem) + throw love::Exception("Filesystem module not loaded!"); + if (!imagemodule) + throw love::Exception("Image module not loaded!"); + + // Release these variables right away since StrongRef retains. + StrongRef data = filesystem->read(filename.c_str()); + data->release(); + + images[pageindex].set(imagemodule->newImageData(data.get())); + images[pageindex]->release(); + } + } + else if (tag == "char") + { + BMFontCharacter c = {}; + + uint32 id = (uint32) cline.getAttributeInt("id"); + + c.x = cline.getAttributeInt("x"); + c.y = cline.getAttributeInt("y"); + c.page = cline.getAttributeInt("page"); + + c.metrics.width = cline.getAttributeInt("width"); + c.metrics.height = cline.getAttributeInt("height"); + c.metrics.bearingX = cline.getAttributeInt("xoffset"); + c.metrics.bearingY = -cline.getAttributeInt("yoffset"); + c.metrics.advance = cline.getAttributeInt("xadvance"); + + characters[id] = c; + } + else if (tag == "kerning") + { + // TODO + } + } + + if (lineHeight == 0) + throw love::Exception("Invalid BMFont file (missing line height attribute?)"); + + if (characters.size() == 0) + throw love::Exception("Invalid BMFont file (no character definitions?)"); + + // Verify the glyph character attributes. + for (const auto &cpair : characters) + { + const BMFontCharacter &c = cpair.second; + + if (!unicode && cpair.first > 127) + throw love::Exception("Invalid BMFont character id (only unicode and ASCII are supported)"); + + if (c.page < 0 || images[c.page].get() == nullptr) + throw love::Exception("Invalid BMFont character page id: %d", c.page); + + const image::ImageData *id = images[c.page].get(); + + if (!id->inside(c.x, c.y) || !id->inside(c.x + c.metrics.width, c.y + c.metrics.height)) + throw love::Exception("Invalid BMFont character coordinates."); + } +} + +int BMFontRasterizer::getLineHeight() const +{ + return lineHeight; +} + +GlyphData *BMFontRasterizer::getGlyphData(uint32 glyph) const +{ + auto it = characters.find(glyph); + + // Return an empty GlyphData if we don't have the glyph character. + if (it == characters.end()) + return new GlyphData(glyph, GlyphMetrics(), GlyphData::FORMAT_RGBA); + + const BMFontCharacter &c = it->second; + GlyphData *g = new GlyphData(glyph, c.metrics, GlyphData::FORMAT_RGBA); + + const auto &imagepair = images.find(c.page); + + if (imagepair == images.end()) + { + g->release(); + return new GlyphData(glyph, GlyphMetrics(), GlyphData::FORMAT_RGBA); + } + + image::ImageData *imagedata = imagepair->second.get(); + image::pixel *pixels = (image::pixel *) g->getData(); + const image::pixel *ipixels = (const image::pixel *) imagedata->getData(); + + love::thread::Lock lock(imagedata->getMutex()); + + // Copy the subsection of the texture from the ImageData to the GlyphData. + for (int y = 0; y < c.metrics.height; y++) + { + size_t idindex = (c.y + y) * imagedata->getWidth() + c.x; + memcpy(&pixels[y * c.metrics.width], &ipixels[idindex], sizeof(image::pixel) * c.metrics.width); + } + + return g; +} + +int BMFontRasterizer::getGlyphCount() const +{ + return (int) characters.size(); +} + +bool BMFontRasterizer::hasGlyph(uint32 glyph) const +{ + return characters.find(glyph) != characters.end(); +} + +bool BMFontRasterizer::accepts(love::filesystem::FileData *fontdef) +{ + const char *data = (const char *) fontdef->getData(); + + // Check if the "info" tag is at the start of the file. This is a truly + // crappy test. Is the tag even guaranteed to be at the start? + return fontdef->getSize() > 4 && memcmp(data, "info", 4) == 0; +} + +} // font +} // love diff --git a/src/modules/font/BMFontRasterizer.h b/src/modules/font/BMFontRasterizer.h new file mode 100644 index 000000000..ec30e9992 --- /dev/null +++ b/src/modules/font/BMFontRasterizer.h @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2006-2014 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_FONT_BMFONT_RASTERIZER_H +#define LOVE_FONT_BMFONT_RASTERIZER_H + +// LOVE +#include "Rasterizer.h" +#include "image/ImageData.h" + +// C++ +#include +#include + +namespace love +{ +namespace font +{ + +/** + * Rasterizer for BMFont bitmap fonts. + **/ +class BMFontRasterizer : public Rasterizer +{ +public: + + BMFontRasterizer(love::filesystem::FileData *fontdef, const std::vector &imagelist); + virtual ~BMFontRasterizer(); + + // Implements Rasterizer. + int getLineHeight() const override; + GlyphData *getGlyphData(uint32 glyph) const override; + int getGlyphCount() const override; + bool hasGlyph(uint32 glyph) const override; + + static bool accepts(love::filesystem::FileData *fontdef); + +private: + + struct BMFontCharacter + { + int x; + int y; + int page; + GlyphMetrics metrics; + }; + + void parseConfig(const std::string &config); + + std::string fontFolder; + + // Image pages, indexed by their page id. + std::map> images; + + // Glyph characters, indexed by their glyph id. + std::map characters; + + int fontSize; + bool unicode; + + int lineHeight; + +}; // BMFontRasterizer + +} // font +} // love + +#endif // LOVE_FONT_BMFONT_RASTERIZER_H diff --git a/src/modules/font/Font.h b/src/modules/font/Font.h index fbcf4298f..ee4a2a10a 100644 --- a/src/modules/font/Font.h +++ b/src/modules/font/Font.h @@ -24,11 +24,13 @@ // LOVE #include "Rasterizer.h" #include "image/ImageData.h" +#include "filesystem/FileData.h" #include "common/Module.h" #include "common/int.h" -// STD +// C++ #include +#include namespace love { @@ -42,16 +44,20 @@ public: virtual ~Font() {} - // Implements Module. - virtual ModuleType getModuleType() const { return M_FONT; } + virtual Rasterizer *newRasterizer(love::filesystem::FileData *data) = 0; + + virtual Rasterizer *newTrueTypeRasterizer(love::filesystem::FileData *data, int size) = 0; + + virtual Rasterizer *newBMFontRasterizer(love::filesystem::FileData *fontdef, const std::vector &images) = 0; + + virtual Rasterizer *newImageRasterizer(love::image::ImageData *data, const std::string &glyphs) = 0; + virtual Rasterizer *newImageRasterizer(love::image::ImageData *data, uint32 *glyphs, int length) = 0; - virtual Rasterizer *newRasterizer(Data *data, int size) = 0; - virtual Rasterizer *newRasterizer(love::image::ImageData *data, const std::string &glyphs) = 0; - virtual Rasterizer *newRasterizer(love::image::ImageData *data, uint32 *glyphs, int length) = 0; virtual GlyphData *newGlyphData(Rasterizer *r, const std::string &glyph) = 0; virtual GlyphData *newGlyphData(Rasterizer *r, uint32 glyph) = 0; - // Implement Module + // Implement Module. + virtual ModuleType getModuleType() const { return M_FONT; } virtual const char *getName() const = 0; }; // Font diff --git a/src/modules/font/ImageRasterizer.cpp b/src/modules/font/ImageRasterizer.cpp index f7d1fada5..c4ba34bfe 100644 --- a/src/modules/font/ImageRasterizer.cpp +++ b/src/modules/font/ImageRasterizer.cpp @@ -53,8 +53,7 @@ int ImageRasterizer::getLineHeight() const GlyphData *ImageRasterizer::getGlyphData(uint32 glyph) const { - GlyphMetrics gm; - memset(&gm, 0, sizeof(GlyphMetrics)); + GlyphMetrics gm = {}; // Set relevant glyph metrics if the glyph is in this ImageFont std::map::const_iterator it = imageGlyphs.find(glyph); diff --git a/src/modules/font/freetype/Font.cpp b/src/modules/font/freetype/Font.cpp index ece8e8ddb..147192f8a 100644 --- a/src/modules/font/freetype/Font.cpp +++ b/src/modules/font/freetype/Font.cpp @@ -22,6 +22,7 @@ #include "TrueTypeRasterizer.h" #include "font/ImageRasterizer.h" +#include "font/BMFontRasterizer.h" #include "libraries/utf8/utf8.h" @@ -35,7 +36,7 @@ namespace freetype Font::Font() { if (FT_Init_FreeType(&library)) - throw love::Exception("TrueTypeFont Loading error: FT_Init_FreeType failed\n"); + throw love::Exception("TrueTypeFont Loading error: FT_Init_FreeType failed"); } Font::~Font() @@ -43,17 +44,30 @@ Font::~Font() FT_Done_FreeType(library); } -Rasterizer *Font::newRasterizer(Data *data, int size) +Rasterizer *Font::newRasterizer(love::filesystem::FileData *data) +{ + if (TrueTypeRasterizer::accepts(library, data)) + return newTrueTypeRasterizer(data, 12); + else if (BMFontRasterizer::accepts(data)) + return newBMFontRasterizer(data, std::vector()); + + throw love::Exception("Invalid font file: %s", data->getFilename().c_str()); +} + +Rasterizer *Font::newTrueTypeRasterizer(love::filesystem::FileData *data, int size) { return new TrueTypeRasterizer(library, data, size); } -Rasterizer *Font::newRasterizer(love::image::ImageData *data, const std::string &text) +Rasterizer *Font::newBMFontRasterizer(love::filesystem::FileData *fontdef, const std::vector &images) { - size_t strlen = text.size(); - size_t numglyphs = 0; + return new BMFontRasterizer(fontdef, images); +} - uint32 *glyphs = new uint32[strlen]; +Rasterizer *Font::newImageRasterizer(love::image::ImageData *data, const std::string &text) +{ + std::vector glyphs; + glyphs.reserve(text.size()); try { @@ -61,21 +75,17 @@ Rasterizer *Font::newRasterizer(love::image::ImageData *data, const std::string utf8::iterator end(text.end(), text.begin(), text.end()); while (i != end) - glyphs[numglyphs++] = *i++; + glyphs.push_back(*i++); } catch (utf8::exception &e) { - delete [] glyphs; throw love::Exception("Decoding error: %s", e.what()); } - Rasterizer *r = newRasterizer(data, glyphs, numglyphs); - delete [] glyphs; - - return r; + return newImageRasterizer(data, &glyphs[0], glyphs.size()); } -Rasterizer *Font::newRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs) +Rasterizer *Font::newImageRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs) { return new ImageRasterizer(data, glyphs, numglyphs); } diff --git a/src/modules/font/freetype/Font.h b/src/modules/font/freetype/Font.h index 814ee63b9..6b0a37377 100644 --- a/src/modules/font/freetype/Font.h +++ b/src/modules/font/freetype/Font.h @@ -25,11 +25,7 @@ #include "font/Font.h" // FreeType2 -#ifdef LOVE_MACOSX_USE_FRAMEWORKS -#include -#else #include -#endif #include FT_FREETYPE_H #include FT_GLYPH_H @@ -45,16 +41,18 @@ class Font : public love::font::Font public: Font(); - - /** - * Destructor. - **/ virtual ~Font(); // Implements Font - Rasterizer *newRasterizer(Data *data, int size); - Rasterizer *newRasterizer(love::image::ImageData *data, const std::string &text); - Rasterizer *newRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs); + Rasterizer *newRasterizer(love::filesystem::FileData *data); + + Rasterizer *newTrueTypeRasterizer(love::filesystem::FileData *data, int size); + + Rasterizer *newBMFontRasterizer(love::filesystem::FileData *fontdef, const std::vector &images); + + Rasterizer *newImageRasterizer(love::image::ImageData *data, const std::string &text); + Rasterizer *newImageRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs); + GlyphData *newGlyphData(Rasterizer *r, const std::string &glyph); GlyphData *newGlyphData(Rasterizer *r, uint32 glyph); @@ -65,6 +63,7 @@ private: // FreeType library FT_Library library; + }; // Font } // freetype diff --git a/src/modules/font/freetype/TrueTypeRasterizer.cpp b/src/modules/font/freetype/TrueTypeRasterizer.cpp index 0cf92f873..4241f150d 100644 --- a/src/modules/font/freetype/TrueTypeRasterizer.cpp +++ b/src/modules/font/freetype/TrueTypeRasterizer.cpp @@ -30,7 +30,7 @@ namespace font namespace freetype { -TrueTypeRasterizer::TrueTypeRasterizer(FT_Library library, Data *data, int size) +TrueTypeRasterizer::TrueTypeRasterizer(FT_Library library, love::filesystem::FileData *data, int size) : data(data) { FT_Error err = FT_Err_Ok; @@ -150,6 +150,15 @@ bool TrueTypeRasterizer::hasGlyph(uint32 glyph) const return FT_Get_Char_Index(face, glyph) != 0; } +bool TrueTypeRasterizer::accepts(FT_Library library, love::filesystem::FileData *data) +{ + const FT_Byte *fbase = (const FT_Byte *) data->getData(); + FT_Long fsize = (FT_Long) data->getSize(); + + // Pasing in -1 for the face index lets us test if the data is valid. + return FT_New_Memory_Face(library, fbase, fsize, -1, nullptr) == 0; +} + } // freetype } // font } // love diff --git a/src/modules/font/freetype/TrueTypeRasterizer.h b/src/modules/font/freetype/TrueTypeRasterizer.h index 6ddaf0461..5d980fd49 100644 --- a/src/modules/font/freetype/TrueTypeRasterizer.h +++ b/src/modules/font/freetype/TrueTypeRasterizer.h @@ -25,7 +25,7 @@ #include "filesystem/File.h" #include "font/Rasterizer.h" -// TrueType2 +// FreeType2 #include #include FT_FREETYPE_H #include FT_GLYPH_H @@ -43,7 +43,8 @@ namespace freetype class TrueTypeRasterizer : public Rasterizer { public: - TrueTypeRasterizer(FT_Library library, Data *data, int size); + + TrueTypeRasterizer(FT_Library library, love::filesystem::FileData *data, int size); virtual ~TrueTypeRasterizer(); // Implement Rasterizer @@ -52,14 +53,17 @@ public: virtual int getGlyphCount() const; virtual bool hasGlyph(uint32 glyph) const; + static bool accepts(FT_Library library, love::filesystem::FileData *data); + private: // TrueType face FT_Face face; // File data - Object::StrongRef data; -}; // FreetypeRasterizer + StrongRef data; + +}; // TrueTypeRasterizer } // freetype } // font diff --git a/src/modules/font/freetype/wrap_Font.cpp b/src/modules/font/freetype/wrap_Font.cpp deleted file mode 100644 index 534594471..000000000 --- a/src/modules/font/freetype/wrap_Font.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (c) 2006-2014 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ - -#include "wrap_Font.h" - -#include "Font.h" - -#include "font/wrap_GlyphData.h" -#include "font/wrap_Rasterizer.h" - -#include "filesystem/wrap_Filesystem.h" - -#include "TrueTypeRasterizer.h" - -namespace love -{ -namespace font -{ -namespace freetype -{ - -#define instance() (Module::getInstance(Module::M_FONT)) - -int w_newRasterizer(lua_State *L) -{ - Rasterizer *t = nullptr; - - if (luax_istype(L, 1, IMAGE_IMAGE_DATA_T)) - { - love::image::ImageData *d = luax_checktype(L, 1, "ImageData", IMAGE_IMAGE_DATA_T); - const char *g = luaL_checkstring(L, 2); - std::string glyphs(g); - luax_catchexcept(L, [&](){ t = instance()->newRasterizer(d, glyphs); }); - } - else if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_T)) - { - love::filesystem::FileData *d = love::filesystem::luax_getfiledata(L, 1); - int size = luaL_checkint(L, 2); - luax_catchexcept(L, - [&]() { t = instance()->newRasterizer(d, size); }, - [&]() { d->release(); } - ); - } - else - return luaL_argerror(L, 1, "expected ImageData, filename, or FileData"); - - luax_pushtype(L, "Rasterizer", FONT_RASTERIZER_T, t); - t->release(); - return 1; -} - -int w_newGlyphData(lua_State *L) -{ - Rasterizer *r = luax_checkrasterizer(L, 1); - GlyphData *t = nullptr; - - // newGlyphData accepts a unicode character or a codepoint number. - if (lua_type(L, 2) == LUA_TSTRING) - { - std::string glyph = luax_checkstring(L, 2); - luax_catchexcept(L, [&](){ t = instance()->newGlyphData(r, glyph); }); - } - else - { - uint32 g = (uint32) luaL_checknumber(L, 2); - t = instance()->newGlyphData(r, g); - } - - luax_pushtype(L, "GlyphData", FONT_GLYPH_DATA_T, t); - t->release(); - return 1; -} - -// List of functions to wrap. -static const luaL_Reg functions[] = -{ - { "newRasterizer", w_newRasterizer }, - { "newGlyphData", w_newGlyphData }, - { 0, 0 } -}; - -static const lua_CFunction types[] = -{ - luaopen_glyphdata, - luaopen_rasterizer, - 0 -}; - -extern "C" int luaopen_love_font(lua_State *L) -{ - Font *instance = instance(); - if (instance == nullptr) - { - luax_catchexcept(L, [&](){ instance = new Font(); }); - } - else - instance->retain(); - - WrappedModule w; - w.module = instance; - w.name = "font"; - w.flags = MODULE_T; - w.functions = functions; - w.types = types; - - return luax_register_module(L, w); -} - -} // freetype -} // font -} // love diff --git a/src/modules/font/wrap_Font.cpp b/src/modules/font/wrap_Font.cpp new file mode 100644 index 000000000..da9125f39 --- /dev/null +++ b/src/modules/font/wrap_Font.cpp @@ -0,0 +1,209 @@ +/** + * Copyright (c) 2006-2014 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "wrap_Font.h" + +#include "Font.h" +#include "freetype/Font.h" + +#include "wrap_GlyphData.h" +#include "wrap_Rasterizer.h" + +#include "filesystem/wrap_Filesystem.h" + +namespace love +{ +namespace font +{ + +#define instance() (Module::getInstance(Module::M_FONT)) + +int w_newRasterizer(lua_State *L) +{ + if (lua_isnoneornil(L, 2)) + { + // Single argument: call Font::newRasterizer. + Rasterizer *t = nullptr; + filesystem::FileData *d = filesystem::luax_getfiledata(L, 1); + + luax_catchexcept(L, + [&]() { t = instance()->newRasterizer(d); }, + [&]() { d->release(); } + ); + + luax_pushtype(L, "Rasterizer", FONT_RASTERIZER_T, t); + t->release(); + return 1; + } + else if (lua_type(L, 2) == LUA_TNUMBER) + { + // Second argument is a number: call newTrueTypeRasterizer. + return w_newTrueTypeRasterizer(L); + } + else + { + // Otherwise call newBMFontRasterizer. + return w_newBMFontRasterizer(L); + } +} + +int w_newTrueTypeRasterizer(lua_State *L) +{ + Rasterizer *t = nullptr; + + filesystem::FileData *d = filesystem::luax_getfiledata(L, 1); + int size = luaL_optint(L, 2, 12); + + luax_catchexcept(L, + [&]() { t = instance()->newTrueTypeRasterizer(d, size); }, + [&]() { d->release(); } + ); + + luax_pushtype(L, "Rasterizer", FONT_RASTERIZER_T, t); + t->release(); + return 1; +} + +static void convimagedata(lua_State *L, int idx) +{ + if (lua_isstring(L, idx) || luax_istype(L, idx, FILESYSTEM_FILE_T) || luax_istype(L, idx, FILESYSTEM_FILE_DATA_T)) + luax_convobj(L, idx, "image", "newImageData"); +} + +int w_newBMFontRasterizer(lua_State *L) +{ + Rasterizer *t = nullptr; + + filesystem::FileData *d = filesystem::luax_getfiledata(L, 1); + std::vector images; + + if (lua_istable(L, 2)) + { + for (int i = 1; i <= (int) lua_objlen(L, 2); i++) + { + lua_rawgeti(L, 2, i); + + convimagedata(L, -1); + image::ImageData *id = luax_checktype(L, -1, "ImageData", IMAGE_IMAGE_DATA_T); + images.push_back(id); + id->retain(); + + lua_pop(L, 1); + } + } + else + { + for (int i = 2; i <= lua_gettop(L); i++) + { + convimagedata(L, i); + image::ImageData *id = luax_checktype(L, i, "ImageData", IMAGE_IMAGE_DATA_T); + images.push_back(id); + id->retain(); + } + } + + luax_catchexcept(L, + [&]() { t = instance()->newBMFontRasterizer(d, images); }, + [&]() { d->release(); for (auto id : images) id->release(); } + ); + + luax_pushtype(L, "Rasterizer", FONT_RASTERIZER_T, t); + t->release(); + return 1; +} + +int w_newImageRasterizer(lua_State *L) +{ + Rasterizer *t = nullptr; + + convimagedata(L, 1); + + image::ImageData *d = luax_checktype(L, 1, "ImageData", IMAGE_IMAGE_DATA_T); + std::string glyphs = luax_checkstring(L, 2); + + luax_catchexcept(L, [&](){ t = instance()->newImageRasterizer(d, glyphs); }); + + luax_pushtype(L, "Rasterizer", FONT_RASTERIZER_T, t); + t->release(); + return 1; +} + +int w_newGlyphData(lua_State *L) +{ + Rasterizer *r = luax_checkrasterizer(L, 1); + GlyphData *t = nullptr; + + // newGlyphData accepts a unicode character or a codepoint number. + if (lua_type(L, 2) == LUA_TSTRING) + { + std::string glyph = luax_checkstring(L, 2); + luax_catchexcept(L, [&](){ t = instance()->newGlyphData(r, glyph); }); + } + else + { + uint32 g = (uint32) luaL_checknumber(L, 2); + t = instance()->newGlyphData(r, g); + } + + luax_pushtype(L, "GlyphData", FONT_GLYPH_DATA_T, t); + t->release(); + return 1; +} + +// List of functions to wrap. +static const luaL_Reg functions[] = +{ + { "newRasterizer", w_newRasterizer }, + { "newTrueTypeRasterizer", w_newTrueTypeRasterizer }, + { "newBMFontRasterizer", w_newBMFontRasterizer }, + { "newImageRasterizer", w_newImageRasterizer }, + { "newGlyphData", w_newGlyphData }, + { 0, 0 } +}; + +static const lua_CFunction types[] = +{ + luaopen_glyphdata, + luaopen_rasterizer, + 0 +}; + +extern "C" int luaopen_love_font(lua_State *L) +{ + Font *instance = instance(); + if (instance == nullptr) + { + luax_catchexcept(L, [&](){ instance = new freetype::Font(); }); + } + else + instance->retain(); + + WrappedModule w; + w.module = instance; + w.name = "font"; + w.flags = MODULE_T; + w.functions = functions; + w.types = types; + + return luax_register_module(L, w); +} + +} // font +} // love diff --git a/src/modules/font/freetype/wrap_Font.h b/src/modules/font/wrap_Font.h similarity index 84% rename from src/modules/font/freetype/wrap_Font.h rename to src/modules/font/wrap_Font.h index 6cf7b7b0b..2f8665fec 100644 --- a/src/modules/font/freetype/wrap_Font.h +++ b/src/modules/font/wrap_Font.h @@ -18,8 +18,8 @@ * 3. This notice may not be removed or altered from any source distribution. **/ -#ifndef LOVE_FONT_FREETYPE_WRAP_FONT_H -#define LOVE_FONT_FREETYPE_WRAP_FONT_H +#ifndef LOVE_FONT_WRAP_FONT_H +#define LOVE_FONT_WRAP_FONT_H // LOVE #include "common/config.h" @@ -29,15 +29,15 @@ namespace love { namespace font { -namespace freetype -{ int w_newRasterizer(lua_State *L); +int w_newTrueTypeRasterizer(lua_State *L); +int w_newBMFontRasterizer(lua_State *L); +int w_newImageRasterizer(lua_State *L); int w_newGlyphData(lua_State *L); extern "C" LOVE_EXPORT int luaopen_love_font(lua_State *L); -} // freetype } // font } // love -#endif // LOVE_FONT_FREETYPE_WRAP_FONT_H +#endif // LOVE_FONT_WRAP_FONT_H diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index cc0094846..436b561cf 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -258,23 +258,24 @@ int w_newQuad(lua_State *L) int w_newFont(lua_State *L) { + Font *font = nullptr; + // Convert to Rasterizer, if necessary. - if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_T)) + if (!luax_istype(L, 1, FONT_RASTERIZER_T)) { - int idxs[] = {1, 2}; - luax_convobj(L, idxs, 2, "font", "newRasterizer"); + std::vector idxs; + for (int i = 0; i < lua_gettop(L); i++) + idxs.push_back(i + 1); + + luax_convobj(L, &idxs[0], (int) idxs.size(), "font", "newRasterizer"); } love::font::Rasterizer *rasterizer = luax_checktype(L, 1, "Rasterizer", FONT_RASTERIZER_T); - Font *font = 0; luax_catchexcept(L, [&]() { font = instance()->newFont(rasterizer, instance()->getDefaultFilter()); } ); - if (font == 0) - return luaL_error(L, "Could not load font."); - // Push the type. luax_pushtype(L, "Font", GRAPHICS_FONT_T, font); font->release(); @@ -287,9 +288,7 @@ int w_newImageFont(lua_State *L) Texture::Filter filter = instance()->getDefaultFilter(); // Convert to ImageData if necessary. - if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_T)) - luax_convobj(L, 1, "image", "newImageData"); - else if (luax_istype(L, 1, GRAPHICS_IMAGE_T)) + if (luax_istype(L, 1, GRAPHICS_IMAGE_T)) { Image *i = luax_checktype(L, 1, "Image", GRAPHICS_IMAGE_T); filter = i->getFilter(); @@ -301,11 +300,11 @@ int w_newImageFont(lua_State *L) } // Convert to Rasterizer if necessary. - if (luax_istype(L, 1, IMAGE_IMAGE_DATA_T)) + if (!luax_istype(L, 1, FONT_RASTERIZER_T)) { luaL_checkstring(L, 2); int idxs[] = {1, 2}; - luax_convobj(L, idxs, 2, "font", "newRasterizer"); + luax_convobj(L, idxs, 2, "font", "newImageRasterizer"); } love::font::Rasterizer *rasterizer = luax_checktype(L, 1, "Rasterizer", FONT_RASTERIZER_T); @@ -313,13 +312,9 @@ int w_newImageFont(lua_State *L) // Create the font. Font *font = instance()->newFont(rasterizer, filter); - if (font == 0) - return luaL_error(L, "Could not load font."); - // Push the type. luax_pushtype(L, "Font", GRAPHICS_FONT_T, font); font->release(); - return 1; } diff --git a/src/scripts/graphics.lua b/src/scripts/graphics.lua index c8fdd4d2e..197fbe255 100644 --- a/src/scripts/graphics.lua +++ b/src/scripts/graphics.lua @@ -1248,12 +1248,12 @@ do local type = type local _newFont = love.graphics.newFont - love.graphics.newFont = function(font, size) + love.graphics.newFont = function(font, size, ...) if type(font) == "number" or not font then size = font font = vera_ttf end - return _newFont(font, size or 12) + return _newFont(font, size, ...) end love.graphics.setNewFont = function(...) diff --git a/src/scripts/graphics.lua.h b/src/scripts/graphics.lua.h index 4a8ad8e40..755a51106 100644 --- a/src/scripts/graphics.lua.h +++ b/src/scripts/graphics.lua.h @@ -6199,7 +6199,7 @@ const unsigned char graphics_lua[] = 0x6f, 0x6e, 0x74, 0x0a, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x46, 0x6f, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x6f, - 0x6e, 0x74, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, + 0x6e, 0x74, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x66, 0x6f, 0x6e, 0x74, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x20, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, @@ -6207,7 +6207,7 @@ const unsigned char graphics_lua[] = 0x09, 0x09, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x72, 0x61, 0x5f, 0x74, 0x74, 0x66, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x6e, 0x65, 0x77, 0x46, 0x6f, 0x6e, 0x74, 0x28, - 0x66, 0x6f, 0x6e, 0x74, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x31, 0x32, 0x29, 0x0a, + 0x66, 0x6f, 0x6e, 0x74, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x4e, 0x65, 0x77, 0x46, 0x6f, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,