view dmtext_bm.c @ 91:e1e308167991

Various improvements in the bitmapped font loading and saving functions.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 02 Oct 2012 17:50:28 +0300
parents b10884934aca
children f16d102dbbac
line wrap: on
line source

/*
 * DMLib
 * -- Bitmap and TTF text & font support
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2012 Tecnic Software productions (TNSP)
 */
#include "dmtext.h"
#include "dmresw.h"


void dmDrawBMTextConst(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt)
{
    const char *ptr = fmt;
    DMScaledBlitFunc blit = dmGetScaledBlitFunc(font->glyphs[0]->format, screen->format, mode);

    while (*ptr)
    {
        SDL_Surface *glyph;
        int pos;
        int ch = *ptr++;

        if (ch == '_')
        {
            xc += 4;
            continue;
        }
        else
        if (ch >= 'A' && ch <= 'Z')
            pos = ch - 'A' + 256 + 1;
        else
        if (ch >= 'a' && ch <= 'z')
            pos = ch - 'a' + 1;
        else
        if (ch >= '0' && ch <= '9')
            pos = ch - '0' + 48;
        else
            pos = ch;
        
        if (pos >= 0 && pos < font->nglyphs)
        {
            glyph = font->glyphs[pos];
            blit(glyph, xc, yc, glyph->w, glyph->h, screen);
            xc += glyph->w;
        }
        else
            xc += font->width;
    }
}


void dmDrawBMTextVA(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt, va_list ap)
{
    char *tmp = dm_strdup_vprintf(fmt, ap);
    if (tmp != NULL)
    {
        dmDrawBMTextConst(screen, font, mode, xc, yc, tmp);
        dmFree(tmp);
    }
}


void dmDrawBMText(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt, ...)
{
    va_list ap;
    
    va_start(ap, fmt);
    dmDrawBMTextVA(screen, font, mode, xc, yc, fmt, ap);
    va_end(ap);
}


DMBitmapFont *dmNewBitmapFont(int nglyphs, int width, int height)
{
    DMBitmapFont *font = dmMalloc0(sizeof(DMBitmapFont));
    if (font == NULL)
        return NULL;
    
    font->width = width;
    font->height = height;
    font->nglyphs = nglyphs;
    font->glyphs = dmCalloc(nglyphs, sizeof(SDL_Surface *));
    if (font->glyphs == NULL)
    {
        dmFree(font);
        return NULL;
    }

    return font;
}


int dmFreeBitmapFont(DMBitmapFont *font)
{
    int i;

    if (font == NULL)
        return DMERR_NULLPTR;
    
    for (i = 0; i < font->nglyphs; i++)
    {
        if (font->glyphs[i] != NULL)
        {
            SDL_FreeSurface(font->glyphs[i]);
            font->glyphs[i] = NULL;
        }
    }

    dmFree(font);
    return DMERR_OK;
}


int dmCreateBitmapFontFromImage(SDL_Surface *image, int width, int height, DMBitmapFont **pfont)
{
    int nglyph, xc, yc, xglyphs, yglyphs;
    DMBitmapFont *font;

    if (image->w < width || width < 4 || image->h < height || height < 4)
        return DMERR_INVALID_ARGS;
    
    xglyphs = image->w / width;
    yglyphs = image->h / height;
    
    if ((font = dmNewBitmapFont(xglyphs * yglyphs, width, height)) == NULL)
        return DMERR_MALLOC;

/*
    fprintf(stderr, "%d x %d split as %d x %d blocks => %d x %d = %d glyphs\n",
        image->w, image->h,
        width, height,
        xglyphs, yglyphs, xglyphs * yglyphs);
*/
    
    nglyph = 0;
    for (yc = 0; yc < yglyphs; yc++)
    for (xc = 0; xc < xglyphs; xc++)
    {
        SDL_Surface *glyph = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
            image->format->BitsPerPixel,
            image->format->Rmask,
            image->format->Gmask,
            image->format->Bmask,
            image->format->Amask);
        
        if (glyph == NULL)
        {
            dmFreeBitmapFont(font);
            return DMERR_MALLOC;
        }

        SDL_Rect r;
        r.x = xc * width;
        r.y = yc * height;
        r.w = width;
        r.h = height;
        
        SDL_BlitSurface(image, &r, glyph, NULL);
        
        font->glyphs[nglyph++] = glyph;
    }
    
    *pfont = font;
    return DMERR_OK;
}


