git repos / blockattack-game

source/code/Libs/NFont.cpp

browsing at commit = 3a164ed38dd608634f2f2e73c5f977a5ffa9786a

raw · blame · history

/*
NFont v4.0.0: A font class for SDL
by Jonathan Dearborn
Dedicated to the memory of Florian Hufsky

License:
    The short:
    Use it however you'd like, but keep the copyright and license notice
    whenever these files or parts of them are distributed in uncompiled form.

    The long:
Copyright (c) 2014 Jonathan Dearborn

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "NFont.h"
#include "SDL_FontCache.h"

#include <cmath>
#include <cstdio>
#ifndef M_PI
    #define M_PI 3.14159265358979323846
#endif

#include <string>
#include <cstring>
#include <list>
using std::string;
using std::list;

#ifdef NFONT_USE_SDL_GPU
#define NFont_Target GPU_Target
#define NFont_Image GPU_Image
#define NFont_Log GPU_LogError
#else
#define NFont_Target SDL_Renderer
#define NFont_Image SDL_Texture
#define NFont_Log SDL_Log
#endif

#define MIN(a,b) ((a) < (b)? (a) : (b))
#define MAX(a,b) ((a) > (b)? (a) : (b))

#define NFONT_BUFFER_SIZE 1024

// vsnprintf replacement adapted from Valentin Milea:
// http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
#if defined(_MSC_VER) && _MSC_VER < 1900

#define vsnprintf c99_vsnprintf

static int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
{
    int count = -1;

    if (size != 0)
        count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
    if (count == -1)
        count = _vscprintf(format, ap);

    return count;
}

#endif



static inline SDL_Surface* createSurface24(Uint32 width, Uint32 height)
{
    #if SDL_BYTEORDER == SDL_BIG_ENDIAN
        return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, 0xFF0000, 0x00FF00, 0x0000FF, 0);
    #else
        return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0);
    #endif
}

static inline SDL_Surface* createSurface32(Uint32 width, Uint32 height)
{
    #if SDL_BYTEORDER == SDL_BIG_ENDIAN
        return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
    #else
        return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
    #endif
}

static inline char* copyString(const char* c)
{
    if(c == NULL)
        return NULL;

    char* result = new char[strlen(c)+1];
    strcpy(result, c);

    return result;
}

static inline Uint32 getPixel(SDL_Surface *Surface, int x, int y)
{
    Uint8* bits;
    Uint32 bpp;

    if(x < 0 || x >= Surface->w)
        return 0;  // Best I could do for errors

    bpp = Surface->format->BytesPerPixel;
    bits = ((Uint8*)Surface->pixels) + y*Surface->pitch + x*bpp;

    switch (bpp)
    {
        case 1:
            return *((Uint8*)Surface->pixels + y * Surface->pitch + x);
            break;
        case 2:
            return *((Uint16*)Surface->pixels + y * Surface->pitch/2 + x);
            break;
        case 3:
            // Endian-correct, but slower
            Uint8 r, g, b;
            r = *((bits)+Surface->format->Rshift/8);
            g = *((bits)+Surface->format->Gshift/8);
            b = *((bits)+Surface->format->Bshift/8);
            return SDL_MapRGB(Surface->format, r, g, b);
            break;
        case 4:
            return *((Uint32*)Surface->pixels + y * Surface->pitch/4 + x);
            break;
    }

    return 0;  // FIXME: Handle errors better
}

static inline void setPixel(SDL_Surface* surface, int x, int y, Uint32 color)
{
    int bpp = surface->format->BytesPerPixel;
    Uint8* bits = ((Uint8 *)surface->pixels) + y*surface->pitch + x*bpp;

    /* Set the pixel */
    switch(bpp)
    {
        case 1:
            *((Uint8 *)(bits)) = (Uint8)color;
            break;
        case 2:
            *((Uint16 *)(bits)) = (Uint16)color;
            break;
        case 3: { /* Format/endian independent */
            Uint8 r,g,b;
            r = (color >> surface->format->Rshift) & 0xFF;
            g = (color >> surface->format->Gshift) & 0xFF;
            b = (color >> surface->format->Bshift) & 0xFF;
            *((bits)+surface->format->Rshift/8) = r;
            *((bits)+surface->format->Gshift/8) = g;
            *((bits)+surface->format->Bshift/8) = b;
            }
            break;
        case 4:
            *((Uint32 *)(bits)) = (Uint32)color;
            break;
    }
}

