diff --git a/binding-mri/font-binding.cpp b/binding-mri/font-binding.cpp index 2a8856a..9a6e264 100644 --- a/binding-mri/font-binding.cpp +++ b/binding-mri/font-binding.cpp @@ -27,6 +27,29 @@ #include + +static void +collectStrings(VALUE obj, std::vector &out) +{ + if (RB_TYPE_P(obj, RUBY_T_STRING)) + { + out.push_back(RSTRING_PTR(obj)); + } + else if (RB_TYPE_P(obj, RUBY_T_ARRAY)) + { + for (long i = 0; i < RARRAY_LEN(obj); ++i) + { + VALUE str = rb_ary_entry(obj, i); + + /* Non-string objects are tolerated (ignored) */ + if (!RB_TYPE_P(str, RUBY_T_STRING)) + continue; + + out.push_back(RSTRING_PTR(str)); + } + } +} + DEF_TYPE(Font); RB_METHOD(fontDoesExist) @@ -48,12 +71,30 @@ RB_METHOD(FontSetName); RB_METHOD(fontInitialize) { - VALUE name = Qnil; + VALUE namesObj = Qnil; int size = 0; - rb_get_args(argc, argv, "|oi", &name, &size RB_ARG_END); + rb_get_args(argc, argv, "|oi", &namesObj, &size RB_ARG_END); - Font *f = new Font(0, size); + Font *f; + + if (NIL_P(namesObj)) + { + namesObj = rb_iv_get(rb_obj_class(self), "default_name"); + f = new Font(0, size); + } + else + { + std::vector names; + collectStrings(namesObj, names); + + f = new Font(&names, size); + } + + /* This is semantically wrong; the new Font object should take + * a dup'ed object here in case of an array. Ditto for the setters. + * However the same bug/behavior exists in all RM versions. */ + rb_iv_set(self, "name", namesObj); setPrivateData(self, f); @@ -65,13 +106,6 @@ RB_METHOD(fontInitialize) if (rgssVer >= 3) wrapProperty(self, &f->getOutColor(), "out_color", ColorType); - if (NIL_P(name)) - name = rb_iv_get(rb_obj_class(self), "default_name"); - - /* Going over the 'name=' function automatically causes - * a possbile name array to be re-verified for existing fonts */ - FontSetName(1, &name, self); - return self; } @@ -105,57 +139,17 @@ RB_METHOD(FontGetName) return rb_iv_get(self, "name"); } -static void -fontSetNameHelper(VALUE self, int argc, VALUE *argv, - const char *nameIv, char *outBuf, size_t outLen) -{ - rb_check_argc(argc, 1); - - VALUE arg = argv[0]; - - // Fixme: in RGSS3, specifying "" (and only that) as font name results in - // no text being drawn (everything else is substituted with Arial I think) - strncpy(outBuf, "", outLen); - - if (RB_TYPE_P(arg, RUBY_T_STRING)) - { - strncpy(outBuf, RSTRING_PTR(arg), outLen); - } - else if (RB_TYPE_P(arg, RUBY_T_ARRAY)) - { - for (long i = 0; i < RARRAY_LEN(arg); ++i) - { - VALUE str = rb_ary_entry(arg, i); - - /* Non-string objects are tolerated (ignored) */ - if (!RB_TYPE_P(str, RUBY_T_STRING)) - continue; - - const char *family = RSTRING_PTR(str); - - /* We only set the core Font object's name attribute - * to the actually existing font name */ - if (!shState->fontState().fontPresent(family)) - continue; - - strncpy(outBuf, family, outLen); - } - } - - /* RMXP doesn't even care if the argument type is - * something other than string/array. Whatever... */ - rb_iv_set(self, nameIv, arg); -} - RB_METHOD(FontSetName) { Font *f = getPrivateData(self); - char result[256]; - fontSetNameHelper(self, argc, argv, "name", - result, sizeof(result)); + rb_check_argc(argc, 1); - f->setName(result); + std::vector namesObj; + collectStrings(argv[0], namesObj); + + f->setName(namesObj); + rb_iv_set(self, "name", argv[0]); return argv[0]; } @@ -223,11 +217,15 @@ RB_METHOD(FontGetDefaultName) RB_METHOD(FontSetDefaultName) { - char result[256]; - fontSetNameHelper(self, argc, argv, "default_name", - result, sizeof(result)); + RB_UNUSED_PARAM; - Font::setDefaultName(result); + rb_check_argc(argc, 1); + + std::vector namesObj; + collectStrings(argv[0], namesObj); + + Font::setDefaultName(namesObj, shState->fontState()); + rb_iv_set(self, "default_name", argv[0]); return argv[0]; } @@ -267,7 +265,24 @@ fontBindingInit() Font::initDefaultDynAttribs(); wrapProperty(klass, &Font::getDefaultColor(), "default_color", ColorType); - rb_iv_set(klass, "default_name", rb_str_new_cstr(Font::getDefaultName())); + + /* Initialize default names */ + const std::vector &defNames = Font::getInitialDefaultNames(); + VALUE defNamesObj; + + if (defNames.size() == 1) + { + defNamesObj = rb_str_new_cstr(defNames[0].c_str()); + } + else + { + defNamesObj = rb_ary_new2(defNames.size()); + + for (size_t i = 0; i < defNames.size(); ++i) + rb_ary_push(defNamesObj, rb_str_new_cstr(defNames[i].c_str())); + } + + rb_iv_set(klass, "default_name", defNamesObj); if (rgssVer >= 3) wrapProperty(klass, &Font::getDefaultOutColor(), "default_out_color", ColorType); @@ -310,14 +325,4 @@ fontBindingInit() INIT_PROP_BIND(Font, Outline, "outline"); INIT_PROP_BIND(Font, OutColor, "out_color"); } - - if (rgssVer >= 2) - { - VALUE defNames = rb_ary_new2(3); - rb_ary_push(defNames, rb_str_new2("Verdana")); - rb_ary_push(defNames, rb_str_new2("Arial")); - rb_ary_push(defNames, rb_str_new2("Courier New")); - - FontSetDefaultName(1, &defNames, klass); - } } diff --git a/src/font.cpp b/src/font.cpp index 560010c..62ab3e8 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -184,7 +184,7 @@ _TTF_Font *SharedFontState::getFont(std::string family, return font; } -bool SharedFontState::fontPresent(std::string family) +bool SharedFontState::fontPresent(std::string family) const { /* Check for substitutions */ if (p->subs.contains(family)) @@ -202,6 +202,26 @@ _TTF_Font *SharedFontState::openBundled(int size) return TTF_OpenFontRW(ops, 1, size); } +void pickExistingFontName(const std::vector &names, + std::string &out, + const SharedFontState &sfs) +{ + /* Note: In RMXP, a names array with no existing entry + * results in no text being drawn at all (same for "" and []); + * we can't replicate this in mkxp due to the default substitute. */ + + for (size_t i = 0; i < names.size(); ++i) + { + if (sfs.fontPresent(names[i])) + { + out = names[i]; + return; + } + } + + out = ""; +} + struct FontPrivate { @@ -229,15 +249,15 @@ struct FontPrivate static Color defaultColorTmp; static Color defaultOutColorTmp; + static std::vector initialDefaultNames; + /* The actual font is opened as late as possible * (when it is queried by a Bitmap), prior it is * set to null */ TTF_Font *sdlFont; - FontPrivate(const char *name = 0, - int size = 0) - : name(name ? std::string(name) : defaultName), - size(size ? size : defaultSize), + FontPrivate(int size) + : size(size), bold(defaultBold), italic(defaultItalic), outline(defaultOutline), @@ -290,6 +310,8 @@ Color *FontPrivate::defaultOutColor = &FontPrivate::defaultOutColorTmp; Color FontPrivate::defaultColorTmp(255, 255, 255, 255); Color FontPrivate::defaultOutColorTmp(0, 0, 0, 128); +std::vector FontPrivate::initialDefaultNames; + bool Font::doesExist(const char *name) { if (!name) @@ -298,10 +320,15 @@ bool Font::doesExist(const char *name) return shState->fontState().fontPresent(name); } -Font::Font(const char *name, +Font::Font(const std::vector *names, int size) { - p = new FontPrivate(name, size); + p = new FontPrivate(size ? size : FontPrivate::defaultSize); + + if (names) + setName(*names); + else + p->name = FontPrivate::defaultName; } Font::Font(const Font &other) @@ -321,17 +348,9 @@ const Font &Font::operator=(const Font &o) return o; } -const char *Font::getName() const +void Font::setName(const std::vector &names) { - return p->name.c_str(); -} - -void Font::setName(const char *value) -{ - if (p->name == value) - return; - - p->name = value; + pickExistingFontName(names, p->name, shState->fontState()); p->sdlFont = 0; } @@ -367,14 +386,15 @@ DEF_ATTR_SIMPLE_STATIC(Font, DefaultOutline, bool, FontPrivate::defaultOutli DEF_ATTR_SIMPLE_STATIC(Font, DefaultColor, Color&, *FontPrivate::defaultColor) DEF_ATTR_SIMPLE_STATIC(Font, DefaultOutColor, Color&, *FontPrivate::defaultOutColor) -const char *Font::getDefaultName() +void Font::setDefaultName(const std::vector &names, + const SharedFontState &sfs) { - return FontPrivate::defaultName.c_str(); + pickExistingFontName(names, FontPrivate::defaultName, sfs); } -void Font::setDefaultName(const char *value) +const std::vector &Font::getInitialDefaultNames() { - FontPrivate::defaultName = value; + return FontPrivate::initialDefaultNames; } void Font::initDynAttribs() @@ -393,8 +413,30 @@ void Font::initDefaultDynAttribs() FontPrivate::defaultOutColor = new Color(FontPrivate::defaultOutColorTmp); } -void Font::initDefaults() +void Font::initDefaults(const SharedFontState &sfs) { + std::vector &names = FontPrivate::initialDefaultNames; + + switch (rgssVer) + { + case 1 : + // FIXME: Japanese version has "MS PGothic" instead + names.push_back("Arial"); + break; + + case 2 : + names.push_back("UmePlus Gothic"); + names.push_back("MS Gothic"); + names.push_back("Courier New"); + break; + + default: + case 3 : + names.push_back("VL Gothic"); + } + + setDefaultName(names, sfs); + FontPrivate::defaultOutline = (rgssVer >= 3 ? true : false); FontPrivate::defaultShadow = (rgssVer == 2 ? true : false); } diff --git a/src/font.h b/src/font.h index 73bb83f..6024007 100644 --- a/src/font.h +++ b/src/font.h @@ -25,6 +25,9 @@ #include "etc.h" #include "util.h" +#include +#include + struct SDL_RWops; struct _TTF_Font; struct Config; @@ -47,7 +50,7 @@ public: _TTF_Font *getFont(std::string family, int size); - bool fontPresent(std::string family); + bool fontPresent(std::string family) const; static _TTF_Font *openBundled(int size); @@ -55,22 +58,6 @@ private: SharedFontStatePrivate *p; }; -/* Concerning Font::name/defaultName : - * In RGSS, this is not actually a string; any type of - * object is accepted, however anything but strings and - * arrays is ignored (and text drawing turns blank). - * Single strings are interpreted as font family names, - * and directly passed to the underlying C++ object; - * arrays however are searched for the first string - * object corresponding to a valid font family name, - * and rendering is done with that. In mkxp, we pass - * this first valid font family as the 'name' attribute - * back to the C++ object on assignment and object - * creation (in case Font.default_name is also an array). - * Invalid parameters (things other than strings or - * arrays not containing any valid family name) are - * passed back as "". */ - struct FontPrivate; class Font @@ -78,31 +65,43 @@ class Font public: static bool doesExist(const char *name); - Font(const char *name = 0, + Font(const std::vector *names = 0, int size = 0); + /* Clone constructor */ Font(const Font &other); + ~Font(); const Font &operator=(const Font &o); - DECL_ATTR( Name, const char * ) - DECL_ATTR( Size, int ) - DECL_ATTR( Bold, bool ) - DECL_ATTR( Italic, bool ) - DECL_ATTR( Color, Color& ) - DECL_ATTR( Shadow, bool ) - DECL_ATTR( Outline, bool ) - DECL_ATTR( OutColor, Color& ) + DECL_ATTR( Size, int ) + DECL_ATTR( Bold, bool ) + DECL_ATTR( Italic, bool ) + DECL_ATTR( Color, Color& ) + DECL_ATTR( Shadow, bool ) + DECL_ATTR( Outline, bool ) + DECL_ATTR( OutColor, Color& ) - DECL_ATTR_STATIC( DefaultName, const char* ) - DECL_ATTR_STATIC( DefaultSize, int ) - DECL_ATTR_STATIC( DefaultBold, bool ) - DECL_ATTR_STATIC( DefaultItalic, bool ) - DECL_ATTR_STATIC( DefaultColor, Color& ) - DECL_ATTR_STATIC( DefaultShadow, bool ) - DECL_ATTR_STATIC( DefaultOutline, bool ) - DECL_ATTR_STATIC( DefaultOutColor, Color& ) + DECL_ATTR_STATIC( DefaultSize, int ) + DECL_ATTR_STATIC( DefaultBold, bool ) + DECL_ATTR_STATIC( DefaultItalic, bool ) + DECL_ATTR_STATIC( DefaultColor, Color& ) + DECL_ATTR_STATIC( DefaultShadow, bool ) + DECL_ATTR_STATIC( DefaultOutline, bool ) + DECL_ATTR_STATIC( DefaultOutColor, Color& ) + + /* There is no point in providing getters for these, + * as the bindings will always return the stored native + * string/array object anyway. It's impossible to mirror + * in the C++ core. + * The core object picks the first existing name from the + * passed array and stores it internally (same for default). */ + void setName(const std::vector &names); + static void setDefaultName(const std::vector &names, + const SharedFontState &sfs); + + static const std::vector &getInitialDefaultNames(); /* Assigns heap allocated objects to object properties; * using this in pure C++ will cause memory leaks @@ -110,7 +109,7 @@ public: void initDynAttribs(); static void initDefaultDynAttribs(); - static void initDefaults(); + static void initDefaults(const SharedFontState &sfs); /* internal */ _TTF_Font *getSdlFont(); diff --git a/src/sharedstate.cpp b/src/sharedstate.cpp index 023ff6b..9872af7 100644 --- a/src/sharedstate.cpp +++ b/src/sharedstate.cpp @@ -172,7 +172,6 @@ void SharedState::initInstance(RGSSThreadData *threadData) * Font depends on SharedState existing */ rgssVersion = threadData->config.rgssVersion; - Font::initDefaults(); _globalIBO = new GlobalIBO(); _globalIBO->ensureSize(1); @@ -183,6 +182,7 @@ void SharedState::initInstance(RGSSThreadData *threadData) try { SharedState::instance = new SharedState(threadData); + Font::initDefaults(instance->p->fontState); defaultFont = new Font(); } catch (const Exception &exc)