From f0add944bcae021b9cc9c73f215a962a6c80f228 Mon Sep 17 00:00:00 2001 From: R Ferreira Date: Wed, 25 Feb 2026 20:46:45 +0000 Subject: [PATCH] Use sprites for sampledisplay controls Render loop handles and zoom buttons as sprites so the entire sample display doesn't need to be redrawn when manipulating them. Also make the zoom buttons a bit bigger since they were tiny. --- arm9/graphics/loophandle.grit | 1 + arm9/graphics/loophandle.png | Bin 0 -> 182 bytes arm9/graphics/sampleed_zoom.grit | 1 + arm9/graphics/sampleed_zoom.png | Bin 0 -> 233 bytes arm9/source/main.cpp | 30 +++- arm9/source/tobkit/sampledisplay.cpp | 252 ++++++++++++++------------- arm9/source/tobkit/sampledisplay.h | 29 ++- tobkit/include/tobkit/gui.h | 4 + tobkit/source/gui.cpp | 19 +- 9 files changed, 205 insertions(+), 131 deletions(-) create mode 100644 arm9/graphics/loophandle.grit create mode 100644 arm9/graphics/loophandle.png create mode 100644 arm9/graphics/sampleed_zoom.grit create mode 100644 arm9/graphics/sampleed_zoom.png diff --git a/arm9/graphics/loophandle.grit b/arm9/graphics/loophandle.grit new file mode 100644 index 0000000..eb37b34 --- /dev/null +++ b/arm9/graphics/loophandle.grit @@ -0,0 +1 @@ +-gt -gB4 -m! -p! -ftc diff --git a/arm9/graphics/loophandle.png b/arm9/graphics/loophandle.png new file mode 100644 index 0000000000000000000000000000000000000000..86135e417d8e7f63aaa6ecd6e7ca326613298ad7 GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^93afW3?x5a^xFxf7>k44ofvPP)Tsw@SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YhS zC&cwX!+#)?;VgriAKMNfL*CQHF@)oKasmUho14bjBP@$&CYT8Kq%~diF_1dQz`*{U U!OwmdKI;Vst0F#L=EC2ui literal 0 HcmV?d00001 diff --git a/arm9/graphics/sampleed_zoom.grit b/arm9/graphics/sampleed_zoom.grit new file mode 100644 index 0000000..eb37b34 --- /dev/null +++ b/arm9/graphics/sampleed_zoom.grit @@ -0,0 +1 @@ +-gt -gB4 -m! -p! -ftc diff --git a/arm9/graphics/sampleed_zoom.png b/arm9/graphics/sampleed_zoom.png new file mode 100644 index 0000000000000000000000000000000000000000..16aa809371291d62cbe2c8cda6c3bdaf87eb9a5f GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^3P5bY!3-qbi#lR~6k~CayA$KhlREW44okYDuOkD) z#(wTUiL5}rLb6AYF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZOn1 z_=LFrXZR0f&R-_-2}p%{x;TbdoK8+iXkkis_occluded()) oamEnable(&oamSub); + } +} void volEnvSetInst(Instrument *inst) { @@ -3374,6 +3382,7 @@ void setupGUI(bool dldi_enabled) { gui = new GUI(); gui->setTheme(settings->getTheme(), settings->getTheme()->col_bg); + gui->setOnOverlayChanged(handleOverlayWidgetChange); kb = new Piano(0, 152, 224, 40, (uint16*)CHAR_BASE_BLOCK_SUB(0), (uint16*)SCREEN_BASE_BLOCK_SUB(1/*8*/), &sub_vram); kb->set_overdraw(false); @@ -4298,6 +4307,8 @@ void VblankHandler(void) } } + oamUpdate(&oamSub); + // Constantly update pattern view while playing if(redraw_main_requested) { @@ -4428,10 +4439,11 @@ int main(int argc, char **argv) { videoSetMode(MODE_5_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG2_ACTIVE); // Sub screen: Keyboard tiles, Typewriter tiles and ERB - videoSetModeSub(MODE_5_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE | DISPLAY_BG2_ACTIVE); + videoSetModeSub(MODE_5_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE | DISPLAY_BG2_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D); + vramSetPrimaryBanks(VRAM_A_MAIN_BG_0x06000000, VRAM_B_MAIN_BG_0x06020000, - VRAM_C_SUB_BG_0x06200000 , VRAM_D_LCD); + VRAM_C_SUB_BG_0x06200000 , VRAM_D_SUB_SPRITE); // SUB_BG0 for Piano Tiles videoBgEnableSub(0); @@ -4458,7 +4470,19 @@ int main(int argc, char **argv) { // Sub screen framebuffer int sub_bg = bgInitSub(2, BgType_Bmp16, BgSize_B16_256x256, 2, 0); - bgSetPriority(sub_bg, 0); + bgSetPriority(sub_bg, 1); + + oamInit(&oamSub, SpriteMapping_1D_32, false); + + // Create a window the same size as the sample display. (so we can occclude loop handles, etc) + windowEnableSub(WINDOW_0); + + bgWindowEnable(sub_bg, (WINDOW)(WINDOW_0|WINDOW_OUT)); + bgWindowEnable(piano_bg, (WINDOW)(WINDOW_0|WINDOW_OUT)); + bgWindowEnable(typewriter_bg, (WINDOW)(WINDOW_0|WINDOW_OUT)); + + windowSetBoundsSub(WINDOW_0, 5, 24, 5+129, 23+61); // sampledisplay x1,y1,x2,y2 + oamWindowEnable(&oamSub, WINDOW_0); // Special effects #ifdef DEBUG diff --git a/arm9/source/tobkit/sampledisplay.cpp b/arm9/source/tobkit/sampledisplay.cpp index 579a368..f389e5b 100644 --- a/arm9/source/tobkit/sampledisplay.cpp +++ b/arm9/source/tobkit/sampledisplay.cpp @@ -27,6 +27,7 @@ #include #include +#include "loophandle.h" #include "ntxm/ntxmtools.h" #include "../tools.h" @@ -45,12 +46,62 @@ SampleDisplay::SampleDisplay(u8 _x, u8 _y, u8 _width, u8 _height, u16 **_vram, S scrollthingypos(0), scrollthingywidth(width-2*SCROLLBUTTON_HEIGHT+2), pen_x_on_scrollthingy(0), zoom_level(0), scrollpos(0), snap_to_zero_crossings(true), draw_mode(false) { + gfxLine = oamAllocateGfx(&oamSub, SpriteSize_16x32, SpriteColorFormat_16Color); + gfxLoopHandle = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_16Color); + gfxZoomButtons = oamAllocateGfx(&oamSub, SpriteSize_32x16, SpriteColorFormat_16Color); + // draw a single pixel then use max y-scale to make a line + *(gfxLine+64)=1; + oamSub.oamRotationMemory[0].vdy = 0; + + for (int i=SPR_LOOPHANDLE_1_L;i<=SPR_LOOPHANDLE_2_R;++i) + { + bool top = i > 17; + bool right = i % 2 == 1; + + oamSet(&oamSub, i, + width+8, top ? y : DRAW_HEIGHT+18, + 0, // priority 0 + 1, // pallette index 1 + SpriteSize_8x8, SpriteColorFormat_16Color, + gfxLoopHandle, + -1, + false, + false, + right, top, // h-flip every other handle, v-flip the last two handles + false); + } + + + memcpy(gfxLoopHandle, loophandleTiles, loophandleTilesLen); + + for (int i=SPR_LOOPLINE_1;i<=SPR_LOOPLINE_2;++i) + { + oamSet(&oamSub, i, 199, y, 0, 0, + SpriteSize_16x32, SpriteColorFormat_16Color, + gfxLine, 0, true, false, false, false, false); + } + + memcpy(gfxZoomButtons, sampleed_zoomTiles, sampleed_zoomTilesLen); + copy_sprite_frame(gfxZoomButtons, 0); + + for (int i=0;i<3;++i) + { + gfxZoomButtonStates[i] = oamAllocateGfx(&oamSub, SpriteSize_32x16, + SpriteColorFormat_16Color); + copy_sprite_frame(gfxZoomButtonStates[i], i); + } + + oamSet(&oamSub, SPR_ZOOM_BUTTONS, x+1, y+1, 0, 2, + SpriteSize_32x16, SpriteColorFormat_16Color, gfxZoomButtons, + -1, false, false, false, false, false); } SampleDisplay::~SampleDisplay(void) { - + oamFreeGfx(&oamSub, gfxLine); + oamFreeGfx(&oamSub, gfxLoopHandle); + oamFreeGfx(&oamSub, gfxZoomButtons); } void SampleDisplay::penDown(u8 px, u8 py) @@ -80,8 +131,8 @@ void SampleDisplay::penDown(u8 px, u8 py) } // Else: Stylus on a zoom button? - else if(isInRect(px-x, py-y, 1, 1, 18, 10)) { - if(px-x < 10) { + else if(isInRect(px-x, py-y, 1, 1, 22, 12)) { + if(px-x < 12) { pen_on_zoom_in = true; zoomIn(); } else { @@ -164,10 +215,13 @@ void SampleDisplay::penMove(u8 px, u8 py) if((smp==0) || ( (active==false) && (loop_points_visible==false) && (draw_mode == false) ) ) return; + bool redraw = !(pen_on_loop_start_point || pen_on_loop_end_point); + if(pen_on_loop_start_point) { s32 olstart = smp->getLoopStart(); s32 newstart = pixelToSample((s32)px-(s32)x-1-(s32)loop_touch_offset); smp->setLoopStartAndLength(newstart, std::max((s32)0, (s32)smp->getLoopLength() - (newstart - olstart))); + drawLoopHandles(); } else if(pen_on_loop_end_point) { s32 newlength = (s32)pixelToSample((s32)px-(s32)x-1-(s32)loop_touch_offset) - smp->getLoopStart(); @@ -177,6 +231,7 @@ void SampleDisplay::penMove(u8 px, u8 py) newlength = 0; } smp->setLoopStartAndLength(newstart, newlength); + drawLoopHandles(); } else if(pen_on_scrollthingy) { @@ -223,7 +278,7 @@ void SampleDisplay::penMove(u8 px, u8 py) } } - draw(); + if(redraw) draw(); } void SampleDisplay::setSample(Sample *_smp) @@ -233,7 +288,10 @@ void SampleDisplay::setSample(Sample *_smp) selstart = selend = 0; if(_smp == 0) { loop_points_visible = false; + oamDisable(&oamSub); } + else if (isExposed()) + oamEnable(&oamSub); draw(); } @@ -291,14 +349,18 @@ void SampleDisplay::setInactive(void) void SampleDisplay::setDrawMode(bool _on) { draw_mode = _on; - draw(); + + if (draw_mode) + oamDisable(&oamSub); + else + oamEnable(&oamSub); } void SampleDisplay::showLoopPoints(void) { if(loop_points_visible == false) { loop_points_visible = true; - draw(); + drawLoopHandles(); } } @@ -307,7 +369,7 @@ void SampleDisplay::hideLoopPoints(void) if(loop_points_visible == true) { loop_points_visible = false; - draw(); + drawLoopHandles(); } } @@ -316,6 +378,28 @@ void SampleDisplay::setSnapToZeroCrossing(bool snap) snap_to_zero_crossings = snap; } +void SampleDisplay::reveal(void) +{ + oamEnable(&oamSub); + Widget::reveal(); +} + +void SampleDisplay::occlude(void) +{ + oamDisable(&oamSub); + Widget::occlude(); +} + +void SampleDisplay::setTheme(Theme *theme_, u16 bgcolor_) +{ + *(SPRITE_PALETTE_SUB+1) = theme_->col_loop; + *(SPRITE_PALETTE_SUB+1+16) = theme_->col_outline; + *(SPRITE_PALETTE_SUB+2+16) = theme_->col_loop; + *(SPRITE_PALETTE_SUB+1+32) = theme_->col_smp_zoom; + + Widget::setTheme(theme_, bgcolor_); +} + /* ===================== PRIVATE ===================== */ long SampleDisplay::find_zero_crossing_near(long pos) @@ -384,6 +468,32 @@ long SampleDisplay::find_zero_crossing_near(long pos) } } +void SampleDisplay::drawLoopHandles(void) +{ + u16 loop_start_pos = smp == 0 ? 0 : sampleToPixel(smp->getLoopStart()); + u16 loop_end_pos = smp == 0 ? 0 : sampleToPixel(smp->getLoopStart() + smp->getLoopLength()); + + if(!draw_mode) + { + oamSub.oamMemory[SPR_LOOPHANDLE_1_L].x = loop_start_pos-2; + oamSub.oamMemory[SPR_LOOPHANDLE_1_R].x = loop_start_pos-2+8-1; + + oamSub.oamMemory[SPR_LOOPHANDLE_2_L].x = loop_end_pos-3; + oamSub.oamMemory[SPR_LOOPHANDLE_2_R].x = loop_end_pos-3+8-1; + } + + bool draw_loop_end = loop_points_visible && (loop_end_pos <= width+8); + bool draw_loop_start = loop_points_visible && (loop_start_pos <= width+8); + + oamSetHidden(&oamSub, SPR_LOOPHANDLE_1_L, !draw_loop_start); + oamSetHidden(&oamSub, SPR_LOOPHANDLE_1_R, !draw_loop_start); + oamSub.oamMemory[SPR_LOOPLINE_1].x = draw_loop_start ? loop_start_pos-2-1 : width+1; + + oamSetHidden(&oamSub, SPR_LOOPHANDLE_2_L, !draw_loop_end); + oamSetHidden(&oamSub, SPR_LOOPHANDLE_2_R, !draw_loop_end); + oamSub.oamMemory[SPR_LOOPLINE_2].x = draw_loop_end ? loop_end_pos-2-2 : width+1; +} + void SampleDisplay::draw(void) { if(!isExposed()) @@ -396,6 +506,11 @@ void SampleDisplay::draw(void) drawBorder(theme->col_signal); } + // + // Loop Points + // + drawLoopHandles(); // Hides loop points if no sample/loop + // Now comes sample-dependant stuff, so return if we have no sample if((smp==0)||(smp->getNSamples()==0)) { drawFullBox(1, 1, width - 2, height - 2, theme->col_smp_bg); @@ -588,129 +703,22 @@ void SampleDisplay::draw(void) } - // - // Loop Points - // - if( (loop_points_visible) && (smp->getLoop() != NO_LOOP) && !draw_mode ) - { - s32 loop_start_pos = sampleToPixel(smp->getLoopStart()); - s32 loop_end_pos = sampleToPixel(smp->getLoopStart() + smp->getLoopLength()); - - // Loop Start - - if( (loop_start_pos >= 0) && (loop_start_pos <= width-2) ) { - // Line - for(u8 i=1; icol_loop; - - /* unused - u8 cutoff = 0; - if(loop_start_pos < 1+LOOP_TRIANGLE_SIZE) - cutoff = 1+LOOP_TRIANGLE_SIZE - loop_start_pos; - */ - - // Left Triangle - if(loop_start_pos > 1 + LOOP_TRIANGLE_SIZE) - { - drawHLine(loop_start_pos-2, DRAW_HEIGHT+1-LOOP_TRIANGLE_SIZE, 2, theme->col_outline); - - for(u8 i=0; icol_loop); - drawPixel(loop_start_pos-i-3, DRAW_HEIGHT+2-LOOP_TRIANGLE_SIZE+i, theme->col_outline); - } - - drawHLine(loop_start_pos-LOOP_TRIANGLE_SIZE+1, DRAW_HEIGHT, LOOP_TRIANGLE_SIZE-1, - theme->col_loop); - drawPixel(loop_start_pos-LOOP_TRIANGLE_SIZE, DRAW_HEIGHT, theme->col_outline); - } - - // Right Triangle - if(loop_start_pos < width - 2 - LOOP_TRIANGLE_SIZE) - { - drawHLine(loop_start_pos+1, DRAW_HEIGHT+1-LOOP_TRIANGLE_SIZE, 2, theme->col_outline); - for(u8 i=0; icol_loop); - drawPixel(loop_start_pos+3+i, DRAW_HEIGHT+2-LOOP_TRIANGLE_SIZE+i, theme->col_outline); - } - drawHLine(loop_start_pos+1, DRAW_HEIGHT-LOOP_TRIANGLE_SIZE+LOOP_TRIANGLE_SIZE, LOOP_TRIANGLE_SIZE-1, - theme->col_loop); - drawPixel(loop_start_pos+LOOP_TRIANGLE_SIZE, DRAW_HEIGHT, theme->col_outline); - } - } - - // Loop End - - if( (loop_end_pos >= 0) && (loop_end_pos <= width-2) ) { - // Line - for(u8 i=1; icol_loop; - - // Left Triangle - if(loop_end_pos > 1 + LOOP_TRIANGLE_SIZE) - { - drawHLine(loop_end_pos-LOOP_TRIANGLE_SIZE+1, 1, LOOP_TRIANGLE_SIZE-1, - theme->col_loop); - drawPixel(loop_end_pos-LOOP_TRIANGLE_SIZE, 1, theme->col_outline); - - for(u8 i=0; icol_loop); - drawPixel(loop_end_pos-1-LOOP_TRIANGLE_SIZE+i+1, 2+i, theme->col_outline); - } - - drawHLine(loop_end_pos-2, LOOP_TRIANGLE_SIZE, 2, theme->col_outline); - } - - // Right Triangle - if(loop_end_pos < width-1-LOOP_TRIANGLE_SIZE) - { - drawHLine(loop_end_pos+1, 1, LOOP_TRIANGLE_SIZE-1, theme->col_loop); - drawPixel(loop_end_pos+LOOP_TRIANGLE_SIZE, 1, theme->col_outline); - - for(u8 i=0; icol_loop); - drawPixel(loop_end_pos+LOOP_TRIANGLE_SIZE-i, 2+i, theme->col_outline); - } - - drawHLine(loop_end_pos+1, LOOP_TRIANGLE_SIZE, 2, theme->col_outline); - } - } - } - // // Zoom buttons // - if(!draw_mode) { - // Outlines - drawHLine(2, 1, 7, theme->col_smp_zoom); - drawHLine(10, 1, 7, theme->col_smp_zoom); - - drawHLine(2, 9, 7, theme->col_smp_zoom); - drawHLine(10, 9, 7, theme->col_smp_zoom); - - drawVLine(1, 2, 7, theme->col_smp_zoom); - drawVLine(9, 2, 7, theme->col_smp_zoom); - drawVLine(17, 2, 7, theme->col_smp_zoom); - // + if(pen_on_zoom_in) { - drawFullBox(2, 2, 7, 7, theme->col_smp_zoom); - drawHLine(3, 5, 5, theme->col_smp_bg); - drawVLine(5, 3, 5, theme->col_smp_bg); - } else { - drawHLine(3, 5, 5, theme->col_smp_zoom); - drawVLine(5, 3, 5, theme->col_smp_zoom); + copy_sprite_frame(gfxZoomButtons, 1); } // - - if(pen_on_zoom_out) { - drawFullBox(10, 2, 7, 7, theme->col_smp_zoom); - drawHLine(11, 5, 5, theme->col_smp_bg); - } else { - drawHLine(11, 5, 5, theme->col_smp_zoom); + else if(pen_on_zoom_out) { + copy_sprite_frame(gfxZoomButtons, 2); + } + + else { + copy_sprite_frame(gfxZoomButtons, 0); } } } @@ -771,7 +779,7 @@ void SampleDisplay::zoomOut(void) u32 SampleDisplay::pixelToSample(s32 pixel) { - pixel=std::max(0,std::min(width-2,(int)pixel)); + pixel=ntxm_clamp(pixel, 0, width-2); u64 abspos = scrollpos + pixel; return u32( abspos * smp->getNSamples() / ( u64(width-2)< shortcuts; Widget *activeWidget; u8 activeScreen; + void (*onOverlayChanged)(u8, bool); Widget *overlayWidgetMain, *overlayWidgetSub; u16 overlayShortcuts; Theme *theme; diff --git a/tobkit/source/gui.cpp b/tobkit/source/gui.cpp index d8f153e..7a2c56c 100644 --- a/tobkit/source/gui.cpp +++ b/tobkit/source/gui.cpp @@ -23,8 +23,8 @@ using namespace tobkit; /* ===================== PUBLIC ===================== */ GUI::GUI() - :activeWidget(0), activeScreen(SUB_SCREEN), overlayWidgetMain(0), - overlayWidgetSub(0), overlayShortcuts(0) + :activeWidget(0), activeScreen(SUB_SCREEN), onOverlayChanged(0), + overlayWidgetMain(0), overlayWidgetSub(0), overlayShortcuts(0) { u8 i; for(i=0;i<14;++i) { @@ -103,8 +103,10 @@ void GUI::unregisterWidget(Widget *w) void GUI::registerOverlayWidget(Widget *w, u16 listeningButtons, u8 screen) { if(screen == SUB_SCREEN) { + onOverlayChanged(SUB_SCREEN, true); overlayWidgetSub = w; } else { + onOverlayChanged(MAIN_SCREEN, true); overlayWidgetMain = w; } overlayShortcuts = listeningButtons; @@ -116,12 +118,14 @@ void GUI::registerOverlayWidget(Widget *w, u16 listeningButtons, u8 screen) void GUI::unregisterOverlayWidget(u8 screen) { if(screen == SUB_SCREEN) { - if(activeWidget==overlayWidgetSub) { + onOverlayChanged(SUB_SCREEN, false); + if(activeWidget==overlayWidgetSub) { activeWidget = 0; - } + } overlayWidgetSub = 0; } else { - if(activeWidget==overlayWidgetMain) { + onOverlayChanged(MAIN_SCREEN, false); + if(activeWidget==overlayWidgetMain) { activeWidget = 0; } overlayWidgetMain = 0; @@ -129,6 +133,11 @@ void GUI::unregisterOverlayWidget(u8 screen) overlayShortcuts = 0; } +void GUI::setOnOverlayChanged(void (*_onOverlayChanged)(u8, bool)) +{ + onOverlayChanged = _onOverlayChanged; +} + // Event calls void GUI::penDown(u8 x, u8 y) {