ft2_pattern_draw.c (28157B)
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 <stdio.h> 7 #include <stdint.h> 8 #include "ft2_header.h" 9 #include "ft2_pattern_ed.h" 10 #include "ft2_config.h" 11 #include "ft2_gui.h" 12 #include "ft2_video.h" 13 #include "ft2_tables.h" 14 #include "ft2_bmp.h" 15 #include "ft2_structs.h" 16 17 static note_t emptyPattern[MAX_CHANNELS * MAX_PATT_LEN]; 18 19 static const uint8_t *font4Ptr, *font5Ptr; 20 static const uint8_t vol2charTab1[16] = { 39, 0, 1, 2, 3, 4, 36, 52, 53, 54, 28, 31, 25, 58, 59, 22 }; 21 static const uint8_t vol2charTab2[16] = { 42, 0, 1, 2, 3, 4, 36, 37, 38, 39, 28, 31, 25, 40, 41, 22 }; 22 static const uint8_t columnModeTab[12] = { 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3 }; 23 static const uint8_t sharpNote1Char_small[12] = { 8*6, 8*6, 9*6, 9*6, 10*6, 11*6, 11*6, 12*6, 12*6, 13*6, 13*6, 14*6 }; 24 static const uint8_t sharpNote2Char_small[12] = { 16*6, 15*6, 16*6, 15*6, 16*6, 16*6, 15*6, 16*6, 15*6, 16*6, 15*6, 16*6 }; 25 static const uint8_t flatNote1Char_small[12] = { 8*6, 9*6, 9*6, 10*6, 10*6, 11*6, 12*6, 12*6, 13*6, 13*6, 14*6, 14*6 }; 26 static const uint8_t flatNote2Char_small[12] = { 16*6, 17*6, 16*6, 17*6, 16*6, 16*6, 17*6, 16*6, 17*6, 16*6, 17*6, 16*6 }; 27 static const uint8_t sharpNote1Char_med[12] = { 12*8, 12*8, 13*8, 13*8, 14*8, 15*8, 15*8, 16*8, 16*8, 10*8, 10*8, 11*8 }; 28 static const uint16_t sharpNote2Char_med[12] = { 36*8, 37*8, 36*8, 37*8, 36*8, 36*8, 37*8, 36*8, 37*8, 36*8, 37*8, 36*8 }; 29 static const uint8_t flatNote1Char_med[12] = { 12*8, 13*8, 13*8, 14*8, 14*8, 15*8, 16*8, 16*8, 10*8, 10*8, 11*8, 11*8 }; 30 static const uint16_t flatNote2Char_med[12] = { 36*8, 38*8, 36*8, 38*8, 36*8, 36*8, 38*8, 36*8, 38*8, 36*8, 38*8, 36*8 }; 31 static const uint16_t sharpNote1Char_big[12] = { 12*16, 12*16, 13*16, 13*16, 14*16, 15*16, 15*16, 16*16, 16*16, 10*16, 10*16, 11*16 }; 32 static const uint16_t sharpNote2Char_big[12] = { 36*16, 37*16, 36*16, 37*16, 36*16, 36*16, 37*16, 36*16, 37*16, 36*16, 37*16, 36*16 }; 33 static const uint16_t flatNote1Char_big[12] = { 12*16, 13*16, 13*16, 14*16, 14*16, 15*16, 16*16, 16*16, 10*16, 10*16, 11*16, 11*16 }; 34 static const uint16_t flatNote2Char_big[12] = { 36*16, 38*16, 36*16, 38*16, 36*16, 36*16, 38*16, 36*16, 38*16, 36*16, 38*16, 36*16 }; 35 36 static void pattCharOut(uint32_t xPos, uint32_t yPos, uint8_t chr, uint8_t fontType, uint32_t color); 37 static void drawEmptyNoteSmall(uint32_t xPos, uint32_t yPos, uint32_t color); 38 static void drawKeyOffSmall(uint32_t xPos, uint32_t yPos, uint32_t color); 39 static void drawNoteSmall(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color); 40 static void drawEmptyNoteMedium(uint32_t xPos, uint32_t yPos, uint32_t color); 41 static void drawKeyOffMedium(uint32_t xPos, uint32_t yPos, uint32_t color); 42 static void drawNoteMedium(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color); 43 static void drawEmptyNoteBig(uint32_t xPos, uint32_t yPos, uint32_t color); 44 static void drawKeyOffBig(uint32_t xPos, uint32_t yPos, uint32_t color); 45 static void drawNoteBig(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color); 46 47 void updatePattFontPtrs(void) 48 { 49 //config.ptnFont is pre-clamped and safe to use 50 font4Ptr = &bmp.font4[config.ptnFont * (FONT4_WIDTH * FONT4_CHAR_H)]; 51 font5Ptr = &bmp.font4[(4 + config.ptnFont) * (FONT4_WIDTH * FONT4_CHAR_H)]; 52 } 53 54 void drawPatternBorders(void) 55 { 56 // get heights/pos/rows depending on configuration 57 const pattCoord2_t *pattCoord = &pattCoord2Table[config.ptnStretch][ui.pattChanScrollShown][ui.extendedPatternEditor]; 58 59 // set pattern cursor Y position 60 editor.ptnCursorY = pattCoord->lowerRowsY - 9; 61 62 int32_t chans = ui.numChannelsShown; 63 if (chans > ui.maxVisibleChannels) 64 chans = ui.maxVisibleChannels; 65 66 // in some configurations, there will be two empty channels to the right, fix that 67 if (chans == 2) 68 chans = 4; 69 else if (chans == 10 && !config.ptnShowVolColumn) 70 chans = 12; 71 72 assert(chans >= 2 && chans <= 12); 73 74 const uint16_t chanWidth = chanWidths[(chans >> 1) - 1] + 2; 75 76 // fill scrollbar framework (if needed) 77 if (ui.pattChanScrollShown) 78 drawFramework(0, 383, 632, 17, FRAMEWORK_TYPE1); 79 80 if (config.ptnFrmWrk) 81 { 82 // pattern editor w/ framework 83 84 if (ui.extendedPatternEditor) 85 { 86 vLine(0, 69, 330, PAL_DSKTOP1); 87 vLine(631, 68, 331, PAL_DSKTOP2); 88 89 vLine(1, 69, 330, PAL_DESKTOP); 90 vLine(630, 69, 330, PAL_DESKTOP); 91 92 hLine(0, 68, 631, PAL_DSKTOP1); 93 hLine(1, 69, 630, PAL_DESKTOP); 94 95 if (!ui.pattChanScrollShown) 96 { 97 hLine(1, 398, 630, PAL_DESKTOP); 98 hLine(0, 399, 632, PAL_DSKTOP2); 99 } 100 } 101 else 102 { 103 vLine(0, 174, 225, PAL_DSKTOP1); 104 vLine(631, 173, 226, PAL_DSKTOP2); 105 106 vLine(1, 174, 225, PAL_DESKTOP); 107 vLine(630, 174, 225, PAL_DESKTOP); 108 109 hLine(0, 173, 631, PAL_DSKTOP1); 110 hLine(1, 174, 630, PAL_DESKTOP); 111 112 if (!ui.pattChanScrollShown) 113 { 114 hLine(1, 398, 630, PAL_DESKTOP); 115 hLine(0, 399, 632, PAL_DSKTOP2); 116 } 117 } 118 119 // fill middle (current row) 120 fillRect(2, pattCoord->lowerRowsY - 9, 628, 9, PAL_DESKTOP); 121 122 // fill row number boxes 123 drawFramework(2, pattCoord->upperRowsY, 25, pattCoord->upperRowsH, FRAMEWORK_TYPE2); // top left 124 drawFramework(604, pattCoord->upperRowsY, 26, pattCoord->upperRowsH, FRAMEWORK_TYPE2); // top right 125 drawFramework(2, pattCoord->lowerRowsY, 25, pattCoord->lowerRowsH, FRAMEWORK_TYPE2); // bottom left 126 drawFramework(604, pattCoord->lowerRowsY, 26, pattCoord->lowerRowsH, FRAMEWORK_TYPE2); // bottom right 127 128 // draw channels 129 uint16_t xOffs = 28; 130 for (int32_t i = 0; i < chans; i++) 131 { 132 vLine(xOffs - 1, pattCoord->upperRowsY, pattCoord->upperRowsH, PAL_DESKTOP); 133 vLine(xOffs - 1, pattCoord->lowerRowsY, pattCoord->lowerRowsH + 1, PAL_DESKTOP); 134 135 drawFramework(xOffs, pattCoord->upperRowsY, chanWidth, pattCoord->upperRowsH, FRAMEWORK_TYPE2); // top part 136 drawFramework(xOffs, pattCoord->lowerRowsY, chanWidth, pattCoord->lowerRowsH, FRAMEWORK_TYPE2); // bottom part 137 138 xOffs += chanWidth+1; 139 } 140 141 vLine(xOffs-1, pattCoord->upperRowsY, pattCoord->upperRowsH, PAL_DESKTOP); 142 vLine(xOffs-1, pattCoord->lowerRowsY, pattCoord->lowerRowsH+1, PAL_DESKTOP); 143 } 144 else 145 { 146 // pattern editor without framework 147 148 if (ui.extendedPatternEditor) 149 { 150 const int32_t clearSize = ui.pattChanScrollShown ? (SCREEN_W * sizeof (int32_t) * 315) : (SCREEN_W * sizeof (int32_t) * 332); 151 memset(&video.frameBuffer[68 * SCREEN_W], 0, clearSize); 152 } 153 else 154 { 155 const int32_t clearSize = ui.pattChanScrollShown ? (SCREEN_W * sizeof(int32_t) * 210) : (SCREEN_W * sizeof(int32_t) * 227); 156 memset(&video.frameBuffer[173 * SCREEN_W], 0, clearSize); 157 } 158 159 drawFramework(0, pattCoord->lowerRowsY - 10, SCREEN_W, 11, FRAMEWORK_TYPE1); 160 } 161 162 if (ui.pattChanScrollShown) 163 { 164 showScrollBar(SB_CHAN_SCROLL); 165 showPushButton(PB_CHAN_SCROLL_LEFT); 166 showPushButton(PB_CHAN_SCROLL_RIGHT); 167 } 168 else 169 { 170 hideScrollBar(SB_CHAN_SCROLL); 171 hidePushButton(PB_CHAN_SCROLL_LEFT); 172 hidePushButton(PB_CHAN_SCROLL_RIGHT); 173 174 setScrollBarPos(SB_CHAN_SCROLL, 0, false); 175 } 176 } 177 178 static void writeCursor(void) 179 { 180 const int32_t tabOffset = (config.ptnShowVolColumn * 32) + (columnModeTab[ui.numChannelsShown-1] * 8) + cursor.object; 181 182 int32_t xPos = pattCursorXTab[tabOffset]; 183 const int32_t width = pattCursorWTab[tabOffset]; 184 185 assert(editor.ptnCursorY > 0 && xPos > 0 && width > 0); 186 xPos += ((cursor.ch - ui.channelOffset) * ui.patternChannelWidth); 187 188 uint32_t *dstPtr = &video.frameBuffer[(editor.ptnCursorY * SCREEN_W) + xPos]; 189 for (int32_t y = 0; y < 9; y++) 190 { 191 for (int32_t x = 0; x < width; x++) 192 dstPtr[x] = video.palette[(dstPtr[x] >> 24) ^ 4]; // ">> 24" to get palette, XOR 4 to change to cursor palette 193 194 dstPtr += SCREEN_W; 195 } 196 } 197 198 static void writePatternBlockMark(int32_t currRow, uint32_t rowHeight, const pattCoord_t *pattCoord) 199 { 200 int32_t y1, y2; 201 202 // this can happen (buggy FT2 code), treat like no mark 203 if (pattMark.markY1 > pattMark.markY2) 204 return; 205 206 const int32_t startCh = ui.channelOffset; 207 const int32_t endCh = ui.channelOffset + (ui.numChannelsShown - 1); 208 const int32_t startRow = currRow - pattCoord->numUpperRows; 209 const int32_t endRow = currRow + pattCoord->numLowerRows; 210 211 // test if pattern marking is outside of visible area (don't draw) 212 if (pattMark.markX1 > endCh || pattMark.markX2 < startCh || pattMark.markY1 > endRow || pattMark.markY2 < startRow) 213 return; 214 215 const markCoord_t *markCoord = &markCoordTable[config.ptnStretch][ui.pattChanScrollShown][ui.extendedPatternEditor]; 216 const int32_t pattYStart = markCoord->upperRowsY; 217 218 // X1 219 220 int32_t x1 = 32 + ((pattMark.markX1 - ui.channelOffset) * ui.patternChannelWidth); 221 if (x1 < 32) 222 x1 = 32; 223 224 // X2 225 226 int32_t x2 = (32 - 8) + (((pattMark.markX2 + 1) - ui.channelOffset) * ui.patternChannelWidth); 227 if (x2 > 608) 228 x2 = 608; 229 230 // Y1 231 232 if (pattMark.markY1 < currRow) 233 { 234 y1 = pattYStart + ((pattMark.markY1 - startRow) * rowHeight); 235 if (y1 < pattYStart) 236 y1 = pattYStart; 237 } 238 else if (pattMark.markY1 == currRow) 239 { 240 y1 = markCoord->midRowY; 241 } 242 else 243 { 244 y1 = markCoord->lowerRowsY + ((pattMark.markY1 - (currRow + 1)) * rowHeight); 245 } 246 247 // Y2 248 249 if (pattMark.markY2-1 < currRow) 250 { 251 y2 = pattYStart + ((pattMark.markY2 - startRow) * rowHeight); 252 } 253 else if (pattMark.markY2-1 == currRow) 254 { 255 y2 = markCoord->midRowY + 11; 256 } 257 else 258 { 259 const int32_t pattYEnd = markCoord->lowerRowsY + (pattCoord->numLowerRows * rowHeight); 260 261 y2 = markCoord->lowerRowsY + ((pattMark.markY2 - (currRow + 1)) * rowHeight); 262 if (y2 > pattYEnd) 263 y2 = pattYEnd; 264 } 265 266 // kludge! (some mark situations could overwrite illegal areas) 267 if (config.ptnStretch && ui.pattChanScrollShown) 268 { 269 if (y1 == pattCoord->upperRowsY-1 || y1 == pattCoord->lowerRowsY-1) 270 y1++; 271 272 if (y2 == 384) 273 y2--; 274 275 // this can actually happen here, don't render in that case 276 if (y1 >= y2) 277 return; 278 } 279 280 assert(x1 > 0 && x1 < SCREEN_W && x2 > 0 && x2 < SCREEN_W && 281 y1 > 0 && y1 < SCREEN_H && y2 > 0 && y2 < SCREEN_H); 282 283 // pattern mark drawing 284 285 const int32_t w = x2 - x1; 286 const int32_t h = y2 - y1; 287 288 assert(x1+w <= SCREEN_W && y1+h <= SCREEN_H); 289 290 uint32_t *ptr32 = &video.frameBuffer[(y1 * SCREEN_W) + x1]; 291 for (int32_t y = 0; y < h; y++) 292 { 293 for (int32_t x = 0; x < w; x++) 294 ptr32[x] = video.palette[(ptr32[x] >> 24) ^ 2]; // ">> 24" to get palette of pixel, XOR 2 to change to mark palette 295 296 ptr32 += SCREEN_W; 297 } 298 } 299 300 static void drawChannelNumbering(uint16_t yPos) 301 { 302 uint16_t xPos = 30; 303 uint8_t ch = ui.channelOffset + 1; 304 305 for (uint8_t i = 0; i < ui.numChannelsShown; i++) 306 { 307 if (ch < 10) 308 { 309 charOutOutlined(xPos, yPos, PAL_MOUSEPT, '0' + (char)ch); 310 } 311 else 312 { 313 charOutOutlined(xPos, yPos, PAL_MOUSEPT, '0' + (ch / 10)); 314 charOutOutlined(xPos + (FONT1_CHAR_W + 1), yPos, PAL_MOUSEPT, '0' + (ch % 10)); 315 } 316 317 ch++; 318 xPos += ui.patternChannelWidth; 319 } 320 } 321 322 static void drawRowNums(int32_t yPos, uint8_t row, bool selectedRowFlag) 323 { 324 #define LEFT_ROW_XPOS 8 325 #define RIGHT_ROW_XPOS 608 326 327 uint32_t pixVal; 328 329 // set color based on some conditions 330 if (selectedRowFlag) 331 pixVal = video.palette[PAL_FORGRND]; 332 else if (config.ptnLineLight && !(row & 3)) 333 pixVal = video.palette[PAL_BLCKTXT]; 334 else 335 pixVal = video.palette[PAL_PATTEXT]; 336 337 if (!config.ptnHex) 338 row = hex2Dec[row]; 339 340 const uint8_t *src1Ptr = &font4Ptr[(row >> 4) * FONT4_CHAR_W]; 341 const uint8_t *src2Ptr = &font4Ptr[(row & 0x0F) * FONT4_CHAR_W]; 342 uint32_t *dst1Ptr = &video.frameBuffer[(yPos * SCREEN_W) + LEFT_ROW_XPOS]; 343 uint32_t *dst2Ptr = dst1Ptr + (RIGHT_ROW_XPOS - LEFT_ROW_XPOS); 344 345 for (int32_t y = 0; y < FONT4_CHAR_H; y++) 346 { 347 for (int32_t x = 0; x < FONT4_CHAR_W; x++) 348 { 349 if (src1Ptr[x]) 350 { 351 dst1Ptr[x] = pixVal; // left side 352 dst2Ptr[x] = pixVal; // right side 353 } 354 355 if (src2Ptr[x]) 356 { 357 dst1Ptr[FONT4_CHAR_W+x] = pixVal; // left side 358 dst2Ptr[FONT4_CHAR_W+x] = pixVal; // right side 359 } 360 } 361 362 src1Ptr += FONT4_WIDTH; 363 src2Ptr += FONT4_WIDTH; 364 dst1Ptr += SCREEN_W; 365 dst2Ptr += SCREEN_W; 366 } 367 } 368 369 // DRAWING ROUTINES (WITH VOLUME COLUMN) 370 371 static void showNoteNum(uint32_t xPos, uint32_t yPos, int16_t note, uint32_t color) 372 { 373 xPos += 3; 374 375 assert(note >= 0 && note <= 97); 376 377 if (ui.numChannelsShown <= 4) 378 { 379 if (note <= 0 || note > 97) 380 drawEmptyNoteBig(xPos, yPos, color); 381 else if (note == NOTE_OFF) 382 drawKeyOffBig(xPos, yPos, color); 383 else 384 drawNoteBig(xPos, yPos, note, color); 385 } 386 else 387 { 388 if (note <= 0 || note > 97) 389 drawEmptyNoteMedium(xPos, yPos, color); 390 else if (note == NOTE_OFF) 391 drawKeyOffMedium(xPos, yPos, color); 392 else 393 drawNoteMedium(xPos, yPos, note, color); 394 } 395 } 396 397 static void showInstrNum(uint32_t xPos, uint32_t yPos, uint8_t ins, uint32_t color) 398 { 399 uint8_t charW, fontType; 400 401 if (ui.numChannelsShown <= 4) 402 { 403 fontType = FONT_TYPE4; 404 charW = FONT4_CHAR_W; 405 xPos += 67; 406 } 407 else if (ui.numChannelsShown <= 6) 408 { 409 fontType = FONT_TYPE4; 410 charW = FONT4_CHAR_W; 411 xPos += 27; 412 } 413 else 414 { 415 fontType = FONT_TYPE3; 416 charW = FONT3_CHAR_W; 417 xPos += 31; 418 } 419 420 if (config.ptnInstrZero) 421 { 422 pattCharOut(xPos, yPos, ins >> 4, fontType, color); 423 pattCharOut(xPos + charW, yPos, ins & 0x0F, fontType, color); 424 } 425 else 426 { 427 const uint8_t chr1 = ins >> 4; 428 const uint8_t chr2 = ins & 0x0F; 429 430 if (chr1 > 0) 431 pattCharOut(xPos, yPos, chr1, fontType, color); 432 433 if (chr1 > 0 || chr2 > 0) 434 pattCharOut(xPos + charW, yPos, chr2, fontType, color); 435 } 436 } 437 438 static void showVolEfx(uint32_t xPos, uint32_t yPos, uint8_t vol, uint32_t color) 439 { 440 uint8_t char1, char2, fontType, charW; 441 442 if (ui.numChannelsShown <= 4) 443 { 444 fontType = FONT_TYPE4; 445 charW = FONT4_CHAR_W; 446 xPos += 91; 447 448 char1 = vol2charTab1[vol >> 4]; 449 if (vol < 0x10) 450 char2 = 39; 451 else 452 char2 = vol & 0x0F; 453 } 454 else if (ui.numChannelsShown <= 6) 455 { 456 fontType = FONT_TYPE4; 457 charW = FONT4_CHAR_W; 458 xPos += 51; 459 460 char1 = vol2charTab1[vol >> 4]; 461 if (vol < 0x10) 462 char2 = 39; 463 else 464 char2 = vol & 0x0F; 465 } 466 else 467 { 468 fontType = FONT_TYPE3; 469 charW = FONT3_CHAR_W; 470 xPos += 43; 471 472 char1 = vol2charTab2[vol >> 4]; 473 if (vol < 0x10) 474 char2 = 42; 475 else 476 char2 = vol & 0x0F; 477 } 478 479 pattCharOut(xPos, yPos, char1, fontType, color); 480 pattCharOut(xPos + charW, yPos, char2, fontType, color); 481 } 482 483 static void showEfx(uint32_t xPos, uint32_t yPos, uint8_t efx, uint8_t efxData, uint32_t color) 484 { 485 uint8_t fontType, charW; 486 487 if (ui.numChannelsShown <= 4) 488 { 489 fontType = FONT_TYPE4; 490 charW = FONT4_CHAR_W; 491 xPos += 115; 492 } 493 else if (ui.numChannelsShown <= 6) 494 { 495 fontType = FONT_TYPE4; 496 charW = FONT4_CHAR_W; 497 xPos += 67; 498 } 499 else 500 { 501 fontType = FONT_TYPE3; 502 charW = FONT3_CHAR_W; 503 xPos += 55; 504 } 505 506 pattCharOut(xPos, yPos, efx, fontType, color); 507 pattCharOut(xPos + charW, yPos, efxData >> 4, fontType, color); 508 pattCharOut(xPos + (charW * 2), yPos, efxData & 0x0F, fontType, color); 509 } 510 511 // DRAWING ROUTINES (WITHOUT VOLUME COLUMN) 512 513 static void showNoteNumNoVolColumn(uint32_t xPos, uint32_t yPos, int16_t note, uint32_t color) 514 { 515 xPos += 3; 516 517 assert(note >= 0 && note <= 97); 518 519 if (ui.numChannelsShown <= 6) 520 { 521 if (note <= 0 || note > 97) 522 drawEmptyNoteBig(xPos, yPos, color); 523 else if (note == NOTE_OFF) 524 drawKeyOffBig(xPos, yPos, color); 525 else 526 drawNoteBig(xPos, yPos, note, color); 527 } 528 else if (ui.numChannelsShown <= 8) 529 { 530 if (note <= 0 || note > 97) 531 drawEmptyNoteMedium(xPos, yPos, color); 532 else if (note == NOTE_OFF) 533 drawKeyOffMedium(xPos, yPos, color); 534 else 535 drawNoteMedium(xPos, yPos, note, color); 536 } 537 else 538 { 539 if (note <= 0 || note > 97) 540 drawEmptyNoteSmall(xPos, yPos, color); 541 else if (note == NOTE_OFF) 542 drawKeyOffSmall(xPos, yPos, color); 543 else 544 drawNoteSmall(xPos, yPos, note, color); 545 } 546 } 547 548 static void showInstrNumNoVolColumn(uint32_t xPos, uint32_t yPos, uint8_t ins, uint32_t color) 549 { 550 uint8_t charW, fontType; 551 552 if (ui.numChannelsShown <= 4) 553 { 554 fontType = FONT_TYPE5; 555 charW = FONT5_CHAR_W; 556 xPos += 59; 557 } 558 else if (ui.numChannelsShown <= 6) 559 { 560 fontType = FONT_TYPE4; 561 charW = FONT4_CHAR_W; 562 xPos += 51; 563 } 564 else if (ui.numChannelsShown <= 8) 565 { 566 fontType = FONT_TYPE4; 567 charW = FONT4_CHAR_W; 568 xPos += 27; 569 } 570 else 571 { 572 fontType = FONT_TYPE3; 573 charW = FONT3_CHAR_W; 574 xPos += 23; 575 } 576 577 if (config.ptnInstrZero) 578 { 579 pattCharOut(xPos, yPos, ins >> 4, fontType, color); 580 pattCharOut(xPos + charW, yPos, ins & 0x0F, fontType, color); 581 } 582 else 583 { 584 const uint8_t chr1 = ins >> 4; 585 const uint8_t chr2 = ins & 0x0F; 586 587 if (chr1 > 0) 588 pattCharOut(xPos, yPos, chr1, fontType, color); 589 590 if (chr1 > 0 || chr2 > 0) 591 pattCharOut(xPos + charW, yPos, chr2, fontType, color); 592 } 593 } 594 595 static void showNoVolEfx(uint32_t xPos, uint32_t yPos, uint8_t vol, uint32_t color) 596 { 597 (void)xPos; 598 (void)yPos; 599 (void)vol; 600 (void)color; 601 } 602 603 static void showEfxNoVolColumn(uint32_t xPos, uint32_t yPos, uint8_t efx, uint8_t efxData, uint32_t color) 604 { 605 uint8_t charW, fontType; 606 607 if (ui.numChannelsShown <= 4) 608 { 609 fontType = FONT_TYPE5; 610 charW = FONT5_CHAR_W; 611 xPos += 91; 612 } 613 else if (ui.numChannelsShown <= 6) 614 { 615 fontType = FONT_TYPE4; 616 charW = FONT4_CHAR_W; 617 xPos += 67; 618 } 619 else if (ui.numChannelsShown <= 8) 620 { 621 fontType = FONT_TYPE4; 622 charW = FONT4_CHAR_W; 623 xPos += 43; 624 } 625 else 626 { 627 fontType = FONT_TYPE3; 628 charW = FONT3_CHAR_W; 629 xPos += 31; 630 } 631 632 pattCharOut(xPos, yPos, efx, fontType, color); 633 pattCharOut(xPos + charW, yPos, efxData >> 4, fontType, color); 634 pattCharOut(xPos + (charW * 2), yPos, efxData & 0x0F, fontType, color); 635 } 636 637 void writePattern(int32_t currRow, int32_t currPattern) 638 { 639 uint32_t noteTextColors[2]; 640 641 void (*drawNote)(uint32_t, uint32_t, int16_t, uint32_t); 642 void (*drawInst)(uint32_t, uint32_t, uint8_t, uint32_t); 643 void (*drawVolEfx)(uint32_t, uint32_t, uint8_t, uint32_t); 644 void (*drawEfx)(uint32_t, uint32_t, uint8_t, uint8_t, uint32_t); 645 646 /* Draw pattern framework every time (erasing existing content). 647 ** FT2 doesn't do this. This is quite lazy and consumes more CPU 648 ** time than needed (overlapped drawing), but it makes the pattern 649 ** mark/cursor drawing MUCH simpler to implement... 650 */ 651 drawPatternBorders(); 652 653 // setup variables 654 655 uint32_t chans = ui.numChannelsShown; 656 if (chans > ui.maxVisibleChannels) 657 chans = ui.maxVisibleChannels; 658 659 assert(chans >= 2 && chans <= 12); 660 661 // get channel width 662 const uint32_t chanWidth = chanWidths[(chans / 2) - 1]; 663 ui.patternChannelWidth = (uint16_t)(chanWidth + 3); 664 665 // get heights/pos/rows depending on configuration 666 uint32_t rowHeight = config.ptnStretch ? 11 : 8; 667 const pattCoord_t *pattCoord = &pattCoordTable[config.ptnStretch][ui.pattChanScrollShown][ui.extendedPatternEditor]; 668 const pattCoord2_t *pattCoord2 = &pattCoord2Table[config.ptnStretch][ui.pattChanScrollShown][ui.extendedPatternEditor]; 669 const int32_t midRowTextY = pattCoord->midRowTextY; 670 const int32_t lowerRowsTextY = pattCoord->lowerRowsTextY; 671 int32_t row = currRow - pattCoord->numUpperRows; 672 const int32_t rowsOnScreen = pattCoord->numUpperRows + 1 + pattCoord->numLowerRows; 673 int32_t textY = pattCoord->upperRowsTextY; 674 const int32_t afterCurrRow = currRow + 1; 675 const int32_t numChannels = ui.numChannelsShown; 676 note_t *pattPtr = pattern[currPattern]; 677 const int32_t numRows = patternNumRows[currPattern]; 678 679 // increment pattern data pointer by horizontal scrollbar offset/channel 680 if (pattPtr != NULL) 681 pattPtr += ui.channelOffset; 682 683 // set up function pointers for drawing 684 if (config.ptnShowVolColumn) 685 { 686 drawNote = showNoteNum; 687 drawInst = showInstrNum; 688 drawVolEfx = showVolEfx; 689 drawEfx = showEfx; 690 } 691 else 692 { 693 drawNote = showNoteNumNoVolColumn; 694 drawInst = showInstrNumNoVolColumn; 695 drawVolEfx = showNoVolEfx; 696 drawEfx = showEfxNoVolColumn; 697 } 698 699 noteTextColors[0] = video.palette[PAL_PATTEXT]; // not selected 700 noteTextColors[1] = video.palette[PAL_FORGRND]; // selected 701 702 // draw pattern data 703 for (int32_t i = 0; i < rowsOnScreen; i++) 704 { 705 if (row >= 0) 706 { 707 const bool selectedRowFlag = (row == currRow); 708 709 drawRowNums(textY, (uint8_t)row, selectedRowFlag); 710 711 const note_t *p = (pattPtr == NULL) ? emptyPattern : &pattPtr[(uint32_t)row * MAX_CHANNELS]; 712 const int32_t xWidth = ui.patternChannelWidth; 713 const uint32_t color = noteTextColors[selectedRowFlag]; 714 715 int32_t xPos = 29; 716 for (int32_t j = 0; j < numChannels; j++, p++, xPos += xWidth) 717 { 718 drawNote(xPos, textY, p->note, color); 719 drawInst(xPos, textY, p->instr, color); 720 drawVolEfx(xPos, textY, p->vol, color); 721 drawEfx(xPos, textY, p->efx, p->efxData, color); 722 } 723 } 724 725 // next row 726 if (++row >= numRows) 727 break; 728 729 // adjust textY position 730 if (row == currRow) 731 textY = midRowTextY; 732 else if (row == afterCurrRow) 733 textY = lowerRowsTextY; 734 else 735 textY += rowHeight; 736 } 737 738 writeCursor(); 739 740 // draw pattern marking (if anything is marked) 741 if (pattMark.markY1 != pattMark.markY2) 742 writePatternBlockMark(currRow, rowHeight, pattCoord); 743 744 // channel numbers must be drawn lastly 745 if (config.ptnChnNumbers) 746 drawChannelNumbering(pattCoord2->upperRowsY+2); 747 } 748 749 // ========== CHARACTER DRAWING ROUTINES FOR PATTERN EDITOR ========== 750 751 void pattTwoHexOut(uint32_t xPos, uint32_t yPos, uint8_t val, uint32_t color) 752 { 753 const uint8_t *ch1Ptr = &font4Ptr[(val >> 4) * FONT4_CHAR_W]; 754 const uint8_t *ch2Ptr = &font4Ptr[(val & 0x0F) * FONT4_CHAR_W]; 755 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 756 757 for (int32_t y = 0; y < FONT4_CHAR_H; y++) 758 { 759 for (int32_t x = 0; x < FONT4_CHAR_W; x++) 760 { 761 if (ch1Ptr[x] != 0) dstPtr[x] = color; 762 if (ch2Ptr[x] != 0) dstPtr[FONT4_CHAR_W+x] = color; 763 } 764 765 ch1Ptr += FONT4_WIDTH; 766 ch2Ptr += FONT4_WIDTH; 767 dstPtr += SCREEN_W; 768 } 769 } 770 771 static void pattCharOut(uint32_t xPos, uint32_t yPos, uint8_t chr, uint8_t fontType, uint32_t color) 772 { 773 const uint8_t *srcPtr; 774 int32_t x, y; 775 776 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 777 778 if (fontType == FONT_TYPE3) 779 { 780 srcPtr = &bmp.font3[chr * FONT3_CHAR_W]; 781 for (y = 0; y < FONT3_CHAR_H; y++) 782 { 783 for (x = 0; x < FONT3_CHAR_W; x++) 784 { 785 if (srcPtr[x] != 0) 786 dstPtr[x] = color; 787 } 788 789 srcPtr += FONT3_WIDTH; 790 dstPtr += SCREEN_W; 791 } 792 } 793 else if (fontType == FONT_TYPE4) 794 { 795 srcPtr = &font4Ptr[chr * FONT4_CHAR_W]; 796 for (y = 0; y < FONT4_CHAR_H; y++) 797 { 798 for (x = 0; x < FONT4_CHAR_W; x++) 799 { 800 if (srcPtr[x] != 0) 801 dstPtr[x] = color; 802 } 803 804 srcPtr += FONT4_WIDTH; 805 dstPtr += SCREEN_W; 806 } 807 } 808 else if (fontType == FONT_TYPE5) 809 { 810 srcPtr = &font5Ptr[chr * FONT5_CHAR_W]; 811 for (y = 0; y < FONT5_CHAR_H; y++) 812 { 813 for (x = 0; x < FONT5_CHAR_W; x++) 814 { 815 if (srcPtr[x] != 0) 816 dstPtr[x] = color; 817 } 818 819 srcPtr += FONT5_WIDTH; 820 dstPtr += SCREEN_W; 821 } 822 } 823 else 824 { 825 srcPtr = &bmp.font7[chr * FONT7_CHAR_W]; 826 for (y = 0; y < FONT7_CHAR_H; y++) 827 { 828 for (x = 0; x < FONT7_CHAR_W; x++) 829 { 830 if (srcPtr[x] != 0) 831 dstPtr[x] = color; 832 } 833 834 srcPtr += FONT7_WIDTH; 835 dstPtr += SCREEN_W; 836 } 837 } 838 } 839 840 static void drawEmptyNoteSmall(uint32_t xPos, uint32_t yPos, uint32_t color) 841 { 842 const uint8_t *srcPtr = &bmp.font7[18 * FONT7_CHAR_W]; 843 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 844 845 for (int32_t y = 0; y < FONT7_CHAR_H; y++) 846 { 847 for (int32_t x = 0; x < FONT7_CHAR_W*3; x++) 848 { 849 if (srcPtr[x] != 0) 850 dstPtr[x] = color; 851 } 852 853 srcPtr += FONT7_WIDTH; 854 dstPtr += SCREEN_W; 855 } 856 } 857 858 static void drawKeyOffSmall(uint32_t xPos, uint32_t yPos, uint32_t color) 859 { 860 const uint8_t *srcPtr = &bmp.font7[21 * FONT7_CHAR_W]; 861 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + (xPos + 2)]; 862 863 for (int32_t y = 0; y < FONT7_CHAR_H; y++) 864 { 865 for (int32_t x = 0; x < FONT7_CHAR_W*2; x++) 866 { 867 if (srcPtr[x] != 0) 868 dstPtr[x] = color; 869 } 870 871 srcPtr += FONT7_WIDTH; 872 dstPtr += SCREEN_W; 873 } 874 } 875 876 static void drawNoteSmall(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color) 877 { 878 uint32_t char1, char2; 879 880 noteNum--; 881 882 const uint8_t note = noteTab1[noteNum]; 883 const uint32_t char3 = noteTab2[noteNum] * FONT7_CHAR_W; 884 885 if (config.ptnAcc == 0) 886 { 887 char1 = sharpNote1Char_small[note]; 888 char2 = sharpNote2Char_small[note]; 889 } 890 else 891 { 892 char1 = flatNote1Char_small[note]; 893 char2 = flatNote2Char_small[note]; 894 } 895 896 const uint8_t *ch1Ptr = &bmp.font7[char1]; 897 const uint8_t *ch2Ptr = &bmp.font7[char2]; 898 const uint8_t *ch3Ptr = &bmp.font7[char3]; 899 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 900 901 for (int32_t y = 0; y < FONT7_CHAR_H; y++) 902 { 903 for (int32_t x = 0; x < FONT7_CHAR_W; x++) 904 { 905 if (ch1Ptr[x] != 0) dstPtr[x] = color; 906 if (ch2Ptr[x] != 0) dstPtr[FONT7_CHAR_W+x] = color; 907 if (ch3Ptr[x] != 0) dstPtr[((FONT7_CHAR_W*2)-2)+x] = color; 908 } 909 910 ch1Ptr += FONT7_WIDTH; 911 ch2Ptr += FONT7_WIDTH; 912 ch3Ptr += FONT7_WIDTH; 913 dstPtr += SCREEN_W; 914 } 915 } 916 917 static void drawEmptyNoteMedium(uint32_t xPos, uint32_t yPos, uint32_t color) 918 { 919 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 920 const uint8_t *srcPtr = &font4Ptr[43 * FONT4_CHAR_W]; 921 922 for (int32_t y = 0; y < FONT4_CHAR_H; y++) 923 { 924 for (int32_t x = 0; x < FONT4_CHAR_W*3; x++) 925 { 926 if (srcPtr[x] != 0) 927 dstPtr[x] = color; 928 } 929 930 srcPtr += FONT4_WIDTH; 931 dstPtr += SCREEN_W; 932 } 933 } 934 935 static void drawKeyOffMedium(uint32_t xPos, uint32_t yPos, uint32_t color) 936 { 937 const uint8_t *srcPtr = &font4Ptr[40 * FONT4_CHAR_W]; 938 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 939 940 for (int32_t y = 0; y < FONT4_CHAR_H; y++) 941 { 942 for (int32_t x = 0; x < FONT4_CHAR_W*3; x++) 943 { 944 if (srcPtr[x] != 0) 945 dstPtr[x] = color; 946 } 947 948 srcPtr += FONT4_WIDTH; 949 dstPtr += SCREEN_W; 950 } 951 } 952 953 static void drawNoteMedium(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color) 954 { 955 uint32_t char1, char2; 956 957 noteNum--; 958 959 const uint8_t note = noteTab1[noteNum]; 960 const uint32_t char3 = noteTab2[noteNum] * FONT4_CHAR_W; 961 962 if (config.ptnAcc == 0) 963 { 964 char1 = sharpNote1Char_med[note]; 965 char2 = sharpNote2Char_med[note]; 966 } 967 else 968 { 969 char1 = flatNote1Char_med[note]; 970 char2 = flatNote2Char_med[note]; 971 } 972 973 const uint8_t *ch1Ptr = &font4Ptr[char1]; 974 const uint8_t *ch2Ptr = &font4Ptr[char2]; 975 const uint8_t *ch3Ptr = &font4Ptr[char3]; 976 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 977 978 for (int32_t y = 0; y < FONT4_CHAR_H; y++) 979 { 980 for (int32_t x = 0; x < FONT4_CHAR_W; x++) 981 { 982 if (ch1Ptr[x] != 0) dstPtr[x] = color; 983 if (ch2Ptr[x] != 0) dstPtr[FONT4_CHAR_W+x] = color; 984 if (ch3Ptr[x] != 0) dstPtr[(FONT4_CHAR_W*2)+x] = color; 985 } 986 987 ch1Ptr += FONT4_WIDTH; 988 ch2Ptr += FONT4_WIDTH; 989 ch3Ptr += FONT4_WIDTH; 990 dstPtr += SCREEN_W; 991 } 992 } 993 994 static void drawEmptyNoteBig(uint32_t xPos, uint32_t yPos, uint32_t color) 995 { 996 const uint8_t *srcPtr = &font4Ptr[67 * FONT4_CHAR_W]; 997 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 998 999 for (int32_t y = 0; y < FONT4_CHAR_H; y++) 1000 { 1001 for (int32_t x = 0; x < FONT4_CHAR_W*6; x++) 1002 { 1003 if (srcPtr[x] != 0) 1004 dstPtr[x] = color; 1005 } 1006 1007 srcPtr += FONT4_WIDTH; 1008 dstPtr += SCREEN_W; 1009 } 1010 } 1011 1012 static void drawKeyOffBig(uint32_t xPos, uint32_t yPos, uint32_t color) 1013 { 1014 const uint8_t *srcPtr = &bmp.font4[61 * FONT4_CHAR_W]; 1015 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 1016 1017 for (int32_t y = 0; y < FONT4_CHAR_H; y++) 1018 { 1019 for (int32_t x = 0; x < FONT4_CHAR_W*6; x++) 1020 { 1021 if (srcPtr[x] != 0) 1022 dstPtr[x] = color; 1023 } 1024 1025 srcPtr += FONT4_WIDTH; 1026 dstPtr += SCREEN_W; 1027 } 1028 } 1029 1030 static void drawNoteBig(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color) 1031 { 1032 uint32_t char1, char2; 1033 1034 noteNum--; 1035 1036 const uint8_t note = noteTab1[noteNum]; 1037 const uint32_t char3 = noteTab2[noteNum] * FONT5_CHAR_W; 1038 1039 if (config.ptnAcc == 0) 1040 { 1041 char1 = sharpNote1Char_big[note]; 1042 char2 = sharpNote2Char_big[note]; 1043 } 1044 else 1045 { 1046 char1 = flatNote1Char_big[note]; 1047 char2 = flatNote2Char_big[note]; 1048 } 1049 1050 const uint8_t *ch1Ptr = &font5Ptr[char1]; 1051 const uint8_t *ch2Ptr = &font5Ptr[char2]; 1052 const uint8_t *ch3Ptr = &font5Ptr[char3]; 1053 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 1054 1055 for (int32_t y = 0; y < FONT5_CHAR_H; y++) 1056 { 1057 for (int32_t x = 0; x < FONT5_CHAR_W; x++) 1058 { 1059 if (ch1Ptr[x] != 0) dstPtr[x] = color; 1060 if (ch2Ptr[x] != 0) dstPtr[FONT5_CHAR_W+x] = color; 1061 if (ch3Ptr[x] != 0) dstPtr[(FONT5_CHAR_W*2)+x] = color; 1062 } 1063 1064 ch1Ptr += FONT5_WIDTH; 1065 ch2Ptr += FONT5_WIDTH; 1066 ch3Ptr += FONT5_WIDTH; 1067 dstPtr += SCREEN_W; 1068 } 1069 }