view src/dmtext_bm.c @ 1785:86d10d5d4915

Fix case where DMGrowBuf is growing backwards and needs to be reallocated in dmGrowBufRealloc() and the data is moved to the "end" of the newly grown buffer. Previously we used clrsize as data size, but that is (in retrospect) obviously incorrect. Use old buffer size instead.
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 13 Jun 2018 01:39:06 +0300
parents 5e5f75b45f8d
children a3983da9b8b9
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, BOOL condensed, 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);

            if (blit != NULL)
            {
                blit(glyph, xc, yc, screen);
                xc += condensed ? glyph->w : font->width;
            }
        }
        else
            xc += font->width;
    }
}


void dmDrawBMTextVA(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt, va_list ap)
{
    char tmp[512];
    vsnprintf(tmp, sizeof(tmp), fmt, ap);
    dmDrawBMTextConst(screen, font, condensed, mode, xc, yc, tmp);
    dmFree(tmp);
}


void dmDrawBMText(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    dmDrawBMTextVA(screen, font, condensed, 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_SetPaletteColors(glyph->format->palette, 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].a = 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_SetPaletteColors(glyph->format->palette, 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;
}