static inline void drawPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color, Uint8 alpha)
{
	if(x > surface->clip_rect.x + surface->clip_rect.w || x < surface->clip_rect.x || y > surface->clip_rect.y + surface->clip_rect.h || y < surface->clip_rect.y)
        return;

    switch (surface->format->BytesPerPixel)
    {
        case 1: { /* Assuming 8-bpp */

                Uint8 *pixel = (Uint8 *)surface->pixels + y*surface->pitch + x;

                Uint8 dR = surface->format->palette->colors[*pixel].r;
                Uint8 dG = surface->format->palette->colors[*pixel].g;
                Uint8 dB = surface->format->palette->colors[*pixel].b;
                Uint8 sR = surface->format->palette->colors[color].r;
                Uint8 sG = surface->format->palette->colors[color].g;
                Uint8 sB = surface->format->palette->colors[color].b;

                dR = dR + ((sR-dR)*alpha >> 8);
                dG = dG + ((sG-dG)*alpha >> 8);
                dB = dB + ((sB-dB)*alpha >> 8);

                *pixel = SDL_MapRGB(surface->format, dR, dG, dB);

        }
        break;

        case 2: { /* Probably 15-bpp or 16-bpp */

                Uint32 Rmask = surface->format->Rmask, Gmask = surface->format->Gmask, Bmask = surface->format->Bmask, Amask = surface->format->Amask;
                Uint16 *pixel = (Uint16 *)surface->pixels + y*surface->pitch/2 + x;
                Uint32 dc = *pixel;
                Uint32 R,G,B,A=0;

                R = ((dc & Rmask) + (( (color & Rmask) - (dc & Rmask) ) * alpha >> 8)) & Rmask;
                G = ((dc & Gmask) + (( (color & Gmask) - (dc & Gmask) ) * alpha >> 8)) & Gmask;
                B = ((dc & Bmask) + (( (color & Bmask) - (dc & Bmask) ) * alpha >> 8)) & Bmask;
                if( Amask )
                    A = ((dc & Amask) + (( (color & Amask) - (dc & Amask) ) * alpha >> 8)) & Amask;

                *pixel= R | G | B | A;

        }
        break;

        case 3: { /* Slow 24-bpp mode, usually not used */
            Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
            Uint8 rshift8=surface->format->Rshift/8;
            Uint8 gshift8=surface->format->Gshift/8;
            Uint8 bshift8=surface->format->Bshift/8;
            Uint8 ashift8=surface->format->Ashift/8;



                Uint8 dR, dG, dB, dA;
                Uint8 sR, sG, sB, sA;

                pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;

                dR = *((pix)+rshift8);
                dG = *((pix)+gshift8);
                dB = *((pix)+bshift8);
                dA = *((pix)+ashift8);

                sR = (color>>surface->format->Rshift)&0xff;
                sG = (color>>surface->format->Gshift)&0xff;
                sB = (color>>surface->format->Bshift)&0xff;
                sA = (color>>surface->format->Ashift)&0xff;

                dR = dR + ((sR-dR)*alpha >> 8);
                dG = dG + ((sG-dG)*alpha >> 8);
                dB = dB + ((sB-dB)*alpha >> 8);
                dA = dA + ((sA-dA)*alpha >> 8);

                *((pix)+rshift8) = dR;
                *((pix)+gshift8) = dG;
                *((pix)+bshift8) = dB;
                *((pix)+ashift8) = dA;

        }
        break;

        case 4: { /* Probably 32-bpp */
            Uint32 Rmask = surface->format->Rmask, Gmask = surface->format->Gmask, Bmask = surface->format->Bmask, Amask = surface->format->Amask;
            Uint32* pixel = (Uint32*)surface->pixels + y*surface->pitch/4 + x;
            Uint32 source = *pixel;
            Uint32 R,G,B,A;
            R = color & Rmask;
            G = color & Gmask;
            B = color & Bmask;
            A = 0;  // keep this as 0 to avoid corruption of non-alpha surfaces

            // Blend and keep dest alpha
            if( alpha != SDL_ALPHA_OPAQUE ){
                R = ((source & Rmask) + (( R - (source & Rmask) ) * alpha >> 8)) & Rmask;
                G = ((source & Gmask) + (( G - (source & Gmask) ) * alpha >> 8)) & Gmask;
                B = ((source & Bmask) + (( B - (source & Bmask) ) * alpha >> 8)) & Bmask;
            }
            if(Amask)
                A = (source & Amask);

            *pixel = R | G | B | A;
        }
        break;
    }
}

