view src/dmtext_bm.c @ 2298:b5abfff07ca9

Add new DMGrowBuf helper functions dmGrowBufCopyOffsSize() and dmGrowBufConstCopyOffsSize().
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 04 Jul 2019 10:54:16 +0300
parents 934cc71c97eb
children 92b93a12c014
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, const DMBitmapFont *font,
    const BOOL condensed, const int mode, int xc, int yc, const char *fmt)
{
    const char *ptr = fmt;
    DMUnscaledBlitFunc blit = dmGetUnscaledBlitFunc(font->glyphs->format, screen->format, mode);
    SDL_Surface surf;
    Uint8 *orig = font->glyphs->pixels;

    memcpy(&surf, font->glyphs, sizeof(SDL_Surface));

    while (*ptr)
    {
        unsigned char ch = *ptr++;
        if (ch < font->maxglyph)
        {
            DMBitmapGlyph *glyph = &font->glyphMap[ch];
            if (glyph->index >= 0)
            {
                surf.pixels = orig + glyph->index * font->gsize;
                surf.w = glyph->width;
                surf.h = glyph->height;

                blit(&surf, xc, yc, screen);
                xc += condensed ? glyph->width : font->width;
            }
            else
                xc += font->width;
        }
        else
            xc += font->width;
    }
}


