From 9496ecc7bbed4dca049464f758302656d79947c0 Mon Sep 17 00:00:00 2001 From: Vavency Date: Thu, 10 Jul 2025 15:45:17 +0300 Subject: [PATCH] [frontend] SkModPlayer finally get rid of multiple fillText calls for part colours in favour of background blending. Also made row number stick to the right side. :) --- .../frontend/src/components/SkModPlayer.vue | 187 +++++++++++------- 1 file changed, 112 insertions(+), 75 deletions(-) diff --git a/packages/frontend/src/components/SkModPlayer.vue b/packages/frontend/src/components/SkModPlayer.vue index 4ea1bcb328..e8de5c24a6 100644 --- a/packages/frontend/src/components/SkModPlayer.vue +++ b/packages/frontend/src/components/SkModPlayer.vue @@ -20,10 +20,20 @@ SPDX-License-Identifier: AGPL-3.0-only
- - - - + + + + + + + + + + + + + +
@@ -63,10 +73,6 @@ const colours = { foreground: { default: '#ffffff', quarter: '#ffff00', - instr: '#80e0ff', - volume: '#80ff80', - fx: '#ff80e0', - operant: '#ffe080', }, }; @@ -91,6 +97,7 @@ const props = defineProps<{ class CanvasDisplay { ctx: CanvasRenderingContext2D; html: HTMLCanvasElement; + background: HTMLSpanElement; drawn: { top: number, bottom: number, completed: boolean }; vPos: number; transform: { x: number, y: number }; @@ -98,16 +105,20 @@ class CanvasDisplay { constructor ( ctx: CanvasRenderingContext2D, html: HTMLCanvasElement, + background: HTMLSpanElement, ) { this.ctx = ctx; this.html = html; this.drawn = { top: 0, bottom: 0, completed: false }; this.vPos = -0xFFFFFFFF; - this.transform = { x: 2 * CHAR_WIDTH + 1, y: 0 }; + this.transform = { x: 0, y: 0 }; this.drawStart = 0; + this.background = background; + // Hacky solution to seeing raw background while the module isn't loaded yet. + background.style.display = 'flex'; } updateStyleTransforms () { - this.html.style.transform = 'translate(' + this.transform.x + 'px,' + this.transform.y + 'px)'; + this.background.style.transform = 'translate(' + this.transform.x + 'px,' + this.transform.y + 'px)'; } resetDrawn() { this.drawn = { @@ -130,6 +141,10 @@ let numberRowCanvas = ref(); let sliceCanvas1 = ref(); let sliceCanvas2 = ref(); let sliceCanvas3 = ref(); +let sliceBackground1 = ref(); +let sliceBackground2 = ref(); +let sliceBackground3 = ref(); +let numberRowParent = ref(); const displayHeight = ref(ROW_BUFFER * CHAR_HEIGHT); const numberRowOffset = ref(HALF_BUFFER * CHAR_HEIGHT); let sliceWidth = 0; @@ -154,6 +169,7 @@ let lastDrawnRow = -1; let alreadyHiddenOnce = false; let virtualCanvasWidth = 0; let slices: CanvasDisplay[] = []; +let numberRowPHTML: HTMLSpanElement; //let copyBuffer = { canvas: new OffscreenCanvas(1, 1), ctx: OffscreenCanvasRenderingContext2D }; const PERF_MONITOR = { @@ -184,9 +200,10 @@ const PERF_MONITOR = { }; function bakeNumberRow() { - if (!numberRowCanvas.value) return; + if (!numberRowCanvas.value && !numberRowParent.value) return; numberRowCanvas.value.width = 2 * CHAR_WIDTH + 1; numberRowCanvas.value.height = MAX_ROW_NUMBERS * CHAR_HEIGHT + 1; + numberRowPHTML = numberRowParent.value; let ctx = numberRowCanvas.value.getContext('2d', { alpha: false }) as OffscreenCanvasRenderingContext2D; ctx.font = '10px monospace'; ctx.fillStyle = colours.background; @@ -203,24 +220,26 @@ function bakeNumberRow() { } } -function setupSlice(r: Ref) { - let chtml = r.value as HTMLCanvasElement; +function setupSlice(canvas: Ref, back: Ref) { + let backgorund = back.value as HTMLSpanElement; + let chtml = canvas.value as HTMLCanvasElement; chtml.width = sliceWidth; chtml.height = sliceHeight; let slice = new CanvasDisplay( chtml.getContext('2d', { alpha: false, desynchronized: false }) as CanvasRenderingContext2D, chtml, + backgorund, ); slice.ctx.font = '10px monospace'; slice.ctx.imageSmoothingEnabled = false; slices.push(slice); } -// Yes, I'm very evil. -let updateSliceSize = function() {}; - function setupCanvas() { - if (sliceCanvas1.value && sliceCanvas2.value && sliceCanvas3.value) { + if ( + sliceCanvas1.value && sliceCanvas2.value && sliceCanvas3.value && + sliceBackground1.value && sliceCanvas2.value && sliceCanvas3.value + ) { nbChannels = 0; if (player.value.currentPlayingNode) { nbChannels = player.value.currentPlayingNode.nbChannels; @@ -229,9 +248,9 @@ function setupCanvas() { virtualCanvasWidth = 13 + CHANNEL_WIDTH * nbChannels + 2; sliceWidth = MAX_SLICE_WIDTH > virtualCanvasWidth ? virtualCanvasWidth : maxChannelsInView * CHANNEL_WIDTH + 1; sliceHeight = HALF_BUFFER * CHAR_HEIGHT; - setupSlice(sliceCanvas1); - setupSlice(sliceCanvas2); - setupSlice(sliceCanvas3); + setupSlice(sliceCanvas1, sliceBackground1); + setupSlice(sliceCanvas2, sliceBackground2); + setupSlice(sliceCanvas3, sliceBackground3); if (sliceDisplay.value) sliceDisplay.value.style.minWidth = (virtualCanvasWidth - CHANNEL_WIDTH) + 'px'; } else { nextTick(() => { @@ -382,17 +401,24 @@ function drawSlices(skipOptimizationChecks = false) { //debug(sli); } + let patternText: string[] = []; for (let i = 0; i < HALF_BUFFER; i++) { const newRow = sli.drawStart + i; - if (sli.drawn.bottom >= newRow && sli.drawn.top <= newRow || newRow < upper || newRow < upper) continue; + if (sli.drawn.bottom >= newRow && sli.drawn.top <= newRow || newRow < upper || newRow < upper) { + patternText.push(''); + continue; + } if (sli.drawn.top > newRow) sli.drawn.top = newRow; if (sli.drawn.bottom <= newRow) sli.drawn.bottom = newRow; - drawRow(sli, newRow, pattern, 0, i * CHAR_HEIGHT + ROW_OFFSET_Y); + patternText.push(rowText(sli, newRow, pattern)); + //drawRow(sli, newRow, pattern, 0, i * CHAR_HEIGHT + ROW_OFFSET_Y); } + drawText(sli, patternText); }); } else { + numberRowPHTML.style.maxHeight = ((player.value.getPatternNumRows(pattern) + HALF_BUFFER) * CHAR_HEIGHT) + 'px'; slices.forEach((sli, i) => { sli.drawStart = curRow; sli.vPos = HALF_BUFFER * (i + 1); @@ -403,13 +429,16 @@ function drawSlices(skipOptimizationChecks = false) { sli.ctx.fillStyle = colours.background; sli.ctx.fillRect(0, 0, sliceWidth, sliceHeight); + let patternText: string[] = []; + for (let itter = 0; itter < HALF_BUFFER; itter++) { if (sli.drawn.top > curRow) sli.drawn.top = curRow; if (sli.drawn.bottom <= curRow) sli.drawn.bottom = curRow; - drawRow(sli, curRow, pattern, 0, itter * CHAR_HEIGHT + ROW_OFFSET_Y); + patternText.push(rowText(sli, curRow, pattern)); curRow++; if (curRow > lower) break; } + drawText(sli, patternText); //debug(sli); }); } @@ -420,52 +449,24 @@ function drawSlices(skipOptimizationChecks = false) { lastPattern = pattern; } -function drawRow(slice: CanvasDisplay, row: number, pattern: number, drawX = 0, drawY = ROW_OFFSET_Y) { - if (!player.value.currentPlayingNode) return false; - if (row < 0 || row > player.value.getPatternNumRows(pattern) - 1) return false; - const spacer = 11; - const space = ' '; - let seperators = ''; - let note = ''; - let instr = ''; - let volume = ''; - let fx = ''; - let op = ''; +function rowText(slice: CanvasDisplay, row: number, pattern: number) : string { + if (!player.value.currentPlayingNode) return ''; + if (row < 0 || row > player.value.getPatternNumRows(pattern) - 1) return ''; + let retrunStr = '|'; for (let channel = currentColumn; channel < nbChannels; channel++) { if (channel === maxChannelsInView + currentColumn) break; const part = player.value.getPatternRowChannel(pattern, row, channel); - - seperators += '|' + space.repeat( spacer + 2 ); - note += part.substring(0, 3) + space.repeat( spacer ); - instr += part.substring(4, 6) + space.repeat( spacer + 1 ); - volume += part.substring(6, 9) + space.repeat( spacer ); - fx += part.substring(10, 11) + space.repeat( spacer + 2 ); - op += part.substring(11, 13) + space.repeat( spacer + 1 ); + retrunStr += part + '|'; } + return retrunStr; +} - //console.debug( 'seperators: ' + seperators + '\nnote: ' + note + '\ninstr: ' + instr + '\nvolume: ' + volume + '\nfx: ' + fx + '\nop: ' + op); - - //slice.ctx.fillStyle = '#00ffff88'; - //slice.ctx.fillRect(0, drawY - 10, sliceWidth, CHAR_HEIGHT); - +function drawText(slice: CanvasDisplay, text: string[], drawX = 0, drawY = ROW_OFFSET_Y) { slice.ctx.fillStyle = colours.foreground.default; - slice.ctx.fillText(seperators, drawX, drawY); - - slice.ctx.fillStyle = colours.foreground.default; - slice.ctx.fillText(note, drawX + CHAR_WIDTH, drawY); - - slice.ctx.fillStyle = colours.foreground.instr; - slice.ctx.fillText(instr, drawX + CHAR_WIDTH * 5, drawY); - - slice.ctx.fillStyle = colours.foreground.volume; - slice.ctx.fillText(volume, drawX + CHAR_WIDTH * 7, drawY); - - slice.ctx.fillStyle = colours.foreground.fx; - slice.ctx.fillText(fx, drawX + CHAR_WIDTH * 11, drawY); - - slice.ctx.fillStyle = colours.foreground.operant; - slice.ctx.fillText(op, drawX + CHAR_WIDTH * 12, drawY); + text.forEach((str, i) => { + if (str.length !== 0) slice.ctx.fillText(str, drawX, drawY + CHAR_HEIGHT * i); + }); return true; } @@ -500,7 +501,7 @@ function forceUpdateDisplay() { if (noNode) player.value.togglePause(); if (currentColumn + maxChannelsInView >= nbChannels) return; slices.forEach((sli) => { - sli.transform.x = currentColumn * CHANNEL_WIDTH + 1 + 2 * CHAR_WIDTH + 1; + sli.transform.x = currentColumn * CHANNEL_WIDTH + 1; sli.updateStyleTransforms(); }); } @@ -561,6 +562,15 @@ onDeactivated(() => {