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 }