ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

ft2_bmp.c (14032B)


      1 // for finding memory leaks in debug mode with Visual Studio
      2 #if defined _DEBUG && defined _MSC_VER
      3 #include <crtdbg.h>
      4 #endif
      5 
      6 #include <assert.h>
      7 #include <stdint.h>
      8 #include <stdbool.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include "ft2_palette.h"
     12 #include "ft2_gfxdata.h"
     13 #include "ft2_bmp.h"
     14 #include "ft2_video.h"
     15 
     16 enum
     17 {
     18 	COMP_RGB = 0,
     19 	COMP_RLE8 = 1,
     20 	COMP_RLE4 = 2
     21 };
     22 
     23 typedef struct bmpHeader_t
     24 {
     25 	uint32_t bfSizebfSize;
     26 	uint16_t bfReserved1;
     27 	uint16_t bfReserved2;
     28 	uint32_t bfOffBits;
     29 	uint32_t biSize;
     30 	int32_t biWidth;
     31 	int32_t biHeight;
     32 	uint16_t biPlanes;
     33 	uint16_t biBitCount;
     34 	uint32_t biCompression;
     35 	uint32_t biSizeImage;
     36 	int32_t biXPelsPerMeter;
     37 	int32_t biYPelsPerMeter;
     38 	int32_t biClrUsed;
     39 	int32_t biClrImportant;
     40 } bmpHeader_t;
     41 
     42 static uint32_t *loadBMPTo32Bit(const uint8_t *src);
     43 static uint8_t *loadBMPTo1Bit(const uint8_t *src);
     44 static uint8_t *loadBMPTo4BitPal(const uint8_t *src);
     45 
     46 bmp_t bmp; // globalized
     47 
     48 // if you match a color in this table, you have the real palette index (LUT pos)
     49 #define NUM_CUSTOM_PALS 17
     50 static const uint32_t bmpCustomPalette[NUM_CUSTOM_PALS] =
     51 { // 0xC0FFEE = spacers
     52 	0x000000, 0x5397FF, 0x000067, 0x4BFFFF, 0xAB7787,
     53 	0xFFFFFF, 0x7F7F7F, 0xABCDEF, 0x733747, 0xF7CBDB,
     54 	0x434343, 0xD3D3D3, 0xFFFF00, 0xC0FFEE, 0xC0FFEE,
     55 	0xC0FFEE, 0xFF0000 // last value = loop pin line
     56 };
     57 
     58 static int8_t getFT2PalNrFromPixel(uint32_t pixel32)
     59 {
     60 	for (int32_t i = 0; i < NUM_CUSTOM_PALS; i++)
     61 	{
     62 		if (pixel32 == bmpCustomPalette[i])
     63 			return (int8_t)i;
     64 	}
     65 
     66 	return PAL_TRANSPR;
     67 }
     68 
     69 bool loadBMPs(void)
     70 {
     71 	memset(&bmp, 0, sizeof (bmp));
     72 
     73 	bmp.ft2OldAboutLogo = loadBMPTo4BitPal(ft2OldAboutLogoBMP);
     74 	bmp.ft2AboutLogo = loadBMPTo32Bit(ft2AboutLogoBMP);
     75 	bmp.buttonGfx = loadBMPTo1Bit(buttonGfxBMP);
     76 	bmp.font1 = loadBMPTo1Bit(font1BMP);
     77 	bmp.font2 = loadBMPTo1Bit(font2BMP);
     78 	bmp.font3 = loadBMPTo1Bit(font3BMP);
     79 	bmp.font4 = loadBMPTo1Bit(font4BMP);
     80 	bmp.font6 = loadBMPTo1Bit(font6BMP);
     81 	bmp.font7 = loadBMPTo1Bit(font7BMP);
     82 	bmp.font8 = loadBMPTo1Bit(font8BMP);
     83 	bmp.ft2LogoBadges = loadBMPTo4BitPal(ft2LogoBadgesBMP);
     84 	bmp.ft2ByBadges = loadBMPTo4BitPal(ft2ByBadgesBMP);
     85 	bmp.midiLogo = loadBMPTo4BitPal(midiLogoBMP);
     86 	bmp.nibblesLogo = loadBMPTo4BitPal(nibblesLogoBMP);
     87 	bmp.nibblesStages = loadBMPTo4BitPal(nibblesStagesBMP);
     88 	bmp.loopPins = loadBMPTo4BitPal(loopPinsBMP);
     89 	bmp.mouseCursors = loadBMPTo4BitPal(mouseCursorsBMP);
     90 	bmp.mouseCursorBusyClock = loadBMPTo4BitPal(mouseCursorBusyClockBMP);
     91 	bmp.mouseCursorBusyGlass = loadBMPTo4BitPal(mouseCursorBusyGlassBMP);
     92 	bmp.whitePianoKeys = loadBMPTo4BitPal(whitePianoKeysBMP);
     93 	bmp.blackPianoKeys = loadBMPTo4BitPal(blackPianoKeysBMP);
     94 	bmp.vibratoWaveforms = loadBMPTo4BitPal(vibratoWaveformsBMP);
     95 	bmp.scopeRec = loadBMPTo4BitPal(scopeRecBMP);
     96 	bmp.scopeMute = loadBMPTo4BitPal(scopeMuteBMP);
     97 	bmp.radiobuttonGfx = loadBMPTo4BitPal(radiobuttonGfxBMP);
     98 	bmp.checkboxGfx = loadBMPTo4BitPal(checkboxGfxBMP);
     99 
    100 	if (bmp.ft2OldAboutLogo == NULL || bmp.ft2AboutLogo == NULL || bmp.buttonGfx == NULL || bmp.font1 == NULL || bmp.font2 == NULL ||
    101 		bmp.font3 == NULL || bmp.font4 == NULL || bmp.font6 == NULL || bmp.font7 == NULL ||
    102 		bmp.font8 == NULL || bmp.ft2LogoBadges == NULL || bmp.ft2ByBadges == NULL ||
    103 		bmp.midiLogo == NULL || bmp.nibblesLogo == NULL || bmp.nibblesStages == NULL ||
    104 		bmp.loopPins == NULL || bmp.mouseCursors == NULL || bmp.mouseCursorBusyClock == NULL ||
    105 		bmp.mouseCursorBusyGlass == NULL || bmp.whitePianoKeys == NULL || bmp.blackPianoKeys == NULL ||
    106 		bmp.vibratoWaveforms == NULL || bmp.scopeRec == NULL || bmp.scopeMute == NULL ||
    107 		bmp.radiobuttonGfx == NULL || bmp.checkboxGfx == NULL)
    108 	{
    109 		showErrorMsgBox("Not enough memory!");
    110 		return false;
    111 	}
    112 
    113 	return true;
    114 }
    115 
    116 void freeBMPs(void)
    117 {
    118 	if (bmp.ft2OldAboutLogo != NULL) { free(bmp.ft2OldAboutLogo); bmp.ft2OldAboutLogo = NULL; }
    119 	if (bmp.ft2AboutLogo != NULL) { free(bmp.ft2AboutLogo); bmp.ft2AboutLogo = NULL; }
    120 	if (bmp.buttonGfx != NULL) { free(bmp.buttonGfx); bmp.buttonGfx = NULL; }
    121 	if (bmp.font1 != NULL) { free(bmp.font1); bmp.font1 = NULL; }
    122 	if (bmp.font2 != NULL) { free(bmp.font2); bmp.font2 = NULL; }
    123 	if (bmp.font3 != NULL) { free(bmp.font3); bmp.font3 = NULL; }
    124 	if (bmp.font4 != NULL) { free(bmp.font4); bmp.font4 = NULL; }
    125 	if (bmp.font6 != NULL) { free(bmp.font6); bmp.font6 = NULL; }
    126 	if (bmp.font7 != NULL) { free(bmp.font7); bmp.font7 = NULL; }
    127 	if (bmp.font8 != NULL) { free(bmp.font8); bmp.font8 = NULL; }
    128 	if (bmp.ft2LogoBadges != NULL) { free(bmp.ft2LogoBadges); bmp.ft2LogoBadges = NULL; }
    129 	if (bmp.ft2ByBadges != NULL) { free(bmp.ft2ByBadges); bmp.ft2ByBadges = NULL; }
    130 	if (bmp.midiLogo != NULL) { free(bmp.midiLogo); bmp.midiLogo = NULL; }
    131 	if (bmp.nibblesLogo != NULL) { free(bmp.nibblesLogo); bmp.nibblesLogo = NULL; }
    132 	if (bmp.nibblesStages != NULL) { free(bmp.nibblesStages); bmp.nibblesStages = NULL; }
    133 	if (bmp.loopPins != NULL) { free(bmp.loopPins); bmp.loopPins = NULL; }
    134 	if (bmp.mouseCursors != NULL) { free(bmp.mouseCursors); bmp.mouseCursors = NULL; }
    135 	if (bmp.mouseCursorBusyClock != NULL) { free(bmp.mouseCursorBusyClock); bmp.mouseCursorBusyClock = NULL; }
    136 	if (bmp.mouseCursorBusyGlass != NULL) { free(bmp.mouseCursorBusyGlass); bmp.mouseCursorBusyGlass = NULL; }
    137 	if (bmp.whitePianoKeys != NULL) { free(bmp.whitePianoKeys); bmp.whitePianoKeys = NULL; }
    138 	if (bmp.blackPianoKeys != NULL) { free(bmp.blackPianoKeys); bmp.blackPianoKeys = NULL; }
    139 	if (bmp.vibratoWaveforms != NULL) { free(bmp.vibratoWaveforms); bmp.vibratoWaveforms = NULL; }
    140 	if (bmp.scopeRec != NULL) { free(bmp.scopeRec); bmp.scopeRec = NULL; }
    141 	if (bmp.scopeMute != NULL) { free(bmp.scopeMute); bmp.scopeMute = NULL; }
    142 	if (bmp.radiobuttonGfx != NULL) { free(bmp.radiobuttonGfx); bmp.radiobuttonGfx = NULL; }
    143 	if (bmp.checkboxGfx != NULL) { free(bmp.checkboxGfx); bmp.checkboxGfx = NULL; }
    144 }
    145 
    146 /* Very basic BMP loaders that supports top-down RLE-packed bitmaps, but only at 4-bit or 8-bit colors.
    147 ** This is only meant to be used for BMPs that are carefully crafted for this program!
    148 */
    149 
    150 #ifdef _DEBUG
    151 
    152 #define CHECK_SRC_BOUNDARY     assert(src8      < src8End);
    153 #define CHECK_DST8_BOUNDARY    assert(tmp8      < allocEnd);
    154 #define CHECK_DST8_BOUNDARY_X  assert(&tmp8[x]  < allocEnd);
    155 #define CHECK_DST32_BOUNDARY   assert(tmp32     < allocEnd);
    156 #define CHECK_DST32_BOUNDARY_X assert(&tmp32[x] < allocEnd);
    157 
    158 #else
    159 #define CHECK_SRC_BOUNDARY
    160 #define CHECK_DST8_BOUNDARY
    161 #define CHECK_DST8_BOUNDARY_X
    162 #define CHECK_DST32_BOUNDARY
    163 #define CHECK_DST32_BOUNDARY_X
    164 #endif
    165 
    166 static uint32_t *loadBMPTo32Bit(const uint8_t *src)
    167 {
    168 	int32_t len, byte, palIdx;
    169 	uint32_t *tmp32, color, color2, pal[256];
    170 
    171 	bmpHeader_t *hdr = (bmpHeader_t *)&src[2];
    172 	const uint8_t *pData = &src[hdr->bfOffBits];
    173 	const int32_t colorsInBitmap = 1 << hdr->biBitCount;
    174 
    175 	if (hdr->biCompression == COMP_RGB || hdr->biClrUsed > 256 || colorsInBitmap > 256)
    176 		return NULL;
    177 
    178 	uint32_t *outData = (uint32_t *)malloc(hdr->biWidth * hdr->biHeight * sizeof (uint32_t));
    179 	if (outData == NULL)
    180 		return NULL;
    181 
    182 #ifdef _DEBUG
    183 	const uint32_t *allocEnd = outData + (hdr->biWidth * hdr->biHeight);
    184 #endif
    185 
    186 	// pre-fill image with first palette color
    187 	const int32_t palEntries = (hdr->biClrUsed == 0) ? colorsInBitmap : hdr->biClrUsed;
    188 	memcpy(pal, &src[0x36], palEntries * sizeof (uint32_t));
    189 
    190 	for (int32_t i = 0; i < hdr->biWidth * hdr->biHeight; i++)
    191 		outData[i] = pal[0];
    192 
    193 	const int32_t lineEnd = hdr->biWidth;
    194 	const uint8_t *src8 = pData;
    195 #ifdef _DEBUG
    196 	const uint8_t *src8End = src8 + hdr->biSizeImage;
    197 #endif
    198 	uint32_t *dst32 = outData;
    199 	int32_t x = 0;
    200 	int32_t y = hdr->biHeight - 1;
    201 
    202 	while (true)
    203 	{
    204 		CHECK_SRC_BOUNDARY
    205 		byte = *src8++;
    206 		if (byte == 0) // escape control
    207 		{
    208 			CHECK_SRC_BOUNDARY
    209 			byte = *src8++;
    210 			if (byte == 0) // end of line
    211 			{
    212 				x = 0;
    213 				y--;
    214 			}
    215 			else if (byte == 1) // end of bitmap
    216 			{
    217 				break;
    218 			}
    219 			else if (byte == 2) // add to x/y position
    220 			{
    221 				CHECK_SRC_BOUNDARY
    222 				x += *src8++;
    223 				CHECK_SRC_BOUNDARY
    224 				y -= *src8++;
    225 			}
    226 			else // absolute bytes
    227 			{
    228 				if (hdr->biCompression == COMP_RLE8)
    229 				{
    230 					tmp32 = &dst32[(y * hdr->biWidth) + x];
    231 					for (int32_t i = 0; i < byte; i++)
    232 					{
    233 						CHECK_DST32_BOUNDARY
    234 						CHECK_SRC_BOUNDARY
    235 						*tmp32++ = pal[*src8++];
    236 					}
    237 
    238 					if (byte & 1)
    239 						src8++;
    240 
    241 					x += byte;
    242 				}
    243 				else
    244 				{
    245 
    246 					len = byte >> 1;
    247 					tmp32 = &dst32[y * hdr->biWidth];
    248 					for (int32_t i = 0; i < len; i++)
    249 					{
    250 						CHECK_SRC_BOUNDARY
    251 						palIdx = *src8++;
    252 
    253 						CHECK_DST32_BOUNDARY_X
    254 						tmp32[x++] = pal[palIdx >> 4];
    255 
    256 						if (x < lineEnd)
    257 						{
    258 							CHECK_DST32_BOUNDARY_X
    259 							tmp32[x++] = pal[palIdx & 0xF];
    260 						}
    261 					}
    262 
    263 					if (((byte + 1) >> 1) & 1)
    264 						src8++;
    265 				}
    266 			}
    267 		}
    268 		else
    269 		{
    270 			CHECK_SRC_BOUNDARY
    271 			palIdx = *src8++;
    272 
    273 			if (hdr->biCompression == COMP_RLE8)
    274 			{
    275 				color = pal[palIdx];
    276 				tmp32 = &dst32[(y * hdr->biWidth) + x];
    277 				for (int32_t i = 0; i < byte; i++)
    278 				{
    279 					CHECK_DST32_BOUNDARY
    280 					*tmp32++ = color;
    281 				}
    282 
    283 				x += byte;
    284 			}
    285 			else
    286 			{
    287 				color = pal[palIdx >> 4];
    288 				color2 = pal[palIdx & 0x0F];
    289 
    290 				len = byte >> 1;
    291 				tmp32 = &dst32[y * hdr->biWidth];
    292 				for (int32_t i = 0; i < len; i++)
    293 				{
    294 					CHECK_DST32_BOUNDARY_X
    295 					tmp32[x++] = color;
    296 
    297 					if (x < lineEnd)
    298 					{
    299 						CHECK_DST32_BOUNDARY_X
    300 						tmp32[x++] = color2;
    301 					}
    302 				}
    303 			}
    304 		}
    305 	}
    306 
    307 	return outData;
    308 }
    309 
    310 static uint8_t *loadBMPTo1Bit(const uint8_t *src) // supports 4-bit RLE only
    311 {
    312 	uint8_t palIdx, color, color2, *tmp8;
    313 	int32_t len, byte, i;
    314 	uint32_t pal[16];
    315 
    316 	bmpHeader_t *hdr = (bmpHeader_t *)&src[2];
    317 	const uint8_t *pData = &src[hdr->bfOffBits];
    318 	const int32_t colorsInBitmap = 1 << hdr->biBitCount;
    319 
    320 	if (hdr->biCompression != COMP_RLE4 || hdr->biClrUsed > 16 || colorsInBitmap > 16)
    321 		return NULL;
    322 
    323 	uint8_t *outData = (uint8_t *)malloc(hdr->biWidth * hdr->biHeight * sizeof (uint8_t));
    324 	if (outData == NULL)
    325 		return NULL;
    326 
    327 #ifdef _DEBUG
    328 	const uint8_t *allocEnd = outData + (hdr->biWidth * hdr->biHeight);
    329 #endif
    330 
    331 	const int32_t palEntries = (hdr->biClrUsed == 0) ? colorsInBitmap : hdr->biClrUsed;
    332 	memcpy(pal, &src[0x36], palEntries * sizeof (uint32_t));
    333 
    334 	// pre-fill image with first palette color
    335 	color = !!pal[0];
    336 	for (i = 0; i < hdr->biWidth * hdr->biHeight; i++)
    337 		outData[i] = color;
    338 
    339 	const int32_t lineEnd = hdr->biWidth;
    340 	const uint8_t *src8 = pData;
    341 #ifdef _DEBUG
    342 	const uint8_t *src8End = src8 + hdr->biSizeImage;
    343 #endif
    344 	uint8_t *dst8 = outData;
    345 	int32_t x = 0;
    346 	int32_t y = hdr->biHeight - 1;
    347 
    348 	while (true)
    349 	{
    350 		CHECK_SRC_BOUNDARY
    351 		byte = *src8++;
    352 		if (byte == 0) // escape control
    353 		{
    354 			CHECK_SRC_BOUNDARY
    355 			byte = *src8++;
    356 			if (byte == 0) // end of line
    357 			{
    358 				x = 0;
    359 				y--;
    360 			}
    361 			else if (byte == 1) // end of bitmap
    362 			{
    363 				break;
    364 			}
    365 			else if (byte == 2) // add to x/y position
    366 			{
    367 				CHECK_SRC_BOUNDARY
    368 				x += *src8++;
    369 				CHECK_SRC_BOUNDARY
    370 				y -= *src8++;
    371 			}
    372 			else // absolute bytes
    373 			{
    374 				len = byte >> 1;
    375 				tmp8 = &dst8[y * hdr->biWidth];
    376 				for (i = 0; i < len; i++)
    377 				{
    378 					CHECK_SRC_BOUNDARY
    379 					palIdx = *src8++;
    380 
    381 					CHECK_DST8_BOUNDARY_X
    382 					tmp8[x++] = !!pal[palIdx >> 4];
    383 					
    384 					if (x < lineEnd)
    385 					{
    386 						CHECK_DST8_BOUNDARY_X
    387 						tmp8[x++] = !!pal[palIdx & 0xF];
    388 					}
    389 				}
    390 
    391 				if (((byte + 1) >> 1) & 1)
    392 					src8++;
    393 			}
    394 		}
    395 		else
    396 		{
    397 			CHECK_SRC_BOUNDARY
    398 			palIdx = *src8++;
    399 
    400 			color = !!pal[palIdx >> 4];
    401 			color2 = !!pal[palIdx & 0x0F];
    402 
    403 			len = byte >> 1;
    404 			tmp8 = &dst8[y * hdr->biWidth];
    405 			for (i = 0; i < len; i++)
    406 			{
    407 				CHECK_DST8_BOUNDARY_X
    408 				tmp8[x++] = color;
    409 
    410 				if (x < lineEnd)
    411 				{
    412 					CHECK_DST8_BOUNDARY_X
    413 					tmp8[x++] = color2;
    414 				}
    415 			}
    416 		}
    417 	}
    418 
    419 	return outData;
    420 }
    421 
    422 static uint8_t *loadBMPTo4BitPal(const uint8_t *src) // supports 4-bit RLE only
    423 {
    424 	uint8_t palIdx, *tmp8, pal1, pal2;
    425 	int32_t len, byte, i;
    426 	uint32_t pal[16];
    427 
    428 	bmpHeader_t *hdr = (bmpHeader_t *)&src[2];
    429 	const uint8_t *pData = &src[hdr->bfOffBits];
    430 	const int32_t colorsInBitmap = 1 << hdr->biBitCount;
    431 
    432 	if (hdr->biCompression != COMP_RLE4 || hdr->biClrUsed > 16 || colorsInBitmap > 16)
    433 		return NULL;
    434 
    435 	uint8_t *outData = (uint8_t *)malloc(hdr->biWidth * hdr->biHeight * sizeof (uint8_t));
    436 	if (outData == NULL)
    437 		return NULL;
    438 
    439 #ifdef _DEBUG
    440 	const uint8_t *allocEnd = outData + (hdr->biWidth * hdr->biHeight);
    441 #endif
    442 
    443 	const int32_t palEntries = (hdr->biClrUsed == 0) ? colorsInBitmap : hdr->biClrUsed;
    444 	memcpy(pal, &src[0x36], palEntries * sizeof (uint32_t));
    445 
    446 	// pre-fill image with first palette color
    447 	palIdx = getFT2PalNrFromPixel(pal[0]);
    448 	for (i = 0; i < hdr->biWidth * hdr->biHeight; i++)
    449 		outData[i] = palIdx;
    450 
    451 	const int32_t lineEnd = hdr->biWidth;
    452 	const uint8_t *src8 = pData;
    453 #ifdef _DEBUG
    454 	const uint8_t *src8End = src8 + hdr->biSizeImage;
    455 #endif
    456 	uint8_t *dst8 = outData;
    457 	int32_t x = 0;
    458 	int32_t y = hdr->biHeight - 1;
    459 
    460 	while (true)
    461 	{
    462 		CHECK_SRC_BOUNDARY
    463 		byte = *src8++;
    464 		if (byte == 0) // escape control
    465 		{
    466 			CHECK_SRC_BOUNDARY
    467 			byte = *src8++;
    468 			if (byte == 0) // end of line
    469 			{
    470 				x = 0;
    471 				y--;
    472 			}
    473 			else if (byte == 1) // end of bitmap
    474 			{
    475 				break;
    476 			}
    477 			else if (byte == 2) // add to x/y position
    478 			{
    479 				CHECK_SRC_BOUNDARY
    480 				x += *src8++;
    481 				CHECK_SRC_BOUNDARY
    482 				y -= *src8++;
    483 			}
    484 			else // absolute bytes
    485 			{
    486 				tmp8 = &dst8[y * hdr->biWidth];
    487 				len = byte >> 1;
    488 				for (i = 0; i < len; i++)
    489 				{
    490 					CHECK_SRC_BOUNDARY
    491 					palIdx = *src8++;
    492 
    493 					CHECK_DST8_BOUNDARY_X
    494 					tmp8[x++] = getFT2PalNrFromPixel(pal[palIdx >> 4]);
    495 
    496 					if (x < lineEnd)
    497 					{
    498 						CHECK_DST8_BOUNDARY_X
    499 						tmp8[x++] = getFT2PalNrFromPixel(pal[palIdx & 0xF]);
    500 					}
    501 				}
    502 
    503 				if (((byte + 1) >> 1) & 1)
    504 					src8++;
    505 			}
    506 		}
    507 		else
    508 		{
    509 			CHECK_SRC_BOUNDARY
    510 			palIdx = *src8++;
    511 
    512 			pal1 = getFT2PalNrFromPixel(pal[palIdx >> 4]);
    513 			pal2 = getFT2PalNrFromPixel(pal[palIdx & 0x0F]);
    514 
    515 			tmp8 = &dst8[y * hdr->biWidth];
    516 			len = byte >> 1;
    517 			for (i = 0; i < len; i++)
    518 			{
    519 				CHECK_DST8_BOUNDARY_X
    520 				tmp8[x++] = pal1;
    521 
    522 				if (x < lineEnd)
    523 				{
    524 					CHECK_DST8_BOUNDARY_X
    525 					tmp8[x++] = pal2;
    526 				}
    527 			}
    528 		}
    529 	}
    530 
    531 	return outData;
    532 }