view tools/lib64gfx.h @ 2265:48b48251610a

Refactor how the image "mode/type" is handled. It is still not perfect for our purposes, but better now.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 17 Jun 2019 02:03:35 +0300
parents adb0480f6ebd
children ecc17b2e129e
line wrap: on
line source

/*
 * Functions for reading and converting various restricted
 * C64/etc and/or indexed/paletted graphics formats.
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2012-2019 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
#ifndef LIB64GFX_H
#define LIB64GFX_H 1

#include "libgfx.h"
#include "dmgrowbuf.h"

#ifdef __cplusplus
extern "C" {
#endif


// Max defines
#define D64_MAX_EXTRA_DATA     16
#define D64_MAX_EXTRA_INFO     64


// Bitmap constants
#define D64_SCR_WIDTH          320
#define D64_SCR_HEIGHT         200
#define D64_SCR_CH_WIDTH       (D64_SCR_WIDTH/8)
#define D64_SCR_CH_HEIGHT      (D64_SCR_HEIGHT/8)


// C64 video screen pixel aspect ratio on PAL
#define D64_SCR_PAR_XY         (0.9365f)

// Sprite constants
#define D64_SPR_WIDTH_PX       24
#define D64_SPR_HEIGHT_PX      21
#define D64_SPR_WIDTH_UT       (D64_SPR_WIDTH_PX / 8) // bytes
#define D64_SPR_HEIGHT_UT      D64_SPR_HEIGHT_PX
#define D64_SPR_SIZE           ((D64_SPR_WIDTH_UT * D64_SPR_HEIGHT_UT) + 1)

// Character constants
#define D64_CHR_WIDTH_PX       8
#define D64_CHR_HEIGHT_PX      8
#define D64_CHR_WIDTH_UT       1 // bytes
#define D64_CHR_HEIGHT_UT      8 // lines
#define D64_CHR_SIZE           (D64_CHR_WIDTH_UT * D64_CHR_HEIGHT_UT)

// Etc.
#define D64_NCOLORS            16
#define D64_VIDBANK_SIZE       (16*1024)
#define D64_MAX_SPRITES        1024
#define D64_MAX_CHARS          256


// Different supported flags/modes
enum
{
    D64_FMT_HIRES        = 0x0001, // Hi-res
    D64_FMT_MC           = 0x0002, // MultiColor
    D64_FMT_ECM          = 0x0003, // ECM mode (applies only to character mode)
    D64_FMT_MODE_MASK    = 0x000f,

    D64_FMT_ILACE        = 0x0010, // Interlace
    D64_FMT_FLI          = 0x0020, // FLI
    D64_FMT_CHAR         = 0x0040, // Character mode
    D64_FMT_BORDER       = 0x0080, // Uses border area
};


// Different types of interlace
enum
{
    D64_ILACE_NONE       = 0x0000, // Not set (invalid for interlaced images)
    D64_ILACE_RES        = 0x0001, // Interlace doubles resolution
    D64_ILACE_COLOR      = 0x0002, // Uses color mixing
};


// Different types of interlace
enum
{
    D64_FLI_8            = 8,
};


// Charmode screen memory configuration
enum
{
    D64_CHCFG_SCREEN,      // Use screen memory
    D64_CHCFG_LINEAR = 1,  // Generate linear pattern so that first line
                           // of chars is 0-39, second 40-79, up to 255.
};


// Various extra settings (see DMC64Image::extraInfo[])
enum
{
    D64_EI_MODE = 0,       // Actual mode, if the format supports several

    D64_EI_CHAR_CASE,
    D64_EI_CHAR_CUSTOM,

    D64_EI_FLI_TYPE,
    D64_EI_ILACE_TYPE,
};


// Image <-> bitmap conversion dithering
enum
{
    D64_DITH_NONE = 0,     // No dithering
};


// Different enc/dec operation types (op->type)
enum
{
    DO_COPY,               // Perform copy between memory (from/to offset) and subject
    DO_SET_MEM,            // Set subject value or fill subject memory area to byte value from offset
    DO_SET_OP,             // Like above but value from op->offs field instead

    DO_SET_MEM_HI,         // Copy one byte value like DO_SET_MEM, but high nibble only
    DO_SET_MEM_LO,         // Like above, but low nibble

    DO_FUNC,               // Execute specified decoding / encoding function

    DO_CHAR_CFG,           // Set character mode configuration

    DO_LAST,               // Last opcode, end processing
};


// "Subjects" of operations (op->subject)
enum
{
    DS_COLOR_RAM,
    DS_BITMAP_RAM,
    DS_SCREEN_RAM,
    DS_EXTRA_DATA,
    DS_CHAR_DATA,

    DS_D020,
    DS_BGCOL,
    DS_D021,
    DS_D022,
    DS_D023,
    DS_D024,

    DS_EXTRA_INFO,

    DS_LAST
};


// Operation flags (op->flags)
enum
{
    DF_NORMAL	= 0xffff,
    DF_DECODE	= 0x0001,
    DF_ENCODE	= 0x0002,
};


typedef struct
{
    char *name;
    char *desc;
    DMColor colors[D64_NCOLORS];
} DMC64Palette;


typedef struct
{
    int dither;      // Dither mode (D64_DITH_*)
    BOOL aspect;     // Correct pixel aspect ratio?

    DMPalette *pal;     // Use this palette
    DMC64Palette *cpal; // If ^pal == NULL, use this C64 palette
} DMC64ImageConvSpec;


typedef struct
{
    Uint8 *data;
    size_t size;
} DMC64MemBlock;


typedef struct
{
    BOOL multicolor, xexpand, yexpand;
    int color, xc, yc;
    Uint8 data[D64_SPR_HEIGHT_UT][D64_SPR_WIDTH_UT];
} DMC64Sprite;


typedef struct _DMC64ImageCommonFormat DMC64ImageCommonFormat;
typedef struct _DMC64ImageFormat DMC64ImageFormat;


typedef struct _DMC64Image
{
    DMC64ImageCommonFormat *fmt;
    int nblocks; // Number of internal blocks used

    // Bitmaps, color RAM, screen, etc. blocks * nblocks
    // Not all of them may be allocated
    DMC64MemBlock
        *color,
        *bitmap,
        *screen,
        *charData;

    // Other standard colours
    Uint8 d020, bgcolor, d022, d023, d024;

    // Extra data areas used by some formats,
    // for example raster colours might be stored
    DMC64MemBlock extraData[D64_MAX_EXTRA_DATA];

    // Extra information / settings used by some formats,
    // for example some PETSCII-type formats.
    // See D64_EI_* constants.
    int extraInfo[D64_MAX_EXTRA_INFO];

    //DMC64Sprite sprites[D64_MAX_SPRITES];
} DMC64Image;


typedef struct _DMC64EncDecOp
{
    int    type;     // Operation type (DO_*)
    int    subject;  // Operation "subject" (DS_*)

    int    offs;     // Offset in "memory"
    int    bank;     // Bank number or extradata index
    size_t size;     // Size of data (0 for "default")
    size_t offs2;    // Offset in data-block

    int (*decFunction)(const struct _DMC64EncDecOp *op, DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageCommonFormat *fmt);
    int (*encFunction)(const struct _DMC64EncDecOp *op, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageCommonFormat *fmt);

    int   flags;     // Operation flags (DF_*)
} DMC64EncDecOp;


#define D64_MAX_ENCDEC_OPS 32
typedef DMC64EncDecOp DMC64EncDecOpList[D64_MAX_ENCDEC_OPS];


typedef int (*DMC64GetPixelFunc)(Uint8 *col, const DMC64Image *img, const int rasterX, const int rasterY);


typedef struct _DMC64ImageCommonFormat
{
    int  mode,               // Format mode/type flags, see D64_FMT_*
         width, height,      // Width and height in pixels
         chWidth, chHeight,  // Width and height in charblocks
         aspectX, aspectY;   // Pixel aspectX/Y

    int  (*convertFrom)(DMImage *, const DMC64Image *, const DMC64ImageConvSpec *spec);
    int  (*convertTo)(DMC64Image *, const DMImage *, const DMC64ImageFormat *fmt, const DMC64ImageConvSpec *spec);

    DMC64GetPixelFunc getPixel;

    DMC64EncDecOpList encdecOps;
} DMC64ImageCommonFormat;


typedef struct _DMC64ImageFormat
{
    char *fext;  // Filename extension
    char *name;  // Format description/name

    size_t addr; // Loading address (0 if no loading address)
    size_t size; // Size, including loading address. Only used in probing and encoding.

    int  flags;  // DM_FMT_* flags (read/write/broken/etc.), see libgfx.h

    int  (*probe)(const DMGrowBuf *buf, const DMC64ImageFormat *fmt);

    int  (*decode)(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt);
    int  (*encode)(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt);

    DMC64ImageCommonFormat formatDef, *format;
} DMC64ImageFormat;


//
// Compression types
//
enum
{
    DM_COMP_RLE_MARKER      = 1, // RLE with a separate marker byte
    DM_COMP_RLE_MASK        = 2, // RLE that has marker bits and lower part acts as run length
};


// Compression flags
enum
{
    DM_RLE_BYTE_RUNS        = 0x0001, // Uses one-byte run lengths
    DM_RLE_WORD_RUNS        = 0x0002, // Uses two-byte (word) run lengths
    DM_RLE_RUNS_MASK        = 0x000f,

    DM_RLE_ORDER_1          = 0x0010, // Order: <marker>, <count/run length>, <data>
    DM_RLE_ORDER_2          = 0x0020, // Order: <marker>, <data>, <count/run length>
    DM_RLE_ORDER_MASK       = 0x00f0,

    DM_RLE_BACKWARDS_INPUT  = 0x0100, // Input is read backwards
    DM_RLE_BACKWARDS_OUTPUT = 0x0200, // Output is written backwards
    DM_RLE_ZERO_COUNT_MAX   = 0x0400, // Zero "count" / run length value is max run (2^8, 2^16)

    DM_OUT_CROP_NONE        = 0x0000,
    DM_OUT_CROP_START       = 0x1000,
    DM_OUT_CROP_END         = 0x2000,
    DM_OUT_CROP_MASK        = 0xf000,
};


typedef struct
{
    char *func;
    size_t cropOutLen;
    int type;                // DM_COMP_*
    int flags;               //

    Uint8
        // DM_COMP_RLE_MARKER mode
        rleMarkerB,          // Marker byte for byte length runs (if DM_RLE_BYTE_RUNS used)
        rleMarkerW,          // Marker byte for word length runs (if DM_RLE_WORD_RUNS used)

        // DM_COMP_RLE_MASK mode
        rleMarkerBits,
        rleMarkerMask,       // Mask bits for marker: data & rleMarkerMask == rleMarkerBits
        rleCountMask;        // Mask bits for length: count = data & rleCountMask

    unsigned int
        // Minimum and maximum run lengths
        rleMinCountB,
        rleMinCountW,
        rleMaxCountB,
        rleMaxCountW;
} DMCompParams;


//
// Global variables
//
extern DMC64Palette      dmC64DefaultPalettes[];
extern const int         ndmC64DefaultPalettes;
extern DMC64ImageFormat  dmC64ImageFormats[];
extern const int         ndmC64ImageFormats;
extern DMC64ImageFormat  **dmC64ImageFormatsSorted;


//
// Library init/close
//
int       dmLib64GFXInit(void);
void      dmLib64GFXClose(void);


//
// Miscellaneous functions
//
int       dmC64ProbeBMP(const DMGrowBuf *buf, const DMC64ImageFormat **fmt);

int       dmC64PaletteFromC64Palette(DMPalette **ppal, const DMC64Palette *cpal, const BOOL mixed);
int       dmC64SetImagePalette(DMImage *img, const DMC64ImageConvSpec *spec, const BOOL mixed);

BOOL      dmCompareAddr16(const DMGrowBuf *buf, const size_t offs, const Uint16 addr);

int       dmC64MemBlockAlloc(DMC64MemBlock *blk, const size_t size);
int       dmC64MemBlockCopy(DMC64MemBlock *dst, const DMC64MemBlock *src);
void      dmC64MemBlockFree(DMC64MemBlock *blk);

// C64 bitmap image allocation/freeing
DMC64Image *dmC64ImageAlloc(const DMC64ImageFormat *fmt);
void      dmC64ImageFree(DMC64Image *img);


// Encoding and decoding of formats and images
int       dmC64ConvertCSDataToImage(DMImage *img, int xoffs, int yoffs, const Uint8 *inBuf, int width, int height, BOOL multicolor, int *colors);

int       dmC64ImageGetNumBlocks(const DMC64ImageFormat *fmt);

const char *dmC64GetOpSubjectName(const int subject);
const DMC64MemBlock * dmC64GetOpMemBlock(const DMC64Image *img, const int subject, const int bank);
int       dmC64SanityCheckEncDecOp(const int i, const DMC64EncDecOp *op, const DMC64Image *img);
int      dmC64MemBlockAllocSubj(DMC64Image *img, const int subject, const int bank);

size_t    dmC64GetSubjectSize(const int subject, const DMC64ImageCommonFormat *fmt);
size_t    dmC64GetOpSubjectSize(const DMC64EncDecOp *op, const DMC64ImageCommonFormat *fmt);


int       dmC64DecodeGenericBMP(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt);
int       dmC64EncodeGenericBMP(const BOOL allocate, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt);

int       dmC64ConvertBMP2Image(DMImage **pdst, const DMC64Image *src, const DMC64ImageConvSpec *spec);
int       dmC64ConvertImage2BMP(DMC64Image **pdst, const DMImage *src, const DMC64ImageFormat *fmt, const DMC64ImageConvSpec *spec);

int       dmC64DecodeBMP(DMC64Image **img, const DMGrowBuf *buf, const size_t probeOffs, const size_t loadOffs, const DMC64ImageFormat **fmt, const DMC64ImageFormat *forced);
int       dmC64EncodeBMP(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt);

int       dmC64ConvertGenericBMP2Image(DMImage *dst, const DMC64Image *src, const DMC64ImageConvSpec *spec);
int       dmC64ConvertGenericImage2BMP(DMC64Image *dst, const DMImage *src, const DMC64ImageFormat *fmt, const DMC64ImageConvSpec *spec);

void      dmGenericRLEAnalyze(const DMGrowBuf *buf, DMCompParams *cfg);

void      dmSetupRLEBuffers(DMGrowBuf *dst, DMGrowBuf *src, const DMCompParams *cfg);
void      dmFinishRLEBuffers(DMGrowBuf *dst, DMGrowBuf *src, const DMCompParams *cfg);

int       dmGenericRLEOutputRun(DMGrowBuf *dst, const DMCompParams *cfg, const Uint8 data, const unsigned int count);
int       dmEncodeGenericRLESequence(DMGrowBuf *dst, const Uint8 data, const unsigned int count, const DMCompParams *cfg);

int       dmDecodeGenericRLE(DMGrowBuf *dst, const DMGrowBuf *src, const DMCompParams *cfg);
int       dmDecodeGenericRLEAlloc(DMGrowBuf *dst, const DMGrowBuf *src, const DMCompParams *cfg);

int       dmEncodeGenericRLE(DMGrowBuf *dst, const DMGrowBuf *src, const DMCompParams *cfg);
int       dmEncodeGenericRLEAlloc(DMGrowBuf *dst, const DMGrowBuf *src, const DMCompParams *cfg);


//
// Macros for defining variables used in getpixel functions
//
#define DM_C64_GENERIC_SC_PIXEL_DEFS(ximg) \
    const int \
        x = rasterX / 8, \
        y = rasterY / 8, \
        yoffs = y * ximg->fmt->chWidth, \
        bmoffs = yoffs * 8 + (rasterY & 7) + (x * 8), \
        scroffs = yoffs + x, \
        vshift = 7 - (rasterX & 7);


#define DM_C64_GENERIC_MC_PIXEL_DEFS(ximg) \
    const int \
        x = rasterX / 4, \
        y = rasterY / 8, \
        yoffs = y * (ximg)->fmt->chWidth, \
        bmoffs = yoffs * 8 + (rasterY & 7) + (x * 8), \
        scroffs = yoffs + x, \
        vshift = 6 - ((rasterX * 2) & 6);


#define DM_C64_GENERIC_CHAR_PIXEL(ximg) \
    const int \
        x = rasterX / 8, \
        y = rasterY / 8, \
        scroffs = y * (ximg)->fmt->chWidth + x; \


//
// Inline helper functions for pixel format decoding
//
static inline int dmC64GetGenericSCPixel(Uint8 *col,
    const DMC64Image *img, const int bmoffs, const int scroffs,
    const int vshift, const int vbank, const int bitmap)
{
    if ((img->bitmap[bitmap].data[bmoffs] >> vshift) & 1)
        *col = img->screen[vbank].data[scroffs] >> 4;
    else
        *col = img->screen[vbank].data[scroffs] & 15;

    return DMERR_OK;
}


static inline int dmC64GetGenericMCPixel(Uint8 *col,
    const DMC64Image *img, const int bmoffs, const int scroffs,
    const int vshift, const int vbank, const int bitmap,
    const int cbank, const int bgcolor)
{
    switch ((img->bitmap[bitmap].data[bmoffs] >> vshift) & 3)
    {
        case  0: *col = bgcolor & 15; break;
        case  1: *col = img->screen[vbank].data[scroffs] >> 4; break;
        case  2: *col = img->screen[vbank].data[scroffs] & 15; break;
        default: *col = img->color[cbank].data[scroffs] & 15; break;
    }

    return DMERR_OK;
}


static inline int dmC64GetGenericCharSCPixel(Uint8 *col,
    const DMC64Image *img, const int scroffs, const int rasterX,
    const int chrbank, const size_t chroffs, const int chr,
    const int cbank, const int bgcolor)
{
    if (chroffs >= img->charData[chrbank].size)
    {
        return dmError(DMERR_INVALID_DATA,
            "Character map index #%d out of bounds for char ROM data.\n",
            chr);
    }

    const int vshift = 7 - (rasterX & 7);
    if ((img->charData[chrbank].data[chroffs] >> vshift) & 1)
        *col = img->color[cbank].data[scroffs] & 15;
    else
        *col = bgcolor & 15;

    return DMERR_OK;
}


static inline int dmC64GetGenericCharMCPixel(Uint8 *col,
    const DMC64Image *img, const int scroffs, const int rasterX,
    const int chrbank, const size_t chroffs, const int chr,
    const int cbank, const int bgcolor,
    const int bgd022, const int bgd023)
{
    if (chroffs >= img->charData[chrbank].size)
    {
        return dmError(DMERR_INVALID_DATA,
            "Character map index #%d out of bounds for char ROM data.\n",
            chr);
    }

    const int ccol = img->color[cbank].data[scroffs];
    if (ccol & 8)
    {
        const int vshift = 6 - (rasterX & 6);
        switch ((img->charData[chrbank].data[chroffs] >> vshift) & 3)
        {
            case 0: *col = bgcolor & 15; break;
            case 1: *col = bgd022 & 15; break;
            case 2: *col = bgd023 & 15; break;
            case 3: *col = ccol & 7;
        }
    }
    else
    {
        const int vshift = 7 - (rasterX & 7);
        if ((img->charData[chrbank].data[chroffs] >> vshift) & 1)
            *col = ccol & 7;
        else
            *col = bgcolor & 15;
    }

    return DMERR_OK;
}


static inline int dmC64GetGenericCharECMPixel(Uint8 *col,
    const DMC64Image *img, const int scroffs, const int rasterX,
    const int chrbank, const size_t chroffs, const int chr,
    const int cbank, const int bgcolor,
    const int bgd022, const int bgd023, const int bgd024)
{
    if (chroffs >= img->charData[0].size)
    {
        return dmError(DMERR_INVALID_DATA,
            "Character map index #%d out of bounds for char ROM data.\n",
            chr);
    }

    const int vshift = 7 - (rasterX & 7);
    if ((img->charData[chrbank].data[chroffs] >> vshift) & 1)
        *col = img->color[cbank].data[scroffs] & 15;
    else
    switch ((chr >> 6) & 3)
    {
        case 0: *col = bgcolor & 15; break;
        case 1: *col = bgd022 & 15; break;
        case 2: *col = bgd023 & 15; break;
        case 3: *col = bgd024 & 15; break;
    }

    return DMERR_OK;
}


static inline const DMC64EncDecOp * fmtGetEncDecOp(const DMC64ImageFormat *fmt, const int index)
{
    return &fmt->format->encdecOps[index];
}


#ifdef __cplusplus
}
#endif

#endif // LIB64GFX_H