ref: 22c28f1c0de3a76cac067309abfd77f8d508c2bb
dir: /src/ft2_bmp.c/
// for finding memory leaks in debug mode with Visual Studio
#if defined _DEBUG && defined _MSC_VER
#include <crtdbg.h>
#endif
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "ft2_palette.h"
#include "ft2_gfxdata.h"
#include "ft2_bmp.h"
enum
{
COMP_RGB = 0,
COMP_RLE8 = 1,
COMP_RLE4 = 2
};
typedef struct bmpHeader_t
{
uint32_t bfSizebfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
uint32_t biSize;
int32_t biWidth;
int32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
int32_t biClrUsed;
int32_t biClrImportant;
} bmpHeader_t;
static uint32_t *loadBMPTo32Bit(const uint8_t *src);
static uint8_t *loadBMPTo1Bit(const uint8_t *src);
static uint8_t *loadBMPTo4BitPal(const uint8_t *src);
bmp_t bmp; // globalized
// if you match a color in this table, you have the real palette index (LUT pos)
#define NUM_CUSTOM_PALS 17
static const uint32_t bmpCustomPalette[NUM_CUSTOM_PALS] =
{ // 0xC0FFEE = spacers
0x000000, 0x5397FF, 0x000067, 0x4BFFFF, 0xAB7787,
0xFFFFFF, 0x7F7F7F, 0xC0FFEE, 0x733747, 0xF7CBDB,
0x434343, 0xD3D3D3, 0xFFFF00, 0xC0FFEE, 0xC0FFEE,
0xC0FFEE, 0xFF0000 // last value = loop pin line
};
static int8_t getFT2PalNrFromPixel(uint32_t pixel32)
{
for (int32_t i = 0; i < NUM_CUSTOM_PALS; i++)
{
if (pixel32 == bmpCustomPalette[i])
return (int8_t)i;
}
return PAL_TRANSPR;
}
bool loadBMPs(void)
{
memset(&bmp, 0, sizeof (bmp));
bmp.ft2AboutLogo = loadBMPTo32Bit(ft2AboutLogoBMP);
bmp.font1 = loadBMPTo1Bit(font1BMP);
bmp.font2 = loadBMPTo1Bit(font2BMP);
bmp.font3 = loadBMPTo1Bit(font3BMP);
bmp.font4 = loadBMPTo1Bit(font4BMP);
bmp.font6 = loadBMPTo1Bit(font6BMP);
bmp.font7 = loadBMPTo1Bit(font7BMP);
bmp.font8 = loadBMPTo1Bit(font8BMP);
bmp.ft2LogoBadges = loadBMPTo4BitPal(ft2LogoBadgesBMP);
bmp.ft2ByBadges = loadBMPTo4BitPal(ft2ByBadgesBMP);
bmp.midiLogo = loadBMPTo4BitPal(midiLogoBMP);
bmp.nibblesLogo = loadBMPTo4BitPal(nibblesLogoBMP);
bmp.nibblesStages = loadBMPTo4BitPal(nibblesStagesBMP);
bmp.loopPins = loadBMPTo4BitPal(loopPinsBMP);
bmp.mouseCursors = loadBMPTo4BitPal(mouseCursorsBMP);
bmp.mouseCursorBusyClock = loadBMPTo4BitPal(mouseCursorBusyClockBMP);
bmp.mouseCursorBusyGlass = loadBMPTo4BitPal(mouseCursorBusyGlassBMP);
bmp.whitePianoKeys = loadBMPTo4BitPal(whitePianoKeysBMP);
bmp.blackPianoKeys = loadBMPTo4BitPal(blackPianoKeysBMP);
bmp.vibratoWaveforms = loadBMPTo4BitPal(vibratoWaveformsBMP);
bmp.scopeRec = loadBMPTo4BitPal(scopeRecBMP);
bmp.scopeMute = loadBMPTo4BitPal(scopeMuteBMP);
bmp.radiobuttonGfx = loadBMPTo4BitPal(radiobuttonGfxBMP);
bmp.checkboxGfx = loadBMPTo4BitPal(checkboxGfxBMP);
if (bmp.ft2AboutLogo == NULL || bmp.font1 == NULL || bmp.font2 == NULL ||
bmp.font3 == NULL || bmp.font4 == NULL || bmp.font6 == NULL ||
bmp.font6 == NULL || bmp.font7 == NULL || bmp.font8 == NULL ||
bmp.ft2LogoBadges == NULL || bmp.ft2ByBadges == NULL ||
bmp.midiLogo == NULL || bmp.nibblesLogo == NULL || bmp.nibblesStages == NULL ||
bmp.loopPins == NULL || bmp.mouseCursors == NULL || bmp.mouseCursorBusyClock == NULL ||
bmp.mouseCursorBusyGlass == NULL || bmp.whitePianoKeys == NULL || bmp.blackPianoKeys == NULL ||
bmp.vibratoWaveforms == NULL || bmp.scopeRec == NULL || bmp.scopeMute == NULL ||
bmp.radiobuttonGfx == NULL || bmp.checkboxGfx == NULL)
return false;
return true;
}
void freeBMPs(void)
{
if (bmp.ft2AboutLogo != NULL) { free(bmp.ft2AboutLogo); bmp.ft2AboutLogo = NULL; }
if (bmp.font1 != NULL) { free(bmp.font1); bmp.font1 = NULL; }
if (bmp.font2 != NULL) { free(bmp.font2); bmp.font2 = NULL; }
if (bmp.font3 != NULL) { free(bmp.font3); bmp.font3 = NULL; }
if (bmp.font4 != NULL) { free(bmp.font4); bmp.font4 = NULL; }
if (bmp.font6 != NULL) { free(bmp.font6); bmp.font6 = NULL; }
if (bmp.font7 != NULL) { free(bmp.font7); bmp.font7 = NULL; }
if (bmp.font8 != NULL) { free(bmp.font8); bmp.font8 = NULL; }
if (bmp.ft2LogoBadges != NULL) { free(bmp.ft2LogoBadges); bmp.ft2LogoBadges = NULL; }
if (bmp.ft2ByBadges != NULL) { free(bmp.ft2ByBadges); bmp.ft2ByBadges = NULL; }
if (bmp.midiLogo != NULL) { free(bmp.midiLogo); bmp.midiLogo = NULL; }
if (bmp.nibblesLogo != NULL) { free(bmp.nibblesLogo); bmp.nibblesLogo = NULL; }
if (bmp.nibblesStages != NULL) { free(bmp.nibblesStages); bmp.nibblesStages = NULL; }
if (bmp.loopPins != NULL) { free(bmp.loopPins); bmp.loopPins = NULL; }
if (bmp.mouseCursors != NULL) { free(bmp.mouseCursors); bmp.mouseCursors = NULL; }
if (bmp.mouseCursorBusyClock != NULL) { free(bmp.mouseCursorBusyClock); bmp.mouseCursorBusyClock = NULL; }
if (bmp.mouseCursorBusyGlass != NULL) { free(bmp.mouseCursorBusyGlass); bmp.mouseCursorBusyGlass = NULL; }
if (bmp.whitePianoKeys != NULL) { free(bmp.whitePianoKeys); bmp.whitePianoKeys = NULL; }
if (bmp.blackPianoKeys != NULL) { free(bmp.blackPianoKeys); bmp.blackPianoKeys = NULL; }
if (bmp.vibratoWaveforms != NULL) { free(bmp.vibratoWaveforms); bmp.vibratoWaveforms = NULL; }
if (bmp.scopeRec != NULL) { free(bmp.scopeRec); bmp.scopeRec = NULL; }
if (bmp.scopeMute != NULL) { free(bmp.scopeMute); bmp.scopeMute = NULL; }
if (bmp.radiobuttonGfx != NULL) { free(bmp.radiobuttonGfx); bmp.radiobuttonGfx = NULL; }
if (bmp.checkboxGfx != NULL) { free(bmp.checkboxGfx); bmp.checkboxGfx = NULL; }
}
/* Very basic BMP loaders that supports top-down RLE-packed bitmaps, but only at 4-bit or 8-bit colors.
** This is only meant to be used for BMPs that are carefully crafted for this program!
*/
static uint32_t *loadBMPTo32Bit(const uint8_t *src)
{
const uint8_t *pData, *src8;
int32_t len, byte, palIdx, i, x , y, lineEnd, palEntries, colorsInBitmap;
uint32_t *outData, *dst32, *tmp32, color, color2, pal[256];
bmpHeader_t *hdr;
hdr = (bmpHeader_t *)&src[2];
pData = &src[hdr->bfOffBits];
colorsInBitmap = 1 << hdr->biBitCount;
if (hdr->biCompression == COMP_RGB || hdr->biClrUsed > 256 || colorsInBitmap > 256)
return NULL;
outData = (uint32_t *)malloc(hdr->biWidth * hdr->biHeight * sizeof (uint32_t));
if (outData == NULL)
return NULL;
// pre-fill image with first palette color
palEntries = hdr->biClrUsed == 0 ? colorsInBitmap : hdr->biClrUsed;
memcpy(pal, &src[0x36], palEntries * sizeof (uint32_t));
for (i = 0; i < hdr->biWidth * hdr->biHeight; i++)
outData[i] = pal[0];
lineEnd = hdr->biWidth;
src8 = pData;
dst32 = outData;
x = 0;
y = hdr->biHeight - 1;
while (true)
{
byte = *src8++;
if (byte == 0) // escape control
{
byte = *src8++;
if (byte == 0) // end of line
{
x = 0;
y--;
}
else if (byte == 1) // end of bitmap
{
break;
}
else if (byte == 2) // add to x/y position
{
x += *src8++;
y -= *src8++;
}
else // absolute bytes
{
if (hdr->biCompression == COMP_RLE8)
{
tmp32 = &dst32[(y * hdr->biWidth) + x];
for (i = 0; i < byte; i++)
*tmp32++ = pal[*src8++];
if (byte & 1)
src8++;
x += byte;
}
else
{
len = byte >> 1;
tmp32 = &dst32[y * hdr->biWidth];
for (i = 0; i < len; i++)
{
palIdx = *src8++;
tmp32[x++] = pal[palIdx >> 4];
if (x < lineEnd) tmp32[x++] = pal[palIdx & 0xF];
}
if (((byte + 1) >> 1) & 1)
src8++;
}
}
}
else
{
palIdx = *src8++;
if (hdr->biCompression == COMP_RLE8)
{
color = pal[palIdx];
tmp32 = &dst32[(y * hdr->biWidth) + x];
for (i = 0; i < byte; i++)
*tmp32++ = color;
x += byte;
}
else
{
color = pal[palIdx >> 4];
color2 = pal[palIdx & 0x0F];
len = byte >> 1;
tmp32 = &dst32[y * hdr->biWidth];
for (i = 0; i < len; i++)
{
tmp32[x++] = color;
if (x < lineEnd) tmp32[x++] = color2;
}
}
}
}
return outData;
}
static uint8_t *loadBMPTo1Bit(const uint8_t *src) // supports 4-bit RLE only
{
const uint8_t *pData, *src8;
uint8_t palIdx, color, color2, *outData, *dst8, *tmp8;
int32_t len, byte, i, x , y, lineEnd, colorsInBitmap, palEntries;
uint32_t pal[16];
bmpHeader_t *hdr;
hdr = (bmpHeader_t *)&src[2];
pData = &src[hdr->bfOffBits];
colorsInBitmap = 1 << hdr->biBitCount;
if (hdr->biCompression != COMP_RLE4 || hdr->biClrUsed > 16 || colorsInBitmap > 16)
return NULL;
outData = (uint8_t *)malloc(hdr->biWidth * hdr->biHeight * sizeof (uint8_t));
if (outData == NULL)
return NULL;
palEntries = hdr->biClrUsed == 0 ? colorsInBitmap : hdr->biClrUsed;
memcpy(pal, &src[0x36], palEntries * sizeof (uint32_t));
// pre-fill image with first palette color
color = pal[0] ? 1 : 0;
for (i = 0; i < hdr->biWidth * hdr->biHeight; i++)
outData[i] = color;
lineEnd = hdr->biWidth;
src8 = pData;
dst8 = outData;
x = 0;
y = hdr->biHeight - 1;
while (true)
{
byte = *src8++;
if (byte == 0) // escape control
{
byte = *src8++;
if (byte == 0) // end of line
{
x = 0;
y--;
}
else if (byte == 1) // end of bitmap
{
break;
}
else if (byte == 2) // add to x/y position
{
x += *src8++;
y -= *src8++;
}
else // absolute bytes
{
len = byte >> 1;
tmp8 = &dst8[y * hdr->biWidth];
for (i = 0; i < len; i++)
{
palIdx = *src8++;
tmp8[x++] = pal[palIdx >> 4] ? 1 : 0;
if (x < lineEnd) tmp8[x++] = pal[palIdx & 0xF] ? 1 : 0;
}
if (((byte + 1) >> 1) & 1)
src8++;
}
}
else
{
palIdx = *src8++;
color = pal[palIdx >> 4] ? 1 : 0;
color2 = pal[palIdx & 0x0F] ? 1 : 0;
len = byte >> 1;
tmp8 = &dst8[y * hdr->biWidth];
for (i = 0; i < len; i++)
{
tmp8[x++] = color;
if (x < lineEnd) tmp8[x++] = color2;
}
}
}
return outData;
}
static uint8_t *loadBMPTo4BitPal(const uint8_t *src) // supports 4-bit RLE only
{
const uint8_t *pData, *src8;
uint8_t palIdx, *outData, *dst8, *tmp8, pal1, pal2;
int32_t len, byte, i, x , y, lineEnd, colorsInBitmap, palEntries;
uint32_t pal[16];
bmpHeader_t *hdr;
hdr = (bmpHeader_t *)&src[2];
pData = &src[hdr->bfOffBits];
colorsInBitmap = 1 << hdr->biBitCount;
if (hdr->biCompression != COMP_RLE4 || hdr->biClrUsed > 16 || colorsInBitmap > 16)
return NULL;
outData = (uint8_t *)malloc(hdr->biWidth * hdr->biHeight * sizeof (uint8_t));
if (outData == NULL)
return NULL;
palEntries = hdr->biClrUsed == 0 ? colorsInBitmap : hdr->biClrUsed;
memcpy(pal, &src[0x36], palEntries * sizeof (uint32_t));
// pre-fill image with first palette color
palIdx = getFT2PalNrFromPixel(pal[0]);
for (i = 0; i < hdr->biWidth * hdr->biHeight; i++)
outData[i] = palIdx;
lineEnd = hdr->biWidth;
src8 = pData;
dst8 = outData;
x = 0;
y = hdr->biHeight - 1;
while (true)
{
byte = *src8++;
if (byte == 0) // escape control
{
byte = *src8++;
if (byte == 0) // end of line
{
x = 0;
y--;
}
else if (byte == 1) // end of bitmap
{
break;
}
else if (byte == 2) // add to x/y position
{
x += *src8++;
y -= *src8++;
}
else // absolute bytes
{
tmp8 = &dst8[y * hdr->biWidth];
len = byte >> 1;
for (i = 0; i < len; i++)
{
palIdx = *src8++;
tmp8[x++] = getFT2PalNrFromPixel(pal[palIdx >> 4]);
if (x < lineEnd) tmp8[x++] = getFT2PalNrFromPixel(pal[palIdx & 0xF]);
}
if (((byte + 1) >> 1) & 1)
src8++;
}
}
else
{
palIdx = *src8++;
pal1 = getFT2PalNrFromPixel(pal[palIdx >> 4]);
pal2 = getFT2PalNrFromPixel(pal[palIdx & 0x0F]);
tmp8 = &dst8[y * hdr->biWidth];
len = byte >> 1;
for (i = 0; i < len; i++)
{
tmp8[x++] = pal1;
if (x < lineEnd) tmp8[x++] = pal2;
}
}
}
return outData;
}