Skip to content

Commit

Permalink
Fix Bootleg cover generator's text being in the wrong place.
Browse files Browse the repository at this point in the history
Draw to an sRGB color space.
  • Loading branch information
MaddTheSane committed Jan 13, 2024
1 parent f8279ad commit 925e552
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 25 deletions.
10 changes: 5 additions & 5 deletions Boxer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1091,12 +1091,12 @@
5514503824BE8DE50002CE28 /* envelope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = envelope.cpp; sourceTree = "<group>"; };
5514503B24BE8FB90002CE28 /* program_autotype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = program_autotype.cpp; sourceTree = "<group>"; };
5514503C24BE8FB90002CE28 /* program_autotype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = program_autotype.h; sourceTree = "<group>"; };
551B793625593573006C57CE /* URL+ADBFilesystemHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+ADBFilesystemHelpers.swift"; sourceTree = "<group>"; };
551B793625593573006C57CE /* URL+ADBFilesystemHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+ADBFilesystemHelpers.swift"; sourceTree = "<group>"; };
55215F442AD22FD8007C7C68 /* MT32LCDDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MT32LCDDisplay.swift; sourceTree = "<group>"; };
5529311E252AF0DA0069EB35 /* soft_limiter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = soft_limiter.h; sourceTree = "<group>"; };
55296E982AD4B86400845EB5 /* DummyMIDIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyMIDIDevice.swift; sourceTree = "<group>"; };
55419B67255A2F1400A779B2 /* URL+ADBQuickLookHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+ADBQuickLookHelpers.swift"; sourceTree = "<group>"; };
55419B73255A7FFA00A779B2 /* URL+ADBAliasHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+ADBAliasHelpers.swift"; sourceTree = "<group>"; };
55419B67255A2F1400A779B2 /* URL+ADBQuickLookHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+ADBQuickLookHelpers.swift"; sourceTree = "<group>"; };
55419B73255A7FFA00A779B2 /* URL+ADBAliasHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+ADBAliasHelpers.swift"; sourceTree = "<group>"; };
55488508226A501000A879B3 /* drive_overlay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drive_overlay.cpp; sourceTree = "<group>"; };
554E2B6C1BFAF6720038C506 /* Shared.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Shared.xcassets; sourceTree = "<group>"; };
554E2B6F1BFAF6D90038C506 /* Boxer only.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Boxer only.xcassets"; sourceTree = "<group>"; };
Expand All @@ -1113,7 +1113,7 @@
555747F9208FA8030045E635 /* sn76496.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sn76496.cpp; sourceTree = "<group>"; };
555747FA208FA8030045E635 /* saa1099.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = saa1099.h; sourceTree = "<group>"; };
555747FB208FA8030045E635 /* ymf262.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ymf262.h; sourceTree = "<group>"; };
5563E57E239EF7CD007A1600 /* ADBSwiftHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ADBSwiftHelpers.swift; sourceTree = "<group>"; };
5563E57E239EF7CD007A1600 /* ADBSwiftHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ADBSwiftHelpers.swift; sourceTree = "<group>"; };
5565757423568DED003489A6 /* risc_x64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = risc_x64.h; sourceTree = "<group>"; };
55844D0626C70E4E00AA5924 /* pacer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pacer.h; sourceTree = "<group>"; };
55844D0F26C70E7700AA5924 /* pacer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pacer.cpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1183,7 +1183,7 @@
55BA63F124B051C300A53F4C /* risc_armv8le.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = risc_armv8le.h; sourceTree = "<group>"; };
55BBA4E6235EE141007AE319 /* Boxer-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Boxer-Bridging-Header.h"; sourceTree = "<group>"; };
55BBA4E7235EE141007AE319 /* Boxer Standalone-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Boxer Standalone-Bridging-Header.h"; sourceTree = "<group>"; };
55BBA4E8235EE141007AE319 /* NSError+ADBErrorHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSError+ADBErrorHelpers.swift"; sourceTree = "<group>"; };
55BBA4E8235EE141007AE319 /* NSError+ADBErrorHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSError+ADBErrorHelpers.swift"; sourceTree = "<group>"; };
55BF6F0320210F6000F3FE59 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
55C3B28D25A808D8001EE655 /* midi_handler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = midi_handler.h; sourceTree = "<group>"; };
55D7B1942ADB821600B83750 /* BootlegCoverArt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BootlegCoverArt.swift; sourceTree = "<group>"; };
Expand Down
2 changes: 1 addition & 1 deletion Boxer/BXGamebox.m
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ - (NSImage *) coverArt

- (void) setCoverArt: (NSImage *)image
{
[[NSWorkspace sharedWorkspace] setIcon: image forFile: self.bundlePath options: 0];
[[NSWorkspace sharedWorkspace] setIcon: image forFile: self.bundlePath options: NSExcludeQuickDrawElementsIconCreationOption];
}

- (NSDictionary *) gameInfo
Expand Down
5 changes: 3 additions & 2 deletions Boxer/BootlegCoverArt.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class JewelCase : NSObject, BXBootlegCoverArt {

if !textRegion.isEmpty {
let textAttributes = type(of: self).textAttributes(for: iconSize)
title.draw(with: textRegion, attributes: textAttributes)
(title as NSString).draw(in: textRegion, withAttributes: textAttributes)
//TODO: use title.draw(with: textRegion, attributes: textAttributes)
}

if let topLayer {
Expand All @@ -50,7 +51,7 @@ class JewelCase : NSObject, BXBootlegCoverArt {
let frame = NSRect(origin: .zero, size: iconSize)

//Create a new empty canvas to draw into
let rep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(iconSize.width*scale), pixelsHigh: Int(iconSize.height*scale), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 32)!
let rep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(iconSize.width*scale), pixelsHigh: Int(iconSize.height*scale), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 32)!.retagging(with: .sRGB)!
rep.size = iconSize

NSGraphicsContext.saveGraphicsState()
Expand Down
57 changes: 40 additions & 17 deletions Boxer/CoverArt.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class CoverArt: NSObject {

// @objc(drawInRect:)
/// Draws the source image as cover art into the specified frame in the current graphics context.
func draw(in frame: NSRect) {
private func draw(in frame: NSRect) {
//Switch to high-quality interpolation before we begin, and restore it once we're done
//(this is not stored by saveGraphicsState/restoreGraphicsState unfortunately)
let oldInterpolation = NSGraphicsContext.current?.imageInterpolation ?? .`default`
Expand Down Expand Up @@ -130,10 +130,10 @@ class CoverArt: NSObject {
// @objc(representationForSize:scale:)
/// Returns a cover art image representation from the source image rendered at the specified size and scale.
private func representation(for iconSize: NSSize, scale: CGFloat = 1) -> NSImageRep! {
var frame = NSRect(origin: .zero, size: iconSize)
let frame = NSRect(origin: .zero, size: iconSize)

//Create a new empty canvas to draw into
let rep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(iconSize.width * scale), pixelsHigh: Int(iconSize.height * scale), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 32)!
let rep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(iconSize.width * scale), pixelsHigh: Int(iconSize.height * scale), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 32)!.retagging(with: .sRGB)!
rep.size = iconSize

NSGraphicsContext.saveGraphicsState()
Expand All @@ -152,7 +152,7 @@ class CoverArt: NSObject {
}

/// Returns a cover art image rendered from the source image to 512, 256, 128 and 32x32 sizes,
/// suitable for use as an OS X icon.
/// suitable for use as a macOS icon.
func coverArt() -> NSImage? {
//If our source image could not be read, then bail out.
guard let image = sourceImage, image.isValid else {
Expand All @@ -161,7 +161,7 @@ class CoverArt: NSObject {

//If our source image already has transparency data,
//then assume that it already has effects of its own applied and don't process it.
if type(of: self).imageHasTransparency(image) {
if imageHasTransparency(image) {
return image
}

Expand All @@ -178,23 +178,46 @@ class CoverArt: NSObject {
}

/// Returns a cover art image rendered from the specified image to 512, 256, 128 and 32x32 sizes,
/// suitable for use as an OS X icon.
/// suitable for use as a macOS icon.
///
/// Note that this returns an NSImage directly, not a `CoverArt` instance.
@objc(coverArtWithImage:)
class func coverArt(with image: NSImage) -> NSImage? {
let generator = self.init(sourceImage: image)
return generator.coverArt()
}

/// Returns whether the specified image appears to contain actual transparent/translucent pixels.
/// This is distinct from whether it has an alpha channel, as the alpha channel may go unused
/// (e.g. in an opaque image saved as 32-bit PNG.)
private class func imageHasTransparency(_ image: NSImage) -> Bool {
var hasTranslucentPixels = false
}

//Only bother testing transparency if the image has an alpha channel
if image.representations.last?.hasAlpha ?? false {
/// Returns whether the specified image appears to contain actual transparent/translucent pixels.
/// This is distinct from whether it has an alpha channel, as the alpha channel may go unused
/// (e.g. in an opaque image saved as 32-bit PNG.)
private func imageHasTransparency(_ image: NSImage) -> Bool {
var hasTranslucentPixels = false

//Only bother testing transparency if the image has an alpha channel
if image.representations.last?.hasAlpha ?? false {
if let bir = image.representations.last as? NSBitmapImageRep {
let imageSize = bir.size
let imageWidth = bir.pixelsWide
let imageHigh = bir.pixelsHigh

//Test 5 pixels in an X pattern: each corner and right in the center of the image.
let testPoints: [(x: Int, y: Int)] = [
(0, 0),
(imageWidth - 1, 0),
(0, imageHigh - 1),
(imageWidth - 1, imageHigh - 1),
(imageWidth / 2, imageHigh / 2)
]

for (x, y) in testPoints {
//If any of the pixels appears to be translucent, then stop looking further.
if let pixel = bir.colorAt(x: x, y: y), pixel.alphaComponent < 0.9 {
hasTranslucentPixels = true
break
}
}
} else {
let imageSize = image.size

//Test 5 pixels in an X pattern: each corner and right in the center of the image.
Expand All @@ -205,7 +228,7 @@ class CoverArt: NSObject {
NSMakePoint(imageSize.width - 1.0, imageSize.height - 1.0),
NSMakePoint(imageSize.width * 0.5, imageSize.height * 0.5)
]

image.lockFocus()
for point in testPoints {
//If any of the pixels appears to be translucent, then stop looking further.
Expand All @@ -216,7 +239,7 @@ class CoverArt: NSObject {
}
image.unlockFocus()
}

return hasTranslucentPixels
}

return hasTranslucentPixels
}

0 comments on commit 925e552

Please sign in to comment.