int dmLoadBitmapFont(DMResource *res, DMBitmapFont **pfont)
{
    DMBitmapFont *font;
    char magic[8];
    Uint16 version, nglyphs, maxglyph;
    int width, height;
    BOOL tsfont = FALSE;
    
    // Check magic and version
    dmf_read_str(res, (Uint8 *) &magic, 6);
    dmf_read_le16(res, &version);
    
    // Check if it is a legacy TSFONT file
    if (memcmp(magic, TSFONT_MAGIC, 6) == 0)
    {
        // Yep, we handle these a bit differently
        int encoding = dmfgetc(res);
        tsfont = TRUE;

        if (version > TSFONT_VERSION)
            return DMERR_VERSION;
        
        // There were only two encodings, 0 = none and 1 = RLE
        // of which RLE was never actually used ... derp.
        if (encoding != 0)
            return DMERR_NOT_SUPPORTED;
    }
    else
    {
        if (memcmp(magic, DMFONT_MAGIC, 6) != 0)
            return DMERR_INVALID;

        if (version > DMFONT_VERSION)
            return DMERR_VERSION;
    }
    
    // Read other header data
    if (tsfont)
    {
        // TSFONT only has number of glyphs stored in the file
        nglyphs = dmfgetc(res);
        
        // Maximum glyph number
        maxglyph = 255;
    }
    else
    {
        dmf_read_le16(res, &nglyphs);
        dmf_read_le16(res, &maxglyph);
    }
    
    width = dmfgetc(res);
    height = dmfgetc(res);

    if (tsfont)
    {
        // TSFONT color assignments (boolean) .. we discard this.
        dmfgetc(res);
    }

    if (width < DMFONT_MIN_WIDTH ||
        height < DMFONT_MIN_HEIGHT ||
        width > DMFONT_MAX_WIDTH ||
        height > DMFONT_MAX_HEIGHT ||
        nglyphs > DMFONT_MAX_GLYPHS ||
        maxglyph > DMFONT_MAX_GLYPHS ||
        maxglyph < 1)
        return DMERR_INVALID_DATA;

    // Allocate font
    if ((*pfont = font = dmNewBitmapFont(maxglyph, width, height)) == NULL)
        return DMERR_MALLOC;

    // Read glyph data, if any
    if (nglyphs > 0)
    {
        int n, i;
        Uint32 BitsPerPixel, Rmask, Gmask, Bmask, Amask;
        SDL_Color pal[DMFONT_NPALETTE];

        // Setup palette for 8bpp fonts
        for (n = 0; n < DMFONT_NPALETTE; n++)
        {
            pal[n].r = n * 16;
            pal[n].g = n * 16;
            pal[n].b = n * 16;
            pal[n].unused = n > 0 ? n * 16 : 0;
        }

        if (tsfont)
        {
            BitsPerPixel = 8;
            Rmask = Gmask = Bmask = Amask = 0;
        }
        else
        {
            BitsPerPixel = dmfgetc(res);
            dmf_read_le32(res, &Rmask);
            dmf_read_le32(res, &Gmask);
            dmf_read_le32(res, &Bmask);
            dmf_read_le32(res, &Amask);
        }

        for (i = 0; i < nglyphs; i++)
        {
            int y;
            Uint16 index;
            Uint8 *pixels;
            SDL_Surface *glyph;

            // TSFONT format has only byte sized index
            if (tsfont)
                index = dmfgetc(res);
            else
                dmf_read_le16(res, &index);

            // Read dimensions
            width = dmfgetc(res);
            height = dmfgetc(res);

            if (width < DMFONT_MIN_WIDTH ||
                height < DMFONT_MIN_HEIGHT ||
                width > DMFONT_MAX_WIDTH ||
                height > DMFONT_MAX_HEIGHT ||
                index > maxglyph)
                return DMERR_INVALID_DATA;

            // Allocate bitmap
            font->glyphs[index] = glyph = SDL_CreateRGBSurface(
                SDL_SWSURFACE, width, height,
                BitsPerPixel, Rmask, Gmask,
                Bmask,
                Amask);

            if (glyph == NULL)
                return DMERR_MALLOC;

            if (BitsPerPixel == 8)
                SDL_SetColors(glyph, pal, 0, DMFONT_NPALETTE);

            // Read pixel data
            pixels = glyph->pixels;
            for (y = 0; y < glyph->h; y++)
            {
                if (dmfread(pixels, glyph->format->BytesPerPixel, glyph->w, res) != (size_t) glyph->w)
                    return DMERR_FREAD;
                pixels += glyph->pitch;
            }
        }
    }

    return DMERR_OK;
}


