// for finding memory leaks in debug mode with Visual Studio#if defined _DEBUG && defined _MSC_VER#include <crtdbg.h>#endif #include <stdint.h>#include <time.h>#include "ft2_header.h"#include "ft2_config.h"#include "ft2_about.h"#include "ft2_mouse.h"#include "ft2_nibbles.h"#include "ft2_gui.h"#include "ft2_pattern_ed.h"#include "scopes/ft2_scopes.h"#include "ft2_help.h"#include "ft2_sample_ed.h"#include "ft2_inst_ed.h"#include "ft2_diskop.h"#include "ft2_wav_renderer.h"#include "ft2_trim.h"#include "ft2_video.h"#include "ft2_tables.h"#include "ft2_bmp.h"#include "ft2_structs.h" static void releaseMouseStates(void){ mouse.lastUsedObjectID = OBJECT_ID_NONE; mouse.lastUsedObjectType = OBJECT_NONE; mouse.leftButtonPressed = false; mouse.leftButtonReleased = false; mouse.rightButtonPressed = false; mouse.rightButtonReleased = false; mouse.firstTimePressingButton = false; mouse.buttonCounter = 0; mouse.lastX = 0; mouse.lastY = 0; ui.sampleDataOrLoopDrag = -1; ui.leftLoopPinMoving = false; ui.rightLoopPinMoving = false;} void unstuckLastUsedGUIElement(void){ if (mouse.lastUsedObjectID == OBJECT_ID_NONE) { /* If last object ID is OBJECT_ID_NONE, check if we moved the ** sample data loop pins, and unstuck them if so */ if (ui.leftLoopPinMoving) { setLeftLoopPinState(false); ui.leftLoopPinMoving = false; } if (ui.rightLoopPinMoving) { setRightLoopPinState(false); ui.rightLoopPinMoving = false; } releaseMouseStates(); return; } switch (mouse.lastUsedObjectType) { default: break; case OBJECT_PUSHBUTTON: { assert(mouse.lastUsedObjectID >= 0 && mouse.lastUsedObjectID < NUM_PUSHBUTTONS); pushButton_t *p = &pushButtons[mouse.lastUsedObjectID]; if (p->state == PUSHBUTTON_PRESSED) { p->state = PUSHBUTTON_UNPRESSED; if (p->visible) drawPushButton(mouse.lastUsedObjectID); } } break; case OBJECT_RADIOBUTTON: { assert(mouse.lastUsedObjectID >= 0 && mouse.lastUsedObjectID < NUM_RADIOBUTTONS); radioButton_t *r = &radioButtons[mouse.lastUsedObjectID]; if (r->state == RADIOBUTTON_PRESSED) { r->state = RADIOBUTTON_UNCHECKED; if (r->visible) drawRadioButton(mouse.lastUsedObjectID); } } break; case OBJECT_CHECKBOX: { assert(mouse.lastUsedObjectID >= 0 && mouse.lastUsedObjectID < NUM_CHECKBOXES); checkBox_t *c = &checkBoxes[mouse.lastUsedObjectID]; if (c->state == CHECKBOX_PRESSED) { c->state = CHECKBOX_UNPRESSED; if (c->visible) drawCheckBox(mouse.lastUsedObjectID); } } break; case OBJECT_SCROLLBAR: { assert(mouse.lastUsedObjectID >= 0 && mouse.lastUsedObjectID < NUM_SCROLLBARS); scrollBar_t *s = &scrollBars[mouse.lastUsedObjectID]; if (s->state == SCROLLBAR_PRESSED) { s->state = SCROLLBAR_UNPRESSED; if (s->visible) drawScrollBar(mouse.lastUsedObjectID); } } break; } releaseMouseStates();} bool setupGUI(void){ // all memory will be NULL-tested and free'd if we return false somewhere in this function editor.tmpFilenameU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR)); editor.tmpInstrFilenameU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR)); if (editor.tmpFilenameU == NULL || editor.tmpInstrFilenameU == NULL) goto setupGUI_OOM; editor.tmpFilenameU[0] = 0; editor.tmpInstrFilenameU[0] = 0; // set uninitialized GUI struct entries textBox_t *t = &textBoxes[1]; // skip first entry, it's reserved for inputBox()) for (int32_t i = 1; i < NUM_TEXTBOXES; i++, t++) { t->visible = false; t->bufOffset = 0; t->cursorPos = 0; t->textPtr = NULL; t->renderBufW = (9 + 1) * t->maxChars; // 9 = max character/glyph width possible t->renderBufH = 10; // 10 = max character height possible t->renderW = t->w - (t->tx * 2); t->renderBuf = (uint8_t *)malloc(t->renderBufW * t->renderBufH * sizeof (int8_t)); if (t->renderBuf == NULL) goto setupGUI_OOM; } pushButton_t *p = pushButtons; for (int32_t i = 0; i < NUM_PUSHBUTTONS; i++, p++) { p->state = 0; p->visible = false; if (i == PB_LOGO || i == PB_BADGE) { p->bitmapFlag = true; } else { p->bitmapFlag = false; p->bitmapUnpressed = NULL; p->bitmapPressed = NULL; } } checkBox_t *c = checkBoxes; for (int32_t i = 0; i < NUM_CHECKBOXES; i++, c++) { c->state = 0; c->checked = false; c->visible = false; } radioButton_t *r = radioButtons; for (int32_t i = 0; i < NUM_RADIOBUTTONS; i++, r++) { r->state = 0; r->visible = false; } scrollBar_t *s = scrollBars; for (int32_t i = 0; i < NUM_SCROLLBARS; i++, s++) { s->visible = false; s->state = 0; s->pos = 0; s->page = 0; s->end = 0; s->thumbX = 0; s->thumbY = 0; s->thumbW = 0; s->thumbH = 0; } setPal16(palTable[config.cfg_StdPalNum], false); seedAboutScreenRandom((uint32_t)time(NULL)); setupInitialTextBoxPointers(); setInitialTrimFlags(); initializeScrollBars(); setMouseMode(MOUSE_MODE_NORMAL); updateTextBoxPointers(); drawGUIOnRunTime(); updateSampleEditorSample(); updatePatternWidth(); initFTHelp(); return true; setupGUI_OOM: showErrorMsgBox("Not enough memory!"); return false;} // TEXT ROUTINES // returns full pixel width of a char/glyphuint8_t charWidth(char ch){ return font1Widths[ch & 0x7F];} // returns full pixel width of a char/glyph (big font)uint8_t charWidth16(char ch){ return font2Widths[ch & 0x7F];} // return full pixel width of a text stringuint16_t textWidth(const char *textPtr){ assert(textPtr != NULL); uint16_t textWidth = 0; while (*textPtr != '\0') textWidth += charWidth(*textPtr++); // there will be a pixel spacer at the end of the last char/glyph, remove it if (textWidth > 0) textWidth--; return textWidth;} uint16_t textNWidth(const char *textPtr, int32_t length){ assert(textPtr != NULL); uint16_t textWidth = 0; for (int32_t i = 0; i < length; i++) { const char ch = textPtr[i]; if (ch == '\0') break; textWidth += charWidth(ch); } // there will be a pixel spacer at the end of the last char/glyph, remove it if (textWidth > 0) textWidth--; return textWidth;} // return full pixel width of a text string (big font)uint16_t textWidth16(const char *textPtr){ assert(textPtr != NULL); uint16_t textWidth = 0; while (*textPtr != '\0') textWidth += charWidth(*textPtr++); // there will be a pixel spacer at the end of the last char/glyph, remove it if (textWidth > 0) textWidth--; return textWidth;} void textOutTiny(int32_t xPos, int32_t yPos, char *str, uint32_t color) // A..Z/a..z and 0..9{ uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; while (*str != '\0') { char chr = *str++; if (chr >= '0' && chr <= '9') { chr -= '0'; } else if (chr >= 'a' && chr <= 'z') { chr -= 'a'; chr += 10; } else if (chr >= 'A' && chr <= 'Z') { chr -= 'A'; chr += 10; } else { dstPtr += FONT3_CHAR_W; continue; } const uint8_t *srcPtr = &bmp.font3[chr * FONT3_CHAR_W]; for (int32_t y = 0; y < FONT3_CHAR_H; y++) { for (int32_t x = 0; x < FONT3_CHAR_W; x++) {#ifdef __arm__ if (srcPtr[x] != 0) dstPtr[x] = color;#else // carefully written like this to generate conditional move instructions (font data is hard to predict) uint32_t tmp = dstPtr[x]; if (srcPtr[x] != 0) tmp = color; dstPtr[x] = tmp;#endif } srcPtr += FONT3_WIDTH; dstPtr += SCREEN_W; } dstPtr -= (SCREEN_W * FONT3_CHAR_H) - FONT3_CHAR_W; }} void textOutTinyOutline(int32_t xPos, int32_t yPos, char *str) // A..Z/a..z and 0..9{ const uint32_t bgColor = video.palette[PAL_BCKGRND]; const uint32_t fgColor = video.palette[PAL_FORGRND]; textOutTiny(xPos-1, yPos, str, bgColor); textOutTiny(xPos, yPos-1, str, bgColor); textOutTiny(xPos+1, yPos, str, bgColor); textOutTiny(xPos, yPos+1, str, bgColor); textOutTiny(xPos, yPos, str, fgColor);} void charOut(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, char chr){ assert(xPos < SCREEN_W && yPos < SCREEN_H); chr &= 0x7F; // this is important to get the nordic glyphs in the font if (chr == ' ') return; const uint32_t pixVal = video.palette[paletteIndex]; const uint8_t *srcPtr = &bmp.font1[chr * FONT1_CHAR_W]; uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; for (uint32_t y = 0; y < FONT1_CHAR_H; y++) { for (uint32_t x = 0; x < FONT1_CHAR_W; x++) {#ifdef __arm__ if (srcPtr[x] != 0) dstPtr[x] = pixVal;#else // carefully written like this to generate conditional move instructions (font data is hard to predict) uint32_t tmp = dstPtr[x]; if (srcPtr[x] != 0) tmp = pixVal; dstPtr[x] = tmp;#endif } srcPtr += FONT1_WIDTH; dstPtr += SCREEN_W; }} void charOutAlpha(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, char chr, uint32_t alpha){ assert(xPos < SCREEN_W && yPos < SCREEN_H); chr &= 0x7F; // this is important to get the nordic glyphs in the font if (chr == ' ') return; if (alpha > 65536) alpha = 65536; const uint32_t pixVal = video.palette[paletteIndex]; const uint32_t palNum = paletteIndex << 24; const uint8_t *srcPtr = &bmp.font1[chr * FONT1_CHAR_W]; uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; for (int32_t y = 0; y < FONT1_CHAR_H; y++) { for (int32_t x = 0; x < FONT1_CHAR_W; x++) { const uint32_t srcPixel = srcPtr[x]; if (srcPixel == 0) continue; const uint32_t dstPixel = dstPtr[x]; const int32_t srcR = RGB32_R(pixVal); const int32_t srcG = RGB32_G(pixVal); const int32_t srcB = RGB32_B(pixVal); int32_t dstR = RGB32_R(dstPixel); int32_t dstG = RGB32_G(dstPixel); int32_t dstB = RGB32_B(dstPixel); dstR = ((dstR * (65536-alpha)) + (srcR * alpha)) >> 16; dstG = ((dstG * (65536-alpha)) + (srcG * alpha)) >> 16; dstB = ((dstB * (65536-alpha)) + (srcB * alpha)) >> 16; dstPtr[x] = RGB32(dstR, dstG, dstB) | palNum; } srcPtr += FONT1_WIDTH; dstPtr += SCREEN_W; }} void charOutBg(uint16_t xPos, uint16_t yPos, uint8_t fgPalette, uint8_t bgPalette, char chr){ assert(xPos < SCREEN_W && yPos < SCREEN_H); chr &= 0x7F; // this is important to get the nordic glyphs in the font if (chr == ' ') return; const uint32_t fg = video.palette[fgPalette]; const uint32_t bg = video.palette[bgPalette]; const uint8_t *srcPtr = &bmp.font1[chr * FONT1_CHAR_W]; uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; for (int32_t y = 0; y < FONT1_CHAR_H; y++) { for (int32_t x = 0; x < FONT1_CHAR_W-1; x++) dstPtr[x] = srcPtr[x] ? fg : bg; // compiles nicely into conditional move instructions srcPtr += FONT1_WIDTH; dstPtr += SCREEN_W; }} void charOutOutlined(uint16_t x, uint16_t y, uint8_t paletteIndex, char chr){ charOut(x - 1, y, PAL_BCKGRND, chr); charOut(x + 1, y, PAL_BCKGRND, chr); charOut(x, y - 1, PAL_BCKGRND, chr); charOut(x, y + 1, PAL_BCKGRND, chr); charOut(x, y, paletteIndex, chr);} void charOutShadow(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, uint8_t shadowPaletteIndex, char chr){ assert(xPos < SCREEN_W && yPos < SCREEN_H); chr &= 0x7F; // this is important to get the nordic glyphs in the font if (chr == ' ') return; const uint32_t pixVal1 = video.palette[paletteIndex]; const uint32_t pixVal2 = video.palette[shadowPaletteIndex]; const uint8_t *srcPtr = &bmp.font1[chr * FONT1_CHAR_W]; uint32_t *dstPtr1 = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; uint32_t *dstPtr2 = dstPtr1 + (SCREEN_W+1); for (int32_t y = 0; y < FONT1_CHAR_H; y++) { for (int32_t x = 0; x < FONT1_CHAR_W; x++) {#ifdef __arm__ if (srcPtr[x] != 0) { dstPtr2[x] = pixVal2; dstPtr1[x] = pixVal1; }#else // carefully written like this to generate conditional move instructions (font data is hard to predict) uint32_t tmp = dstPtr2[x]; if (srcPtr[x] != 0) tmp = pixVal2; dstPtr2[x] = tmp; tmp = dstPtr1[x]; if (srcPtr[x] != 0) tmp = pixVal1; dstPtr1[x] = tmp;Lines 1–500 of 1436. Load more
// for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER #include <crtdbg.h> #endif #include <stdint.h> #include <time.h> #include "ft2_header.h" #include "ft2_config.h" #include "ft2_about.h" #include "ft2_mouse.h" #include "ft2_nibbles.h" #include "ft2_gui.h" #include "ft2_pattern_ed.h" #include "scopes/ft2_scopes.h" #include "ft2_help.h" #include "ft2_sample_ed.h" #include "ft2_inst_ed.h" #include "ft2_diskop.h" #include "ft2_wav_renderer.h" #include "ft2_trim.h" #include "ft2_video.h" #include "ft2_tables.h" #include "ft2_bmp.h" #include "ft2_structs.h" static void releaseMouseStates(void) { mouse.lastUsedObjectID = OBJECT_ID_NONE; mouse.lastUsedObjectType = OBJECT_NONE; mouse.leftButtonPressed = false; mouse.leftButtonReleased = false; mouse.rightButtonPressed = false; mouse.rightButtonReleased = false; mouse.firstTimePressingButton = false; mouse.buttonCounter = 0; mouse.lastX = 0; mouse.lastY = 0; ui.sampleDataOrLoopDrag = -1; ui.leftLoopPinMoving = false; ui.rightLoopPinMoving = false; } void unstuckLastUsedGUIElement(void) { if (mouse.lastUsedObjectID == OBJECT_ID_NONE) { /* If last object ID is OBJECT_ID_NONE, check if we moved the ** sample data loop pins, and unstuck them if so */ if (ui.leftLoopPinMoving) { setLeftLoopPinState(false); ui.leftLoopPinMoving = false; } if (ui.rightLoopPinMoving) { setRightLoopPinState(false); ui.rightLoopPinMoving = false; } releaseMouseStates(); return; } switch (mouse.lastUsedObjectType) { default: break; case OBJECT_PUSHBUTTON: { assert(mouse.lastUsedObjectID >= 0 && mouse.lastUsedObjectID < NUM_PUSHBUTTONS); pushButton_t *p = &pushButtons[mouse.lastUsedObjectID]; if (p->state == PUSHBUTTON_PRESSED) { p->state = PUSHBUTTON_UNPRESSED; if (p->visible) drawPushButton(mouse.lastUsedObjectID); } } break; case OBJECT_RADIOBUTTON: { assert(mouse.lastUsedObjectID >= 0 && mouse.lastUsedObjectID < NUM_RADIOBUTTONS); radioButton_t *r = &radioButtons[mouse.lastUsedObjectID]; if (r->state == RADIOBUTTON_PRESSED) { r->state = RADIOBUTTON_UNCHECKED; if (r->visible) drawRadioButton(mouse.lastUsedObjectID); } } break; case OBJECT_CHECKBOX: { assert(mouse.lastUsedObjectID >= 0 && mouse.lastUsedObjectID < NUM_CHECKBOXES); checkBox_t *c = &checkBoxes[mouse.lastUsedObjectID]; if (c->state == CHECKBOX_PRESSED) { c->state = CHECKBOX_UNPRESSED; if (c->visible) drawCheckBox(mouse.lastUsedObjectID); } } break; case OBJECT_SCROLLBAR: { assert(mouse.lastUsedObjectID >= 0 && mouse.lastUsedObjectID < NUM_SCROLLBARS); scrollBar_t *s = &scrollBars[mouse.lastUsedObjectID]; if (s->state == SCROLLBAR_PRESSED) { s->state = SCROLLBAR_UNPRESSED; if (s->visible) drawScrollBar(mouse.lastUsedObjectID); } } break; } releaseMouseStates(); } bool setupGUI(void) { // all memory will be NULL-tested and free'd if we return false somewhere in this function editor.tmpFilenameU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR)); editor.tmpInstrFilenameU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR)); if (editor.tmpFilenameU == NULL || editor.tmpInstrFilenameU == NULL) goto setupGUI_OOM; editor.tmpFilenameU[0] = 0; editor.tmpInstrFilenameU[0] = 0; // set uninitialized GUI struct entries textBox_t *t = &textBoxes[1]; // skip first entry, it's reserved for inputBox()) for (int32_t i = 1; i < NUM_TEXTBOXES; i++, t++) { t->visible = false; t->bufOffset = 0; t->cursorPos = 0; t->textPtr = NULL; t->renderBufW = (9 + 1) * t->maxChars; // 9 = max character/glyph width possible t->renderBufH = 10; // 10 = max character height possible t->renderW = t->w - (t->tx * 2); t->renderBuf = (uint8_t *)malloc(t->renderBufW * t->renderBufH * sizeof (int8_t)); if (t->renderBuf == NULL) goto setupGUI_OOM; } pushButton_t *p = pushButtons; for (int32_t i = 0; i < NUM_PUSHBUTTONS; i++, p++) { p->state = 0; p->visible = false; if (i == PB_LOGO || i == PB_BADGE) { p->bitmapFlag = true; } else { p->bitmapFlag = false; p->bitmapUnpressed = NULL; p->bitmapPressed = NULL; } } checkBox_t *c = checkBoxes; for (int32_t i = 0; i < NUM_CHECKBOXES; i++, c++) { c->state = 0; c->checked = false; c->visible = false; } radioButton_t *r = radioButtons; for (int32_t i = 0; i < NUM_RADIOBUTTONS; i++, r++) { r->state = 0; r->visible = false; } scrollBar_t *s = scrollBars; for (int32_t i = 0; i < NUM_SCROLLBARS; i++, s++) { s->visible = false; s->state = 0; s->pos = 0; s->page = 0; s->end = 0; s->thumbX = 0; s->thumbY = 0; s->thumbW = 0; s->thumbH = 0; } setPal16(palTable[config.cfg_StdPalNum], false); seedAboutScreenRandom((uint32_t)time(NULL)); setupInitialTextBoxPointers(); setInitialTrimFlags(); initializeScrollBars(); setMouseMode(MOUSE_MODE_NORMAL); updateTextBoxPointers(); drawGUIOnRunTime(); updateSampleEditorSample(); updatePatternWidth(); initFTHelp(); return true; setupGUI_OOM: showErrorMsgBox("Not enough memory!"); return false; } // TEXT ROUTINES // returns full pixel width of a char/glyph uint8_t charWidth(char ch) { return font1Widths[ch & 0x7F]; } // returns full pixel width of a char/glyph (big font) uint8_t charWidth16(char ch) { return font2Widths[ch & 0x7F]; } // return full pixel width of a text string uint16_t textWidth(const char *textPtr) { assert(textPtr != NULL); uint16_t textWidth = 0; while (*textPtr != '\0') textWidth += charWidth(*textPtr++); // there will be a pixel spacer at the end of the last char/glyph, remove it if (textWidth > 0) textWidth--; return textWidth; } uint16_t textNWidth(const char *textPtr, int32_t length) { assert(textPtr != NULL); uint16_t textWidth = 0; for (int32_t i = 0; i < length; i++) { const char ch = textPtr[i]; if (ch == '\0') break; textWidth += charWidth(ch); } // there will be a pixel spacer at the end of the last char/glyph, remove it if (textWidth > 0) textWidth--; return textWidth; } // return full pixel width of a text string (big font) uint16_t textWidth16(const char *textPtr) { assert(textPtr != NULL); uint16_t textWidth = 0; while (*textPtr != '\0') textWidth += charWidth(*textPtr++); // there will be a pixel spacer at the end of the last char/glyph, remove it if (textWidth > 0) textWidth--; return textWidth; } void textOutTiny(int32_t xPos, int32_t yPos, char *str, uint32_t color) // A..Z/a..z and 0..9 { uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; while (*str != '\0') { char chr = *str++; if (chr >= '0' && chr <= '9') { chr -= '0'; } else if (chr >= 'a' && chr <= 'z') { chr -= 'a'; chr += 10; } else if (chr >= 'A' && chr <= 'Z') { chr -= 'A'; chr += 10; } else { dstPtr += FONT3_CHAR_W; continue; } const uint8_t *srcPtr = &bmp.font3[chr * FONT3_CHAR_W]; for (int32_t y = 0; y < FONT3_CHAR_H; y++) { for (int32_t x = 0; x < FONT3_CHAR_W; x++) { #ifdef __arm__ if (srcPtr[x] != 0) dstPtr[x] = color; #else // carefully written like this to generate conditional move instructions (font data is hard to predict) uint32_t tmp = dstPtr[x]; if (srcPtr[x] != 0) tmp = color; dstPtr[x] = tmp; #endif } srcPtr += FONT3_WIDTH; dstPtr += SCREEN_W; } dstPtr -= (SCREEN_W * FONT3_CHAR_H) - FONT3_CHAR_W; } } void textOutTinyOutline(int32_t xPos, int32_t yPos, char *str) // A..Z/a..z and 0..9 { const uint32_t bgColor = video.palette[PAL_BCKGRND]; const uint32_t fgColor = video.palette[PAL_FORGRND]; textOutTiny(xPos-1, yPos, str, bgColor); textOutTiny(xPos, yPos-1, str, bgColor); textOutTiny(xPos+1, yPos, str, bgColor); textOutTiny(xPos, yPos+1, str, bgColor); textOutTiny(xPos, yPos, str, fgColor); } void charOut(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, char chr) { assert(xPos < SCREEN_W && yPos < SCREEN_H); chr &= 0x7F; // this is important to get the nordic glyphs in the font if (chr == ' ') return; const uint32_t pixVal = video.palette[paletteIndex]; const uint8_t *srcPtr = &bmp.font1[chr * FONT1_CHAR_W]; uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; for (uint32_t y = 0; y < FONT1_CHAR_H; y++) { for (uint32_t x = 0; x < FONT1_CHAR_W; x++) { #ifdef __arm__ if (srcPtr[x] != 0) dstPtr[x] = pixVal; #else // carefully written like this to generate conditional move instructions (font data is hard to predict) uint32_t tmp = dstPtr[x]; if (srcPtr[x] != 0) tmp = pixVal; dstPtr[x] = tmp; #endif } srcPtr += FONT1_WIDTH; dstPtr += SCREEN_W; } } void charOutAlpha(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, char chr, uint32_t alpha) { assert(xPos < SCREEN_W && yPos < SCREEN_H); chr &= 0x7F; // this is important to get the nordic glyphs in the font if (chr == ' ') return; if (alpha > 65536) alpha = 65536; const uint32_t pixVal = video.palette[paletteIndex]; const uint32_t palNum = paletteIndex << 24; const uint8_t *srcPtr = &bmp.font1[chr * FONT1_CHAR_W]; uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; for (int32_t y = 0; y < FONT1_CHAR_H; y++) { for (int32_t x = 0; x < FONT1_CHAR_W; x++) { const uint32_t srcPixel = srcPtr[x]; if (srcPixel == 0) continue; const uint32_t dstPixel = dstPtr[x]; const int32_t srcR = RGB32_R(pixVal); const int32_t srcG = RGB32_G(pixVal); const int32_t srcB = RGB32_B(pixVal); int32_t dstR = RGB32_R(dstPixel); int32_t dstG = RGB32_G(dstPixel); int32_t dstB = RGB32_B(dstPixel); dstR = ((dstR * (65536-alpha)) + (srcR * alpha)) >> 16; dstG = ((dstG * (65536-alpha)) + (srcG * alpha)) >> 16; dstB = ((dstB * (65536-alpha)) + (srcB * alpha)) >> 16; dstPtr[x] = RGB32(dstR, dstG, dstB) | palNum; } srcPtr += FONT1_WIDTH; dstPtr += SCREEN_W; } } void charOutBg(uint16_t xPos, uint16_t yPos, uint8_t fgPalette, uint8_t bgPalette, char chr) { assert(xPos < SCREEN_W && yPos < SCREEN_H); chr &= 0x7F; // this is important to get the nordic glyphs in the font if (chr == ' ') return; const uint32_t fg = video.palette[fgPalette]; const uint32_t bg = video.palette[bgPalette]; const uint8_t *srcPtr = &bmp.font1[chr * FONT1_CHAR_W]; uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; for (int32_t y = 0; y < FONT1_CHAR_H; y++) { for (int32_t x = 0; x < FONT1_CHAR_W-1; x++) dstPtr[x] = srcPtr[x] ? fg : bg; // compiles nicely into conditional move instructions srcPtr += FONT1_WIDTH; dstPtr += SCREEN_W; } } void charOutOutlined(uint16_t x, uint16_t y, uint8_t paletteIndex, char chr) { charOut(x - 1, y, PAL_BCKGRND, chr); charOut(x + 1, y, PAL_BCKGRND, chr); charOut(x, y - 1, PAL_BCKGRND, chr); charOut(x, y + 1, PAL_BCKGRND, chr); charOut(x, y, paletteIndex, chr); } void charOutShadow(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, uint8_t shadowPaletteIndex, char chr) { assert(xPos < SCREEN_W && yPos < SCREEN_H); chr &= 0x7F; // this is important to get the nordic glyphs in the font if (chr == ' ') return; const uint32_t pixVal1 = video.palette[paletteIndex]; const uint32_t pixVal2 = video.palette[shadowPaletteIndex]; const uint8_t *srcPtr = &bmp.font1[chr * FONT1_CHAR_W]; uint32_t *dstPtr1 = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; uint32_t *dstPtr2 = dstPtr1 + (SCREEN_W+1); for (int32_t y = 0; y < FONT1_CHAR_H; y++) { for (int32_t x = 0; x < FONT1_CHAR_W; x++) { #ifdef __arm__ if (srcPtr[x] != 0) { dstPtr2[x] = pixVal2; dstPtr1[x] = pixVal1; } #else // carefully written like this to generate conditional move instructions (font data is hard to predict) uint32_t tmp = dstPtr2[x]; if (srcPtr[x] != 0) tmp = pixVal2; dstPtr2[x] = tmp; tmp = dstPtr1[x]; if (srcPtr[x] != 0) tmp = pixVal1; dstPtr1[x] = tmp;