fix(menubar): 修正预览位图缩放

This commit is contained in:
ysicing authored and 缘生 committed 2026-10-01 11:18:40 +08:00
1 parent 9356f86e5d
commit e7040dd04b
2 files changed
+18 -8

No files matched your search

@@ -294,9 +294,9 @@ enum MenuBarRenderer {
return image
}
/// 设置页预览:按指定外观绘制到不透明底色上。
/// 设置页预览:按指定外观栅格化,透明区域由界面叠加底色。
/// 绘制回调型图像每次布局或重绘都会重跑文字排版,因此栅格化一次,SwiftUI 之后只复用位图。
static func preview(_ image: NSImage, dark: Bool) -> NSImage {
static func preview(_ image: NSImage, dark: Bool, backingScale: CGFloat? = nil) -> NSImage {
let appearance = NSAppearance(named: dark ? .darkAqua : .aqua)
let draw = { (rect: NSRect) in
let content = {
@@ -308,16 +308,19 @@ enum MenuBarRenderer {
}
if let appearance { appearance.performAsCurrentDrawingAppearance(content) } else { content() }
}
let scale = NSScreen.main?.backingScaleFactor ?? 2
let scale = backingScale ?? NSScreen.main?.backingScaleFactor ?? 2
guard let rep = NSBitmapImageRep(bitmapDataPlanes: nil,
pixelsWide: Int(ceil(image.size.width * scale)),
pixelsHigh: Int(ceil(image.size.height * scale)),
bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false,
colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 0),
let context = NSGraphicsContext(bitmapImageRep: rep) else {
colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 0) else {
return NSImage(size: image.size, flipped: false) { draw($0); return true }
}
// 逻辑尺寸决定上下文的点到像素缩放,必须在创建上下文前设置。
rep.size = image.size
guard let context = NSGraphicsContext(bitmapImageRep: rep) else {
return NSImage(size: image.size, flipped: false) { draw($0); return true }
}
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = context
draw(NSRect(origin: .zero, size: image.size))
@@ -202,8 +202,8 @@ private func isolatedDefaults() -> UserDefaults {
@MainActor
@Suite struct SettingsTests {
@Test(arguments: [false, true])
func settingsPreviewRasterizesOnceAndKeepsAppearanceColors(dark: Bool) throws {
@Test(arguments: [false, true], [CGFloat(1), CGFloat(2)])
func settingsPreviewRasterizesOnceAndKeepsAppearanceColors(dark: Bool, scale: CGFloat) throws {
var draws = 0
let source = NSImage(size: NSSize(width: 40, height: 18), flipped: false) { rect in
draws += 1
@@ -212,7 +212,7 @@ private func isolatedDefaults() -> UserDefaults {
return true
}
source.isTemplate = true
let preview = MenuBarRenderer.preview(source, dark: dark)
let preview = MenuBarRenderer.preview(source, dark: dark, backingScale: scale)
let drawsAfterRender = draws
#expect(drawsAfterRender >= 1)
// 反复读取预览(布局、重绘)不能再触发源图的绘制回调。
@@ -222,9 +222,16 @@ private func isolatedDefaults() -> UserDefaults {
#expect(preview.representations.contains { $0 is NSBitmapImageRep })
// 模板图按外观着色:浅色外观为黑,深色外观为白。
let rep = try #require(preview.representations.compactMap { $0 as? NSBitmapImageRep }.first)
#expect(rep.pixelsWide == Int(source.size.width * scale))
#expect(rep.pixelsHigh == Int(source.size.height * scale))
let center = try #require(rep.colorAt(x: rep.pixelsWide / 2, y: rep.pixelsHigh / 2)?.usingColorSpace(.deviceRGB))
#expect(center.alphaComponent > 0.99)
#expect((center.redComponent > 0.5) == dark)
for (x, y) in [(0, 0), (rep.pixelsWide - 1, 0), (0, rep.pixelsHigh - 1), (rep.pixelsWide - 1, rep.pixelsHigh - 1)] {
let corner = try #require(rep.colorAt(x: x, y: y)?.usingColorSpace(.deviceRGB))
#expect(corner.alphaComponent > 0.99)
#expect((corner.redComponent > 0.5) == dark)
}
}
@Test func stackedCenteredStyleRendersDifferentPlacementAtSameWidth() {