diff options
Diffstat (limited to 'test/macos_e2e.swift')
| -rw-r--r-- | test/macos_e2e.swift | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/test/macos_e2e.swift b/test/macos_e2e.swift index 58ad5206..fbfa0e3e 100644 --- a/test/macos_e2e.swift +++ b/test/macos_e2e.swift @@ -270,6 +270,15 @@ private final class Driver: PardesViewDelegate { if let view, let wanted = pardes_font_take(&fontSize) { view.adoptFont(path: String(cString: wanted), sizeHundredths: fontSize) } + // The app reads both of these every pump too (AppDelegate.applyTheme). + // There is no window here to dress, but the VIEW half is the same and + // the drawing depends on it: without this a `Theme` or a + // `WindowOpacity` in a script would move the core's settings and never + // reach a pixel, and the capture would record the old ground forever. + if let view { + view.adoptThemeBG(pardes_theme_bg()) + view.adoptWindowOpacity(pardes_window_opacity()) + } } /// Hand the run loop a slice. Main-queue work (anything the view defers) @@ -489,6 +498,51 @@ private final class Driver: PardesViewDelegate { output += "== draw \(label) \(w)x\(h) nonblank\n" } + /// The alpha range of a drawn frame, recorded into the golden. + /// + /// The contract WindowOpacity states (shaders/ui.frag.glsl, and + /// PardesView.backgroundAlpha for this shell) is two-sided, so one number + /// could not express it: BACKGROUNDS take the setting, and glyph ink stays + /// opaque. A minimum below 255 is the ground letting the desktop through; + /// a maximum of 255 is the ink still sitting on top of it. At + /// `WindowOpacity 100` both ends are 255 and the view is opaque again. + private func drawOpacity(_ label: String) throws { + let view = try live() + let bounds = view.bounds + guard let rep = view.bitmapImageRepForCachingDisplay(in: bounds) else { + throw ScriptError(message: "draw-opacity \(label): the view would not make a bitmap for \(bounds.size)") + } + view.cacheDisplay(in: bounds, to: rep) + guard let data = rep.bitmapData, rep.pixelsWide > 0, rep.pixelsHigh > 0 else { + throw ScriptError(message: "draw-opacity \(label): cacheDisplay produced no pixels") + } + guard rep.hasAlpha, rep.bitsPerPixel == 32 else { + // An opaque view caches into a rep with no alpha at all, which is + // itself the answer at WindowOpacity 100 — say so rather than + // reporting a range read out of the wrong byte. + output += "== draw-opacity \(label) \(Int(bounds.width.rounded()))x\(Int(bounds.height.rounded())) opaque\n" + return + } + // Where alpha sits depends on the rep's format, and only the two + // AppKit actually hands back here are worth decoding. + let alphaFirst = rep.bitmapFormat.contains(.alphaFirst) + let offset = alphaFirst ? 0 : 3 + let rowBytes = rep.bytesPerRow + var lowest: UInt8 = 255 + var highest: UInt8 = 0 + for y in 0..<rep.pixelsHigh { + let row = data + y * rowBytes + for x in 0..<rep.pixelsWide { + let alpha = row[x * 4 + offset] + if alpha < lowest { lowest = alpha } + if alpha > highest { highest = alpha } + } + } + let w = Int(bounds.width.rounded()) + let h = Int(bounds.height.rounded()) + output += "== draw-opacity \(label) \(w)x\(h) alpha \(lowest)..\(highest)\n" + } + // MARK: - the script func run(source: String) throws -> String { @@ -799,6 +853,14 @@ private final class Driver: PardesViewDelegate { guard !rest.isEmpty else { throw ScriptError(message: "draw needs a label") } try draw(rest) + // What WindowOpacity actually did to the pixels, which is the only + // place it can be checked: the core's cell buffer says nothing about + // alpha, and `draw` deliberately compares channels without knowing + // which one is which. + case "draw-opacity": + guard !rest.isEmpty else { throw ScriptError(message: "draw-opacity needs a label") } + try drawOpacity(rest) + case "draw-effect": let view = try live() guard !rest.isEmpty else { throw ScriptError(message: "draw-effect needs a label") } |
