From 7cbd44dd1cfe97126e7fc996214e458525d2980c Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Wed, 16 Sep 2026 13:20:29 -0300 Subject: Align macOS rendering with Linux and establish parity regressions --- src/macos/Sources/AppDelegate.swift | 2 +- src/macos/Sources/PardesView.swift | 128 ++++++++++++++++++++++-------------- 2 files changed, 81 insertions(+), 49 deletions(-) (limited to 'src/macos/Sources') diff --git a/src/macos/Sources/AppDelegate.swift b/src/macos/Sources/AppDelegate.swift index 107c40fd..4e91e407 100644 --- a/src/macos/Sources/AppDelegate.swift +++ b/src/macos/Sources/AppDelegate.swift @@ -478,7 +478,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, PardesViewDelegate { if let rgb = view.themeBG { backdrop.isHidden = true window.isOpaque = view.backgroundAlpha >= 1 - window.backgroundColor = NSColor( + window.backgroundColor = view.backgroundAlpha < 1 ? .clear : NSColor( srgbRed: CGFloat((rgb >> 16) & 0xFF) / 255, green: CGFloat((rgb >> 8) & 0xFF) / 255, blue: CGFloat(rgb & 0xFF) / 255, diff --git a/src/macos/Sources/PardesView.swift b/src/macos/Sources/PardesView.swift index ba3677e7..7c537055 100644 --- a/src/macos/Sources/PardesView.swift +++ b/src/macos/Sources/PardesView.swift @@ -1006,7 +1006,7 @@ final class PardesView: NSView { // up on themselves — a translucent ground would darken toward opaque // over a few frames otherwise. if clearGround || backgroundAlpha < 1 { ctx.clear(bounds) } - if !clearGround { fill(ctx, bounds, ground, backgroundAlpha) } + if !clearGround { fillBackground(ctx, bounds, ground, backgroundAlpha) } if frozenImages == nil { frameImages.removeAll(keepingCapacity: true) } guard cols > 0, rows > 0, Int(count) == cols * rows, let cells = suppliedCells ?? pardes_frame_cells() @@ -1060,7 +1060,7 @@ final class PardesView: NSView { if color != bgClear { let bandY = tagline ? y + bandTop : y let bandHeight = tagline ? taglines.height : cellHeight - fill(ctx, CGRect(x: CGFloat(start) * cellWidth, y: bandY, + fillBackground(ctx, CGRect(x: CGFloat(start) * cellWidth, y: bandY, width: CGFloat(col - start) * cellWidth, height: bandHeight), color, block ? 1 : backgroundAlpha) } @@ -1071,29 +1071,6 @@ final class PardesView: NSView { } } - // The rule joining the topbar band to the first pane-tag band. Both were - // centred in their own rows until now, and centring two reduced-height - // bands is exactly what leaves their unused half-bands touching with a - // strip of window background showing between them — the case - // config.gui_topbar_pane_border_px exists to close. Drawn only when that - // first pane row really carries a band, so a session whose second row is - // pane BODY gets no stray line across it. - let ruleThickness = taglines.borderThickness - let ruleColor = pardes_topbar_pane_border_rgb() - if ruleThickness > 0, ruleColor != UInt32(PARDES_COLOR_DEFAULT), - rows > Int(PARDES_TOPBAR_H) { - let base = Int(PARDES_TOPBAR_H) * cols - let joins = (0.. [FrozenBodyLayer] { var result: [FrozenBodyLayer] = [] for index in 0.., cols: Int, rows: Int) { + var layers: [[Int]] = [] for index in 0.. 0 { + fillBackground(ctx, CGRect(x: 0, y: taglines.borderTop, width: bounds.width, + height: taglines.borderThickness), + pardes_topbar_pane_border_rgb(), backgroundAlpha) + } } private func gripGlyph(_ text: String, face: Face) -> GripGlyph? { @@ -1288,10 +1289,13 @@ final class PardesView: NSView { private func drawTagLayer(_ ctx: CGContext, values: [Int], cells: UnsafePointer, hideCursor: Bool) { let left = CGFloat(values[4]) * cellWidth let y = CGFloat(values[5]) * cellHeight + taglines.top(row: values[5], canvasHeight: bounds.height) - let viewport = CGRect(x: left, y: y, width: CGFloat(values[6]) * cellWidth, height: taglines.height) + // SDL gives the tag a full body-row background; only its glyphs + // and per-cell highlights use the compact band. + let viewport = CGRect(x: left, y: CGFloat(values[5]) * cellHeight, + width: CGFloat(values[6]) * cellWidth, height: cellHeight) ctx.saveGState() ctx.clip(to: viewport) - fill(ctx, viewport, UInt32(values[10]), backgroundAlpha) + fillBackground(ctx, viewport, UInt32(values[10]), backgroundAlpha) for pass in 0...1 { for col in 0..= viewport.maxY { break } - let rowStyle = resolve(cells[row*cols], block: false, ground: themeBG ?? pardesDefaultBG, clearGround: false) - fill(ctx, CGRect(x: left, y: y, width: viewport.width, height: height), rowStyle.bg, backgroundAlpha) + let rowStyle = resolve(cells[row*cols], block: false, ground: themeBG ?? pardesDefaultBG, clearGround: themeBG == nil) + fillBackground(ctx, CGRect(x: left, y: y, width: viewport.width, height: height), rowStyle.bg, backgroundAlpha) for pass in 0...1 { for col in 0..> 16) & 0xFF) / 255, - green: CGFloat((rgb >> 8) & 0xFF) / 255, - blue: CGFloat(rgb & 0xFF) / 255, - alpha: alpha) + // Theme channels, like PDF raster bytes, are sRGB. Device RGB can + // brighten them when AppKit's backing bitmap uses another profile. + ctx.setFillColor(CGColor(srgbRed: CGFloat((rgb >> 16) & 0xFF) / 255, + green: CGFloat((rgb >> 8) & 0xFF) / 255, + blue: CGFloat(rgb & 0xFF) / 255, + alpha: alpha)) + } + + /// One surface pixel has one background opacity, even when a compact + /// layer covers a canonical cell. Source-over would turn 71% into 92%, + /// then 98%, as the ground, row and cell are painted over each other. + private func fillBackground(_ ctx: CGContext, _ rect: CGRect, _ rgb: UInt32, _ alpha: CGFloat) { + ctx.saveGState() + ctx.setShouldAntialias(false) + if rgb == bgClear { + ctx.clear(rect) + } else { + ctx.setBlendMode(.copy) + fill(ctx, rect, rgb, alpha) + } + ctx.restoreGState() } private func fill(_ ctx: CGContext, _ rect: CGRect, _ rgb: UInt32, _ alpha: CGFloat) { -- cgit v1.3