static inline NFont::Rectf rectUnion(const NFont::Rectf& A, const NFont::Rectf& B)
{
    float x,x2,y,y2;
    x = MIN(A.x, B.x);
    y = MIN(A.y, B.y);
    x2 = MAX(A.x+A.w, B.x+B.w);
    y2 = MAX(A.y+A.h, B.y+B.h);
    NFont::Rectf result(x, y, MAX(0, x2 - x), MAX(0, y2 - y));
    return result;
}

// Adapted from SDL_IntersectRect
static inline NFont::Rectf rectIntersect(const NFont::Rectf& A, const NFont::Rectf& B)
{
    NFont::Rectf result;
	float Amin, Amax, Bmin, Bmax;

	// Horizontal intersection
	Amin = A.x;
	Amax = Amin + A.w;
	Bmin = B.x;
	Bmax = Bmin + B.w;
	if(Bmin > Amin)
	        Amin = Bmin;
	result.x = Amin;
	if(Bmax < Amax)
	        Amax = Bmax;
	result.w = Amax - Amin > 0 ? Amax - Amin : 0;

	// Vertical intersection
	Amin = A.y;
	Amax = Amin + A.h;
	Bmin = B.y;
	Bmax = Bmin + B.h;
	if(Bmin > Amin)
	        Amin = Bmin;
	result.y = Amin;
	if(Bmax < Amax)
	        Amax = Bmax;
	result.h = Amax - Amin > 0 ? Amax - Amin : 0;

	return result;
}

static inline SDL_Surface* copySurface(SDL_Surface *Surface)
{
    return SDL_ConvertSurface(Surface, Surface->format, Surface->flags);
}