int dmSaveBitmapFont(DMResource *res, DMBitmapFont *font)
{
    int maxglyph, nglyphs, n;
    if (font == NULL)
        return DMERR_NULLPTR;

    if (font->nglyphs > DMFONT_MAX_GLYPHS ||
        font->width > DMFONT_MAX_WIDTH ||
        font->height > DMFONT_MAX_HEIGHT ||
        font->width < DMFONT_MIN_WIDTH ||
        font->height < DMFONT_MIN_HEIGHT)
        return DMERR_INVALID_DATA;

    // Count number of actually existing glyphs
    for (maxglyph = nglyphs = n = 0; n < font->nglyphs; n++)
    {
        if (font->glyphs[n] != NULL)
        {
            nglyphs++;
            maxglyph = n;
        }
    }
    
    // Write the DMFONT header
    if (!dmf_write_str(res, (Uint8 *) DMFONT_MAGIC, 6))
        return DMERR_FWRITE;

    dmf_write_le16(res, DMFONT_VERSION);
    dmf_write_le16(res, nglyphs);
    dmf_write_le16(res, maxglyph);
    dmfputc(font->width, res);
    dmfputc(font->height, res);
    
    if (nglyphs > 0)
    {
        int i;
        SDL_Surface *glyph = font->glyphs[maxglyph];

        // If there are actual glyphs stored, save thi
        dmfputc(glyph->format->BitsPerPixel, res);
        dmf_write_le32(res, glyph->format->Rmask);
        dmf_write_le32(res, glyph->format->Gmask);
        dmf_write_le32(res, glyph->format->Bmask);
        dmf_write_le32(res, glyph->format->Amask);

        for (i = 0; i < font->nglyphs; i++)
        {
            glyph = font->glyphs[i];
            if (glyph != NULL)
            {
                int y;
                Uint8 *pixels = glyph->pixels;
                
                if (glyph->w < DMFONT_MIN_WIDTH ||
                    glyph->h < DMFONT_MIN_HEIGHT ||
                    glyph->w > DMFONT_MAX_WIDTH ||
                    glyph->h > DMFONT_MAX_HEIGHT)
                    return DMERR_INVALID_DATA;

                // Each glyph has its table index and w/h stored
                dmf_write_le16(res, i);
                dmf_write_le16(res, glyph->w);
                dmf_write_le16(res, glyph->h);

                // Write the pixel data
                for (y = 0; y < glyph->h; y++)
                {
                    if (dmfwrite(pixels, glyph->format->BytesPerPixel, glyph->w, res) != (size_t) glyph->w)
                        return DMERR_FWRITE;
                    pixels += glyph->pitch;
                }
            }
        }
    }

    return DMERR_OK;
}