view tools/64vw.c @ 1931:410679d2fe8a

"Enable" the image->c64 bitmap conversion path in gfxconv. It does not work without the necessary bits elsewhere, though. Also add DMC64ImageConvSpec structure for delivering conversion parameters, though it is not yet used either.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 28 Jun 2018 17:26:30 +0300
parents cc4570c0bca5
children 8896d5676f1b
line wrap: on
line source

/*
 * 64vw - Display some C64 etc graphics formats via libSDL
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2012-2018 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
#include "dmlib.h"
#include "dmargs.h"
#include "dmfile.h"
#include "lib64gfx.h"
#include <SDL.h>


#define SET_SKIP_AMOUNT 10


int     optVFlags = 0;
int     optScrWidth, optScrHeight;
int     optForcedFormat = -1;
BOOL    optInfoOnly  = FALSE,
        optProbeOnly = FALSE,
        optListOnly  = FALSE;
size_t  noptFilenames1 = 0, noptFilenames2 = 0;
char    **optFilenames = NULL;


static const DMOptArg optList[] =
{
    { 0, '?', "help",       "Show this help", OPT_NONE },
    { 1, 'v', "verbose",    "Be more verbose", OPT_NONE },
    { 2,   0, "fs",         "Fullscreen", OPT_NONE },
    { 3, 'S', "scale",      "Scale image by factor (1-10)", OPT_ARGREQ },
    { 4, 'f', "format",     "Force input format (see --formats)", OPT_ARGREQ },
    { 5, 'F', "formats",    "List supported input formats", OPT_NONE },
    { 6, 'i', "info",       "Print information only (no display)", OPT_NONE },
    { 7, 'l', "list",       "Output list of files that were recognized (implies -i)", OPT_NONE },
    { 8, 'p', "probe",      "Probe only (do not attempt to decode the image)", OPT_NONE },
};

const int optListN = sizeof(optList) / sizeof(optList[0]);


void dmSetScaleFactor(float factor)
{
    optScrWidth = (int) ((float) C64_SCR_WIDTH * factor * C64_SCR_PAR_XY);
    optScrHeight = (int) ((float) C64_SCR_HEIGHT * factor);
}


void argShowFormats()
{
    printf(
    "Available C64 bitmap formats (-f <frmt>):\n"
    " frmt | Type            | Description\n"
    "------+-----------------+-------------------------------------\n"
    );
    for (int i = 0; i < ndmC64ImageFormats; i++)
    {
        const DMC64ImageFormat *fmt = &dmC64ImageFormats[i];
        char buf[64];
        printf("%-6s| %-15s | %s%s\n",
            fmt->fext,
            dmC64GetImageTypeString(buf, sizeof(buf), fmt->format->type, FALSE),
            fmt->name,
            fmt->flags & DM_FMT_BROKEN ? " [BROKEN]" : "");
    }
    printf("%d formats supported.\n", ndmC64ImageFormats);
}


void argShowHelp()
{
    dmPrintBanner(stdout, dmProgName, "[options] <input image file>");
    dmArgsPrintHelp(stdout, optList, optListN, 0);
}


BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
{
    switch (optN)
    {
        case 0:
            argShowHelp();
            exit(0);
            break;

        case 1:
            dmVerbosity++;
            break;

        case 2:
            optVFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
            break;

        case 3:
            {
                float factor;
                if (sscanf(optArg, "%f", &factor) == 1)
                {
                    if (factor < 1 || factor >= 10)
                    {
                        dmErrorMsg("Invalid scale factor %1.0f, see help for valid values.\n", factor);
                        return FALSE;
                    }

                    dmSetScaleFactor(factor);
                }
                else
                {
                    dmErrorMsg("Invalid scale factor '%s'.\n", optArg);
                    return FALSE;
                }
            }
            break;

        case 4:
            optForcedFormat = -1;
            for (int i = 0; i < ndmC64ImageFormats; i++)
            {
                const DMC64ImageFormat *fmt = &dmC64ImageFormats[i];
                if (fmt->fext != NULL &&
                    strcasecmp(optArg, fmt->fext) == 0)
                {
                    optForcedFormat = i;
                    break;
                }
            }

            if (optForcedFormat < 0)
            {
                dmErrorMsg("Invalid image format argument '%s'.\n", optArg);
                return FALSE;
            }
            break;

        case 5:
            argShowFormats();
            exit(0);
            break;

        case 7:
            optListOnly = TRUE;
            // Fallthrough

        case 6:
            if (dmVerbosity < 1)
                dmVerbosity = 1;
            optInfoOnly = TRUE;
            break;

        case 8:
            if (dmVerbosity < 1)
                dmVerbosity = 1;
            optProbeOnly = TRUE;
            break;

        default:
            dmErrorMsg("Unknown option '%s'.\n", currArg);
            return FALSE;
    }

    return TRUE;
}


BOOL argHandleFile1(char *filename)
{
    (void) filename;

    noptFilenames1++;
    return TRUE;
}


BOOL argHandleFile2(char *filename)
{
    if (noptFilenames2 < noptFilenames1)
    {
        optFilenames[noptFilenames2++] = filename;
        return TRUE;
    }
    else
        return FALSE;
}


int dmReadC64Image(const char *filename, const DMC64ImageFormat *forced, const DMC64ImageFormat **fmt, DMC64Image **cimage)
{
    Uint8 *dataBuf = NULL;
    size_t dataSize;
    DMGrowBuf tmp;
    int ret;

    if ((ret = dmReadDataFile(NULL, filename, &dataBuf, &dataSize)) != DMERR_OK)
        goto exit;

    dmGrowBufConstCreateFrom(&tmp, dataBuf, dataSize);

    if (optProbeOnly)
        ret = dmC64ProbeBMP(&tmp, fmt) != DM_PROBE_SCORE_FALSE ? DMERR_OK : DMERR_NOT_SUPPORTED;
    else
        ret = dmC64DecodeBMP(cimage, &tmp, 0, 2, fmt, forced);

exit:
    dmFree(dataBuf);
    return ret;
}


int dmDecodeC64Image(const DMC64Image *cimage, const DMC64ImageFormat *fmt,
    SDL_Surface *surf, const DMC64ImageConvSpec *spec)
{
    DMImage bmap;
    int ret;

    memset(&bmap, 0, sizeof(bmap));
    bmap.size     = surf->pitch * surf->h;
    bmap.data     = surf->pixels;
    bmap.pitch    = surf->pitch;
    bmap.width    = surf->w;
    bmap.height   = surf->h;
    bmap.ncolors  = C64_NCOLORS;
    bmap.constpal = TRUE;
    bmap.pal      = dmDefaultC64Palette;

    if (fmt->format->convertFrom != NULL)
        ret = fmt->format->convertFrom(&bmap, cimage, fmt, spec);
    else
        ret = dmC64ConvertGenericBMP2Image(&bmap, cimage, fmt, spec);

    SDL_SetPaletteColors(surf->format->palette, (SDL_Color *) bmap.pal, 0, bmap.ncolors);

    return ret;
}


int main(int argc, char *argv[])
{
    const DMC64ImageFormat *forced;
    DMC64ImageConvSpec spec;
    SDL_Window *window = NULL;
    SDL_Renderer *renderer = NULL;
    SDL_Texture *texture = NULL;
    SDL_Surface *surf = NULL;
    BOOL initSDL = FALSE, exitFlag, needRedraw;
    size_t currIndex, prevIndex;
    int ret;

    dmC64InitializeFormats();
    dmSetScaleFactor(2.0);
    memset(&spec, 0, sizeof(spec));

    dmInitProg("64vw", "Display some C64 bitmap graphics formats", "0.4", NULL, NULL);

    // Parse arguments, round #1
    if (!dmArgsProcess(argc, argv, optList, optListN,
        argHandleOpt, argHandleFile1, OPTH_BAILOUT))
        exit(1);

    if (noptFilenames1 == 0)
    {
        dmErrorMsg("No input file(s) specified, perhaps you need some --help\n");
        goto exit;
    }

    // Allocate space for filename pointers
    if ((optFilenames = dmCalloc(noptFilenames1, sizeof(char *))) == NULL)
    {
        dmErrorMsg("Could not allocate memory for input file list.\n");
        goto exit;
    }

    // Assign the filename pointers
    if (!dmArgsProcess(argc, argv, optList, optListN,
        NULL, argHandleFile2, OPTH_BAILOUT | OPTH_ONLY_OTHER))
        goto exit;

    // Check for forced input format
    if (optForcedFormat >= 0)
    {
        forced = &dmC64ImageFormats[optForcedFormat];
        dmMsg(0, "Forced %s format image, type %d, %s\n",
            forced->name, forced->format->type, forced->fext);
    }
    else
        forced = NULL;

    // If we are simply displaying file information, no need to initialize SDL etc
    if (optInfoOnly || optProbeOnly)
    {
        for (size_t n = 0; n < noptFilenames2; n++)
        {
            char *filename = optFilenames[n];
            const DMC64ImageFormat *fmt = NULL;
            DMC64Image *cimage = NULL;

            if ((ret = dmReadC64Image(filename, forced, &fmt, &cimage)) != DMERR_OK)
            {
                if (!optListOnly)
                {
                    dmErrorMsg("Could not read image file '%s', %d: %s\n",
                        filename, ret, dmErrorStr(ret));
                }
            }
            else
            if (optListOnly)
            {
                fprintf(stdout, "%s\n", filename);
            }
            else
            {
                fprintf(stdout, "\n%s\n", filename);
                dmC64ImageDump(stdout, cimage, fmt, "  ");
            }

            dmC64ImageFree(cimage);
        }
        goto exit;
    }

    // Initialize libSDL
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
    {
        dmErrorMsg("Could not initialize SDL: %s\n", SDL_GetError());
        goto exit;
    }
    initSDL = TRUE;

    // Open window
    if ((window = SDL_CreateWindow(dmProgName,
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        optScrWidth, optScrHeight,
        optVFlags | SDL_WINDOW_RESIZABLE
        //| SDL_WINDOW_HIDDEN
        )) == NULL)
    {
        dmErrorMsg("Can't create an SDL window: %s\n", SDL_GetError());
        goto exit;
    }

    if ((renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC)) == NULL)
    {
        dmErrorMsg("Can't create an SDL renderer: %s\n", SDL_GetError());
        goto exit;
    }

//    SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");

    // Start main loop
    currIndex = 0;
    prevIndex = 1;
    needRedraw = TRUE;
    exitFlag = FALSE;
    while (!exitFlag)
    {
        SDL_Event event;
        while (SDL_PollEvent(&event))
        switch (event.type)
        {
            case SDL_KEYDOWN:
                switch (event.key.keysym.sym)
                {
                    case SDLK_ESCAPE:
                    case SDLK_q:
                        exitFlag = TRUE;
                        break;

                    case SDLK_DOWN:
                    case SDLK_LEFT:
                        if (currIndex > 0)
                            currIndex--;
                        else
                            currIndex = 0;
                        break;

                    case SDLK_UP:
                    case SDLK_RIGHT:
                        if (currIndex < noptFilenames2 - 1)
                            currIndex++;
                        else
                            currIndex = noptFilenames2 - 1;
                        break;

                    case SDLK_PAGEDOWN:
                        if (currIndex > SET_SKIP_AMOUNT)
                            currIndex -= SET_SKIP_AMOUNT;
                        else
                            currIndex = 0;
                        break;

                    case SDLK_PAGEUP:
                        if (currIndex < noptFilenames2 - 1 - SET_SKIP_AMOUNT)
                            currIndex += SET_SKIP_AMOUNT;
                        else
                            currIndex = noptFilenames2 - 1;
                        break;

                    case SDLK_HOME:
                        currIndex = 0;
                        break;

                    case SDLK_END:
                        currIndex = noptFilenames2 - 1;
                        break;

                    case SDLK_f:
                        optVFlags ^= SDL_WINDOW_FULLSCREEN_DESKTOP;
                        if (SDL_SetWindowFullscreen(window, optVFlags) != 0)
                            goto exit;
                        break;

                    default:
                        break;
                }

                needRedraw = TRUE;
                break;

            case SDL_WINDOWEVENT:
                switch (event.window.event)
                {
                    case SDL_WINDOWEVENT_EXPOSED:
                        needRedraw = TRUE;
                        break;

                    case SDL_WINDOWEVENT_RESIZED:
                        optScrWidth  = event.window.data1;
                        optScrHeight = event.window.data2;

                        needRedraw = TRUE;
                        break;
                }
                break;

            case SDL_QUIT:
                goto exit;
        }

        if (currIndex != prevIndex)
        {
            char *filename = optFilenames[currIndex];
            const DMC64ImageFormat *fmt = NULL;
            DMC64Image *cimage = NULL;
            char *title = NULL;

            if (surf != NULL)
            {
                SDL_FreeSurface(surf);
                surf = NULL;
            }

            if ((ret = dmReadC64Image(filename, forced, &fmt, &cimage)) != DMERR_OK)
            {
                dmErrorMsg("Failed to decode bitmap data %d: %s\n", ret, dmErrorStr(ret));
                goto fail;
            }

            if (fmt == NULL || cimage == NULL)
            {
                dmErrorMsg("Probing could not find any matching image format. Perhaps try forcing a format via -f.\n");
                goto fail;
            }

            // Create surface (we are lazy and ugly)
            if ((surf = SDL_CreateRGBSurfaceWithFormat(0,
                cimage->width, cimage->height, 8, SDL_PIXELFORMAT_INDEX8)) == NULL)
            {
                dmC64ImageFree(cimage);
                dmErrorMsg("Could not allocate surface.\n");
                goto exit;
            }

            if (dmDecodeC64Image(cimage, fmt, surf, &spec) == DMERR_OK)
            {
                title = dm_strdup_printf("%s - [%d / %d] %s (%dx%d @ %s)",
                    dmProgName,
                    currIndex + 1,
                    noptFilenames2,
                    filename,
                    cimage->width, cimage->height,
                    fmt->name);

                if (dmVerbosity >= 1)
                {
                    fprintf(stdout, "\n%s\n", filename);
                    dmC64ImageDump(stdout, cimage, fmt, "  ");
                }
            }

fail:
            dmC64ImageFree(cimage);

            if (surf == NULL && (surf = SDL_CreateRGBSurfaceWithFormat(0,
                C64_SCR_WIDTH, C64_SCR_HEIGHT, 8, SDL_PIXELFORMAT_INDEX8)) == NULL)
            {
                dmErrorMsg("Could not allocate surface.\n");
                goto exit;
            }


            if (texture != NULL)
                SDL_DestroyTexture(texture);

            if ((texture = SDL_CreateTextureFromSurface(renderer, surf)) == NULL)
            {
                dmErrorMsg("Could not create texture from surface: %s\n", SDL_GetError());
                goto exit;
            }

            if (title == NULL)
            {
                title = dm_strdup_printf("%s - %s [%d / %d]",
                    dmProgName, filename,
                    currIndex + 1, noptFilenames2);
            }

            SDL_SetWindowTitle(window, title);
            dmFree(title);

            needRedraw = TRUE;
            prevIndex = currIndex;
        }

        if (needRedraw)
        {
            SDL_RenderClear(renderer);
            SDL_RenderCopy(renderer, texture, NULL, NULL);
            SDL_RenderPresent(renderer);
            needRedraw = FALSE;
        }

        SDL_Delay(50);
    }

exit:
    // Cleanup
    dmFree(optFilenames);

    if (texture != NULL)
        SDL_DestroyTexture(texture);

    if (renderer != NULL)
        SDL_DestroyRenderer(renderer);

    if (window != NULL)
        SDL_DestroyWindow(window);

    if (surf != NULL)
        SDL_FreeSurface(surf);

    if (initSDL)
        SDL_Quit();

    return 0;
}