NFont::Color::Color()
    : r(0), g(0), b(0), a(255)
{}
NFont::Color::Color(Uint8 r, Uint8 g, Uint8 b)
    : r(r), g(g), b(b), a(255)
{}
NFont::Color::Color(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
    : r(r), g(g), b(b), a(a)
{}
NFont::Color::Color(const SDL_Color& color)
    : r(color.r), g(color.g), b(color.b), a(color.a)
{}

NFont::Color& NFont::Color::rgb(Uint8 R, Uint8 G, Uint8 B)
{
    r = R;
    g = G;
    b = B;

    return *this;
}

NFont::Color& NFont::Color::rgba(Uint8 R, Uint8 G, Uint8 B, Uint8 A)
{
    r = R;
    g = G;
    b = B;
    a = A;

    return *this;
}

NFont::Color& NFont::Color::color(const SDL_Color& color)
{
    r = color.r;
    g = color.g;
    b = color.b;
    a = color.a;

    return *this;
}

SDL_Color NFont::Color::to_SDL_Color() const
{
    SDL_Color c = {r, g, b, a};
    return c;
}




NFont::Rectf::Rectf()
    : x(0), y(0), w(0), h(0)
{}

NFont::Rectf::Rectf(float x, float y)
    : x(x), y(y), w(0), h(0)
{}

NFont::Rectf::Rectf(float x, float y, float w, float h)
    : x(x), y(y), w(w), h(h)
{}

NFont::Rectf::Rectf(const SDL_Rect& rect)
    : x(rect.x), y(rect.y), w(rect.w), h(rect.h)
{}

#ifdef NFONT_USE_SDL_GPU
NFont::Rectf::Rectf(const GPU_Rect& rect)
    : x(rect.x), y(rect.y), w(rect.w), h(rect.h)
{}
#endif

SDL_Rect NFont::Rectf::to_SDL_Rect() const
{
    SDL_Rect r = {int(x), int(y), int(w), int(h)};
    return r;
}

#ifdef NFONT_USE_SDL_GPU
GPU_Rect NFont::Rectf::to_GPU_Rect() const
{
    return GPU_MakeRect(x, y, w, h);
}
#endif







char* NFont::buffer = NULL;  // Shared buffer for efficient drawing


// Constructors
NFont::NFont()
{
    init();
}

#ifdef NFONT_USE_SDL_GPU
NFont::NFont(TTF_Font* ttf)
{
    init();
    load(ttf, FC_GetDefaultColor(font));
}
NFont::NFont(TTF_Font* ttf, const NFont::Color& color)
{
    init();
    load(ttf, color);
}
NFont::NFont(const char* filename_ttf, Uint32 pointSize)
{
    init();
    load(filename_ttf, pointSize);
}
NFont::NFont(const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style)
{
    init();
    load(filename_ttf, pointSize, color, style);
}
NFont::NFont(SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style)
{
    init();
    load(file_rwops_ttf, own_rwops, pointSize, color, style);
}

#else

NFont::NFont(NFont_Target* renderer, TTF_Font* ttf)
{
    init();
    load(renderer, ttf, FC_GetDefaultColor(font));
}
NFont::NFont(NFont_Target* renderer, TTF_Font* ttf, const NFont::Color& color)
{
    init();
    load(renderer, ttf, color);
}
NFont::NFont(NFont_Target* renderer, const char* filename_ttf, Uint32 pointSize)
{
    init();
    load(renderer, filename_ttf, pointSize);
}
NFont::NFont(NFont_Target* renderer, const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style)
{
    init();
    load(renderer, filename_ttf, pointSize, color, style);
}
NFont::NFont(NFont_Target* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style)
{
    init();
    load(renderer, file_rwops_ttf, own_rwops, pointSize, color, style);
}
#endif


NFont::~NFont()
{
    FC_FreeFont(font);
}


void NFont::init()
{
    font = FC_CreateFont();

    if(buffer == NULL)
        buffer = new char[NFONT_BUFFER_SIZE];
}






void NFont::setLoadingString(const char* str)
{
    FC_SetLoadingString(font, str);
}

#ifdef NFONT_USE_SDL_GPU
bool NFont::load(TTF_Font* ttf)
#else
bool NFont::load(NFont_Target* renderer, TTF_Font* ttf)
#endif
{
    #ifdef NFONT_USE_SDL_GPU
    return load(ttf, FC_GetDefaultColor(font));
    #else
    return load(renderer, ttf, Color(0,0,0,255));
    #endif
}

#ifdef NFONT_USE_SDL_GPU
bool NFont::load(TTF_Font* ttf, const NFont::Color& color)
#else
bool NFont::load(NFont_Target* renderer, TTF_Font* ttf, const NFont::Color& color)
#endif
{
    if(ttf == NULL)
        return false;

    #ifndef NFONT_USE_SDL_GPU
    if(renderer == NULL)
        return false;
    #endif

    FC_ClearFont(font);
    #ifdef NFONT_USE_SDL_GPU
    return FC_LoadFontFromTTF(font, ttf, color.to_SDL_Color());
    #else
    return FC_LoadFontFromTTF(font, renderer, ttf, color.to_SDL_Color());
    #endif
}

#ifdef NFONT_USE_SDL_GPU
bool NFont::load(const char* filename_ttf, Uint32 pointSize)
#else
bool NFont::load(NFont_Target* renderer, const char* filename_ttf, Uint32 pointSize)
#endif
{
    #ifdef NFONT_USE_SDL_GPU
    return load(filename_ttf, pointSize, Color(0,0,0,255));
    #else
    return load(renderer, filename_ttf, pointSize, Color(0,0,0,255));
    #endif
}

#ifdef NFONT_USE_SDL_GPU
bool NFont::load(const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style)
#else
bool NFont::load(NFont_Target* renderer, const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style)
#endif
{
    FC_ClearFont(font);
    #ifdef NFONT_USE_SDL_GPU
    return FC_LoadFont(font, filename_ttf, pointSize, color.to_SDL_Color(), style);
    #else
    return FC_LoadFont(font, renderer, filename_ttf, pointSize, color.to_SDL_Color(), style);
    #endif
}

#ifdef NFONT_USE_SDL_GPU
bool NFont::load(SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style)
#else
bool NFont::load(NFont_Target* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style)
#endif
{
    FC_ClearFont(font);
    #ifdef NFONT_USE_SDL_GPU
    return FC_LoadFont_RW(font, file_rwops_ttf, own_rwops, pointSize, color.to_SDL_Color(), style);
    #else
    return FC_LoadFont_RW(font, renderer, file_rwops_ttf, own_rwops, pointSize, color.to_SDL_Color(), style);
    #endif
}



void NFont::free()
{
    FC_ClearFont(font);
}



NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_Draw(font, dest, x, y, "%s", buffer);
}