void dmDrawBMTextVA(SDL_Surface *screen, const DMBitmapFont *font,
    const BOOL condensed, const int mode, const int xc, const 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, const DMBitmapFont *font,
    const BOOL condensed, const int mode, const int xc, const 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(const int nglyphs, const int maxglyph,
    const int width, const int height, const int bpp)
{
    DMBitmapFont *font = dmMalloc0(sizeof(DMBitmapFont));
    if (font == NULL || (bpp != 8 && bpp != 32))
        return NULL;

    font->width = width;
    font->height = height;
    font->nglyphs = nglyphs;
    font->maxglyph = maxglyph;

    if ((font->glyphMap = dmCalloc(font->maxglyph, sizeof(DMBitmapGlyph))) == NULL)
        goto error;

    if ((font->glyphs = SDL_CreateRGBSurfaceWithFormat(
        0, width, height * (nglyphs + 1), bpp,
        bpp == 8 ? SDL_PIXELFORMAT_INDEX8 : SDL_PIXELFORMAT_RGBA32)) == NULL)
        goto error;

    font->gsize = font->height * font->glyphs->pitch;

    for (int i = 0; i < font->maxglyph; i++)
    {
        DMBitmapGlyph *glyph = &font->glyphMap[i];
        glyph->width  = width;
        glyph->height = height;
        glyph->index  = -1;     // means that this index is empty/unused
    }

    return font;

error:
    dmFreeBitmapFont(font);
    return NULL;
}


int dmFreeBitmapFont(DMBitmapFont *font)
{
    if (font == NULL)
        return DMERR_NULLPTR;

    if (font->glyphs != NULL)
        SDL_FreeSurface(font->glyphs);

    dmFree(font->glyphMap);
    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, const SDL_Color *pal, const int start, const int size)
{
    if (font == NULL)
        return DMERR_NULLPTR;

    if (start < 0 || size < 1)
        return DMERR_INVALID_ARGS;

    SDL_SetPaletteColors(font->glyphs->format->palette, pal, start, size);

    return DMERR_OK;
}


//#define FN_DEBUG


int dmLoadBitmapFont(DMResource *fp, DMBitmapFont **pfont)
{
    DMBitmapFont *font;
    char magic[8];
    Uint16 version, nglyphs, maxglyph;
    Uint8 width, height, bpp;
    BOOL tsfont = FALSE;

    // Check magic and version
    if (!dmf_read_str(fp, (Uint8 *) &magic, 6) ||
        !dmf_read_le16(fp, &version))
        return DMERR_FREAD;

    // 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(fp);
        tsfont = TRUE;

#ifdef FN_DEBUG
        fprintf(stderr, "TSFONT v%d.%d (0x%04x), encoding=%d\n",
            version >> 8, version & 0xff, version, encoding);
#endif

        if (version > TSFONT_VERSION || version < 0x0200)
            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)
    {
#ifdef FN_DEBUG
        fprintf(stderr, "DMFONT v%d.%d (0x%04x)\n",
            version >> 8, version & 0xff, version);
#endif

        if (version > DMFONT_VERSION)
            return DMERR_VERSION;
    }
    else
        return DMERR_INVALID;

    // Read other header data
    if (tsfont)
    {
        // TSFONT has number of glyphs and dimensions
        Uint8 tmp, unused;
        if (!dmf_read_byte(fp, &tmp) ||
            !dmf_read_byte(fp, &width) ||
            !dmf_read_byte(fp, &height) ||
            !dmf_read_byte(fp, &unused))
            return DMERR_FREAD;

        nglyphs = tmp;
        maxglyph = 256;
        bpp = 8;

        // Very old TSFONTs have some extra data that is not used
        // .. can't actually even remember what it was for.
        if (version == 0x0200)
        {
            for (int i = 0; i < 32; i++)
            {
                if (!dmf_read_byte(fp, &unused))
                    return DMERR_FREAD;
            }
        }
    }
    else
    {
        // DMFONT has Uint16 values for nglyphs and maxglyph, plus BPP
        if (!dmf_read_le16(fp, &nglyphs) ||
            !dmf_read_le16(fp, &maxglyph) ||
            !dmf_read_byte(fp, &width) ||
            !dmf_read_byte(fp, &height) ||
            !dmf_read_byte(fp, &bpp))
            return DMERR_FREAD;
    }

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

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

    if (bpp != 8 && bpp != 32)
        return DMERR_NOT_SUPPORTED;

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

    // Setup palette for 8bpp fonts
    if (bpp == 8)
    {
        SDL_Color pal[DMFONT_NPALETTE];
        for (int n = 0; n < DMFONT_NPALETTE; n++)
        {
            pal[n].r = pal[n].g = pal[n].b = n > 0 ? 255 : 0;
            pal[n].a = n > 0 ? 255 : 0;
        }
        dmSetBitmapFontPalette(font, pal, 0, DMFONT_NPALETTE);
    }

    // Read glyph data, if any
    for (int i = 0; i < nglyphs; i++)
    {
        DMBitmapGlyph *glyph;
        Uint8 gwidth, gheight;
        Uint16 gindex;
        Uint8 *pixels;

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

        // Read dimensions
        if (!dmf_read_byte(fp, &gwidth) ||
            !dmf_read_byte(fp, &gheight))
            return DMERR_FREAD;

#ifdef FN_DEBUG
        fprintf(stderr, "#%d @ %d - %d x %d\n", i, gindex, gwidth, gheight);
#endif

        // Check the glyph data
        if (gwidth < DMFONT_MIN_WIDTH ||
            gheight < DMFONT_MIN_HEIGHT ||
            gwidth > DMFONT_MAX_WIDTH ||
            gheight > DMFONT_MAX_HEIGHT ||
            gwidth > width ||
            gheight > height ||
            gindex >= maxglyph)
            return DMERR_INVALID_DATA;

        // Set glyph data
        glyph = &font->glyphMap[gindex];
        glyph->width  = gwidth;
        glyph->height = gheight;
        glyph->index  = i;

        // Read pixel data
        pixels = font->glyphs->pixels + (i * font->gsize);
        for (int y = 0; y < glyph->height; y++)
        {
            if (dmfread(pixels, font->glyphs->format->BytesPerPixel,
                glyph->width, fp) != (size_t) glyph->width)
                return DMERR_FREAD;

            pixels += font->glyphs->pitch;
        }
    }

    return DMERR_OK;
}