view dmtext_bm.c @ 510:43ea59887c69

Start work on making C64 formats encoding possible by changing DMDecodeOps to DMEncDecOps and adding fields and op enums for custom encode functions, renaming, etc. Split generic op sanity checking into a separate function in preparation for its use in generic encoding function.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 19 Nov 2012 15:06:01 +0200
parents b842cc92c787
children 656332eec724
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"


void dmDrawBMTextConst(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt)
{
    const char *ptr = fmt;
    DMUnscaledBlitFunc blit = NULL;

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

        if (ch >= 0 && ch < font->nglyphs && (glyph = font->glyphs[ch]) != NULL)
        {
            if (blit == NULL)
                blit = dmGetUnscaledBlitFunc(glyph->format, screen->format, mode);
            
            blit(glyph, xc, yc, 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[512];
    vsnprintf(tmp, sizeof(tmp), fmt, ap);
    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;
}


/* Set the palette for each glyph. While the function allows you to
 * specify 'start' and 'end' indices and palette array freely, you should
 * typically use DMFONT_NPALETTE size palette starting at index 0.
 */
int dmSetBitmapFontPalette(DMBitmapFont *font, SDL_Color *pal, int start, int size)
{
    int i;

    if (font == NULL)
        return DMERR_NULLPTR;
    
    if (start < 0 || size < 1)
        return DMERR_INVALID_ARGS;
    
    for (i = 0; i < font->nglyphs; i++)
    {
        SDL_Surface *glyph = font->glyphs[i];
        if (glyph != NULL)
        {
            SDL_SetColors(glyph, pal, start, size);
        }
    }

    return DMERR_OK;
}

//#define FN_DEBUG

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;

#ifdef FN_DEBUG
        fprintf(stderr, "TSFONT v%d.%d (0x%04x), encoding=%d\n", version >> 8, version & 0xff, version, encoding);
#endif
        
        // 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 = 256;
    }
    else
    {
        dmf_read_le16(res, &nglyphs);
        dmf_read_le16(res, &maxglyph);
    }
    
    width = dmfgetc(res);
    height = dmfgetc(res);

#ifdef FN_DEBUG
    fprintf(stderr, "nglyphs=%d (0x%02x), maxglyph=%d (0x%02x) width=%d, height=%d\n",
        nglyphs, nglyphs, maxglyph, maxglyph, width, height);
#endif

    if (tsfont)
    {
        // TSFONT color assignments (boolean) .. we discard this.
        dmfgetc(res);
        
        // Very old TSFONTs have some extra data that is not used
        // .. can't actually even remember what it was for.
        if (version == 0x0200)
        {
            int i;
            for (i = 0; i < 32; i++)
                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 = pal[n].g = pal[n].b = 0;
            pal[n].unused = n > 0 ? 255 : 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);

#ifdef FN_DEBUG
            fprintf(stderr, "#%d @ %d - w=%d, h=%d\n", i, index, width, height);
#endif

            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;
}