/*static int getIndexPastWidth(const char* text, int width, const int* charWidth)
{
    int charnum;
    int len = strlen(text);

    for (int index = 0; index < len; index++)
    {
        char c = text[index];
        charnum = (unsigned char)(c) - 33;

        // spaces and nonprintable characters
        if (c == ' ' || charnum > 222)
        {
            width -= charWidth[0];
        }
        else
            width -= charWidth[charnum];

        if(width <= 0)
            return index;
    }
    return 0;
}*/



/*static list<string> explode(const string& str, char delimiter)
{
    list<string> result;

    size_t oldPos = 0;
    size_t pos = str.find_first_of(delimiter);
    while(pos != string::npos)
    {
        result.push_back(str.substr(oldPos, pos - oldPos));
        oldPos = pos+1;
        pos = str.find_first_of(delimiter, oldPos);
    }

    result.push_back(str.substr(oldPos, string::npos));

    return result;
}*/

NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(box.x, box.y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    #ifdef NFONT_USE_SDL_GPU
    return FC_DrawBox(font, dest, box.to_GPU_Rect(), "%s", buffer);
    #else
    return FC_DrawBox(font, dest, box.to_SDL_Rect(), "%s", buffer);
    #endif
}

static FC_AlignEnum translate_enum_NFont_to_FC(NFont::AlignEnum align)
{
    switch(align)
    {
    case NFont::LEFT:
        return FC_ALIGN_LEFT;
    case NFont::CENTER:
        return FC_ALIGN_CENTER;
    case NFont::RIGHT:
        return FC_ALIGN_RIGHT;
    default:
        return FC_ALIGN_LEFT;
    }
}

NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, AlignEnum align, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(box.x, box.y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    #ifdef NFONT_USE_SDL_GPU
    return FC_DrawBoxAlign(font, dest, box.to_GPU_Rect(), translate_enum_NFont_to_FC(align), "%s", buffer);
    #else
    return FC_DrawBoxAlign(font, dest, box.to_SDL_Rect(), translate_enum_NFont_to_FC(align), "%s", buffer);
    #endif
}

NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, const Scale& scale, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(box.x, box.y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    #ifdef NFONT_USE_SDL_GPU
    return FC_DrawBoxScale(font, dest, box.to_GPU_Rect(), FC_MakeScale(scale.x, scale.y), "%s", buffer);
    #else
    return FC_DrawBoxScale(font, dest, box.to_SDL_Rect(), FC_MakeScale(scale.x, scale.y), "%s", buffer);
    #endif
}

NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, const Color& color, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(box.x, box.y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    #ifdef NFONT_USE_SDL_GPU
    return FC_DrawBoxColor(font, dest, box.to_GPU_Rect(), color.to_SDL_Color(), "%s", buffer);
    #else
    return FC_DrawBoxColor(font, dest, box.to_SDL_Rect(), color.to_SDL_Color(), "%s", buffer);
    #endif
}

NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, const Effect& effect, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(box.x, box.y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    #ifdef NFONT_USE_SDL_GPU
    return FC_DrawBoxEffect(font, dest, box.to_GPU_Rect(), FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer);
    #else
    return FC_DrawBoxEffect(font, dest, box.to_SDL_Rect(), FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer);
    #endif
}

NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawColumn(font, dest, x, y, width, "%s", buffer);
}

NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, AlignEnum align, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawColumnAlign(font, dest, x, y, width, translate_enum_NFont_to_FC(align), "%s", buffer);
}

NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, const Scale& scale, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawColumnScale(font, dest, x, y, width, FC_MakeScale(scale.x, scale.y), "%s", buffer);
}

NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, const Color& color, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawColumnColor(font, dest, x, y, width, color.to_SDL_Color(), "%s", buffer);
}

NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, const Effect& effect, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    #ifdef NFONT_USE_SDL_GPU
    return FC_DrawColumnEffect(font, dest, x, y, width, FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer);
    #else
    return FC_DrawColumnEffect(font, dest, x, y, width, FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer);
    #endif
}



NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, const Scale& scale, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawScale(font, dest, x, y, FC_MakeScale(scale.x, scale.y), "%s", buffer);
}

NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, AlignEnum align, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawAlign(font, dest, x, y, translate_enum_NFont_to_FC(align), "%s", buffer);
}

NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, const Color& color, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawColor(font, dest, x, y, color.to_SDL_Color(), "%s", buffer);
}


NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, const Effect& effect, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawEffect(font, dest, x, y, FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer);
}




// Getters


NFont::FilterEnum NFont::getFilterMode() const
{
    FC_FilterEnum f = FC_GetFilterMode(font);
    if(f == FC_FILTER_LINEAR)
        return NFont::LINEAR;
    return NFont::NEAREST;
}

Uint16 NFont::getHeight() const
{
    return FC_GetLineHeight(font);
}

Uint16 NFont::getHeight(const char* formatted_text, ...) const
{
    if(formatted_text == NULL)
        return 0;

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetHeight(font, "%s", buffer);
}

Uint16 NFont::getWidth(const char* formatted_text, ...)
{
    if (formatted_text == NULL)
        return 0;

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetWidth(font, "%s", buffer);
}


NFont::Rectf NFont::getCharacterOffset(Uint16 position_index, int column_width, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(0,0,0,0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetCharacterOffset(font, position_index, column_width, "%s", buffer);
}

// Given an offset (x,y) from the text draw position (the upper-left corner), returns the character position (UTF-8 index)
Uint16 NFont::getPositionFromOffset(float x, float y, int column_width, NFont::AlignEnum align, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return 0;

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetPositionFromOffset(font, x, y, column_width, translate_enum_NFont_to_FC(align), "%s", buffer);
}


Uint16 NFont::getColumnHeight(Uint16 width, const char* formatted_text, ...)
{
    if(formatted_text == NULL || width == 0)
        return 0;

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetColumnHeight(font, width, "%s", buffer);
}

int NFont::getAscent(const char character)
{
    return FC_GetAscent(font, "%c", character);
}

int NFont::getAscent() const
{
    return FC_GetAscent(font, NULL);
}

int NFont::getAscent(const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return FC_GetAscent(font, NULL);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetAscent(font, "%s", buffer);
}

int NFont::getDescent(const char character)
{
    return FC_GetDescent(font, "%c", character);
}

int NFont::getDescent() const
{
    return FC_GetDescent(font, NULL);
}

int NFont::getDescent(const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return FC_GetDescent(font, NULL);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetDescent(font, "%s", buffer);
}

int NFont::getSpacing() const
{
    return FC_GetSpacing(font);
}

int NFont::getLineSpacing() const
{
    return FC_GetLineSpacing(font);
}

Uint16 NFont::getBaseline() const
{
    return FC_GetBaseline(font);
}

Uint16 NFont::getMaxWidth() const
{
    return FC_GetMaxWidth(font);
}

NFont::Color NFont::getDefaultColor() const
{
    return FC_GetDefaultColor(font);
}





// Setters

void NFont::setFilterMode(NFont::FilterEnum filter)
{
    if(filter == NFont::LINEAR)
        FC_SetFilterMode(font, FC_FILTER_LINEAR);
    else
        FC_SetFilterMode(font, FC_FILTER_NEAREST);
}

void NFont::setSpacing(int LetterSpacing)
{
    FC_SetSpacing(font, LetterSpacing);
}

void NFont::setLineSpacing(int LineSpacing)
{
    FC_SetLineSpacing(font, LineSpacing);
}

void NFont::setDefaultColor(const Color& color)
{
    FC_SetDefaultColor(font, color.to_SDL_Color());
}