performance updates

This commit is contained in:
Mark Gregory
2026-11-06 08:48:12 +00:00
parent 376f027f12
commit 8d33198d21
3 changed files with 47 additions and 1 deletions
+7 -1
View File
@@ -180,7 +180,7 @@ final class DJNativeSpectrumView: DJWaveMouseView {
private var hoverX: Double?
private var tracking: NSTrackingArea?
override init(frame: NSRect) {
super.init(frame:frame)
super.init(frame:frame);clipsToBounds = true
freeze = { [weak self] start in self?.frozenViewport = start;self?.needsDisplay = true }
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
@@ -223,6 +223,12 @@ final class DJNativeSpectrumView: DJWaveMouseView {
super.mouseDown(with:event)
}
override func draw(_ dirtyRect: NSRect) {
guard let context = NSGraphicsContext.current?.cgContext else { return }
context.saveGState()
defer { context.restoreGState() }
// Cached sections extend beyond the viewport while scrolling. Neither
// those sections nor their markers may paint into adjacent deck controls.
context.clip(to:bounds)
NSColor.black.setFill();bounds.fill()
guard let player,player.duration > 0,span > 0,!player.waveform.isEmpty else { return }
let time = player.displayTime,start = frozenViewport ?? (time-span/2),width = bounds.width,height = bounds.height
+1
View File
@@ -1249,6 +1249,7 @@ struct DJScrollingSpectrum: View {
var spectralWaveforms = true
var body: some View {
DJNativeSpectrum(player:player,bpm:bpm,color:NSColor(color),seconds:seconds,label:label,showTimeGrid:showTimeGrid,spectralWaveforms:spectralWaveforms)
.clipped()
.accessibilityLabel(accessibilityName).accessibilityValue(DJStyle.stamp(player.time))
.help("Hold the left button and drag left to advance or right to rewind. Shift-drag creates a loop; drag green loop edges to resize or its centre to move it.")
}
+39
View File
@@ -2501,3 +2501,42 @@ extension KJamsCloneTests {
XCTAssertGreaterThan(view.waveformTileBuildCount,builds,"Changing window size must rebuild sharp waveform tiles")
}
}
extension KJamsCloneTests {
@MainActor func testSpectrumCachedSectionsCannotPaintOutsideViewport() async throws {
let file = try XCTUnwrap(Bundle(for:Self.self).resourceURL?.appendingPathComponent("Fixtures/Demo.mp3"))
let player = Player(persistsMix:false);defer { player.stop() }
try player.load(.init(id:1,title:"Clip",audio:file.path,cdg:nil,rating:0,plays:0),startPlaying:false)
for _ in 0..<100 where player.waveformLoading { try await Task.sleep(nanoseconds:20_000_000) }
let view = DJNativeSpectrumView(frame:NSRect(x:0,y:0,width:300,height:80))
view.player = player;view.span = 0.5;view.bpm = 120
XCTAssertTrue(view.clipsToBounds)
let bitmap = try XCTUnwrap(NSBitmapImageRep(bitmapDataPlanes:nil,pixelsWide:380,pixelsHigh:140,bitsPerSample:8,samplesPerPixel:4,hasAlpha:true,isPlanar:false,colorSpaceName:.deviceRGB,bytesPerRow:0,bitsPerPixel:0))
let graphics = try XCTUnwrap(NSGraphicsContext(bitmapImageRep:bitmap))
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = graphics
let context = graphics.cgContext
context.setFillColor(CGColor(red:1,green:0,blue:1,alpha:1));context.fill(CGRect(x:0,y:0,width:380,height:140))
let sentinel = try XCTUnwrap(bitmap.colorAt(x:0,y:0)?.usingColorSpace(.deviceRGB))
context.translateBy(x:40,y:30)
for position in [0.25,0.49,0.51,1.0] {
player.seek(position)
// Deliberately omit AppKit's implicit clipping: draw() must constrain
// its own cached tiles, including at a tile-boundary crossing.
view.draw(view.bounds)
}
NSGraphicsContext.restoreGraphicsState()
for y in 0..<140 {
for x in 0..<380 where x < 40 || x >= 340 || y < 30 || y >= 110 {
let color = try XCTUnwrap(bitmap.colorAt(x:x,y:y)?.usingColorSpace(.deviceRGB))
guard abs(color.redComponent-sentinel.redComponent) < 0.001,abs(color.greenComponent-sentinel.greenComponent) < 0.001,abs(color.blueComponent-sentinel.blueComponent) < 0.001,abs(color.alphaComponent-sentinel.alphaComponent) < 0.001 else {
return XCTFail("Spectrum drew outside its viewport at \(x),\(y): \(color)")
}
}
}
XCTAssertGreaterThan(view.waveformTileBuildCount,0)
let inside = try XCTUnwrap(bitmap.colorAt(x:190,y:70)?.usingColorSpace(.deviceRGB))
XCTAssertLessThan(inside.redComponent*inside.blueComponent,0.99,"Spectrum must still paint inside its bounds")
}
}