mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: colorized Ghostty icon begins!
This commit is contained in:
6
macos/Assets.xcassets/Custom Icon/Contents.json
Normal file
6
macos/Assets.xcassets/Custom Icon/Contents.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
15
macos/Assets.xcassets/Custom Icon/CustomIconBase.imageset/Contents.json
vendored
Normal file
15
macos/Assets.xcassets/Custom Icon/CustomIconBase.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "base.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "original"
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Custom Icon/CustomIconBase.imageset/base.png
vendored
Normal file
BIN
macos/Assets.xcassets/Custom Icon/CustomIconBase.imageset/base.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 145 KiB |
15
macos/Assets.xcassets/Custom Icon/CustomIconCRT.imageset/Contents.json
vendored
Normal file
15
macos/Assets.xcassets/Custom Icon/CustomIconCRT.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "crt-effect.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "original"
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Custom Icon/CustomIconCRT.imageset/crt-effect.png
vendored
Normal file
BIN
macos/Assets.xcassets/Custom Icon/CustomIconCRT.imageset/crt-effect.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
15
macos/Assets.xcassets/Custom Icon/CustomIconGhost.imageset/Contents.json
vendored
Normal file
15
macos/Assets.xcassets/Custom Icon/CustomIconGhost.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ghosty.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Custom Icon/CustomIconGhost.imageset/ghosty.png
vendored
Normal file
BIN
macos/Assets.xcassets/Custom Icon/CustomIconGhost.imageset/ghosty.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
15
macos/Assets.xcassets/Custom Icon/CustomIconGloss.imageset/Contents.json
vendored
Normal file
15
macos/Assets.xcassets/Custom Icon/CustomIconGloss.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "gloss.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "original"
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Custom Icon/CustomIconGloss.imageset/gloss.png
vendored
Normal file
BIN
macos/Assets.xcassets/Custom Icon/CustomIconGloss.imageset/gloss.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
15
macos/Assets.xcassets/Custom Icon/CustomIconScreen.imageset/Contents.json
vendored
Normal file
15
macos/Assets.xcassets/Custom Icon/CustomIconScreen.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "screen-dark.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "original"
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Custom Icon/CustomIconScreen.imageset/screen-dark.png
vendored
Normal file
BIN
macos/Assets.xcassets/Custom Icon/CustomIconScreen.imageset/screen-dark.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.2 KiB |
15
macos/Assets.xcassets/Custom Icon/CustomIconScreenMask.imageset/Contents.json
vendored
Normal file
15
macos/Assets.xcassets/Custom Icon/CustomIconScreenMask.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "screen-mask.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "original"
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Custom Icon/CustomIconScreenMask.imageset/screen-mask.png
vendored
Normal file
BIN
macos/Assets.xcassets/Custom Icon/CustomIconScreenMask.imageset/screen-mask.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
@ -39,6 +39,9 @@
|
|||||||
A53D0C952B53B4D800305CE6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
A53D0C952B53B4D800305CE6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||||
A53D0C9B2B543F3B00305CE6 /* Ghostty.App.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */; };
|
A53D0C9B2B543F3B00305CE6 /* Ghostty.App.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */; };
|
||||||
A53D0C9C2B543F7B00305CE6 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB729B6F53A0055DE60 /* Package.swift */; };
|
A53D0C9C2B543F7B00305CE6 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB729B6F53A0055DE60 /* Package.swift */; };
|
||||||
|
A54B0CE92D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A54B0CE82D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift */; };
|
||||||
|
A54B0CEB2D0CFB4C00CBEFF8 /* NSImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A54B0CEA2D0CFB4A00CBEFF8 /* NSImage+Extension.swift */; };
|
||||||
|
A54B0CED2D0CFB7700CBEFF8 /* ColorizedGhosttyIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A54B0CEC2D0CFB7300CBEFF8 /* ColorizedGhosttyIcon.swift */; };
|
||||||
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A54D786B2CA79788001B19B1 /* BaseTerminalController.swift */; };
|
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A54D786B2CA79788001B19B1 /* BaseTerminalController.swift */; };
|
||||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55685DF29A03A9F004303CE /* AppError.swift */; };
|
A55685E029A03A9F004303CE /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55685DF29A03A9F004303CE /* AppError.swift */; };
|
||||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB729B6F53A0055DE60 /* Package.swift */; };
|
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB729B6F53A0055DE60 /* Package.swift */; };
|
||||||
@ -124,6 +127,9 @@
|
|||||||
A53A6C022CCC1B7D00943E98 /* Ghostty.Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Action.swift; sourceTree = "<group>"; };
|
A53A6C022CCC1B7D00943E98 /* Ghostty.Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Action.swift; sourceTree = "<group>"; };
|
||||||
A53D0C932B53B43700305CE6 /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = "<group>"; };
|
A53D0C932B53B43700305CE6 /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = "<group>"; };
|
||||||
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.App.swift; sourceTree = "<group>"; };
|
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.App.swift; sourceTree = "<group>"; };
|
||||||
|
A54B0CE82D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorizedGhosttyIconView.swift; sourceTree = "<group>"; };
|
||||||
|
A54B0CEA2D0CFB4A00CBEFF8 /* NSImage+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+Extension.swift"; sourceTree = "<group>"; };
|
||||||
|
A54B0CEC2D0CFB7300CBEFF8 /* ColorizedGhosttyIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorizedGhosttyIcon.swift; sourceTree = "<group>"; };
|
||||||
A54D786B2CA79788001B19B1 /* BaseTerminalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTerminalController.swift; sourceTree = "<group>"; };
|
A54D786B2CA79788001B19B1 /* BaseTerminalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTerminalController.swift; sourceTree = "<group>"; };
|
||||||
A55685DF29A03A9F004303CE /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = "<group>"; };
|
A55685DF29A03A9F004303CE /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = "<group>"; };
|
||||||
A55B7BB729B6F53A0055DE60 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
|
A55B7BB729B6F53A0055DE60 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
|
||||||
@ -237,6 +243,7 @@
|
|||||||
A57D79252C9C8782001D522E /* Secure Input */,
|
A57D79252C9C8782001D522E /* Secure Input */,
|
||||||
A534263E2A7DCC5800EBB7A2 /* Settings */,
|
A534263E2A7DCC5800EBB7A2 /* Settings */,
|
||||||
A51BFC1C2B2FB5AB00E92F16 /* About */,
|
A51BFC1C2B2FB5AB00E92F16 /* About */,
|
||||||
|
A54B0CE72D0CEC9800CBEFF8 /* Colorized Ghostty Icon */,
|
||||||
A51BFC292B30F69F00E92F16 /* Update */,
|
A51BFC292B30F69F00E92F16 /* Update */,
|
||||||
);
|
);
|
||||||
path = Features;
|
path = Features;
|
||||||
@ -256,6 +263,7 @@
|
|||||||
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */,
|
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */,
|
||||||
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */,
|
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */,
|
||||||
A599CDAF2CF103F20049FA26 /* NSAppearance+Extension.swift */,
|
A599CDAF2CF103F20049FA26 /* NSAppearance+Extension.swift */,
|
||||||
|
A54B0CEA2D0CFB4A00CBEFF8 /* NSImage+Extension.swift */,
|
||||||
A52FFF5C2CAB4D05000C6A5B /* NSScreen+Extension.swift */,
|
A52FFF5C2CAB4D05000C6A5B /* NSScreen+Extension.swift */,
|
||||||
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
||||||
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
||||||
@ -307,6 +315,15 @@
|
|||||||
path = macOS;
|
path = macOS;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
A54B0CE72D0CEC9800CBEFF8 /* Colorized Ghostty Icon */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A54B0CEC2D0CFB7300CBEFF8 /* ColorizedGhosttyIcon.swift */,
|
||||||
|
A54B0CE82D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift */,
|
||||||
|
);
|
||||||
|
path = "Colorized Ghostty Icon";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
A54CD6ED299BEB14008C95BB /* Sources */ = {
|
A54CD6ED299BEB14008C95BB /* Sources */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -580,8 +597,10 @@
|
|||||||
files = (
|
files = (
|
||||||
A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */,
|
A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */,
|
||||||
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */,
|
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */,
|
||||||
|
A54B0CEB2D0CFB4C00CBEFF8 /* NSImage+Extension.swift in Sources */,
|
||||||
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */,
|
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */,
|
||||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
||||||
|
A54B0CE92D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift in Sources */,
|
||||||
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */,
|
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */,
|
||||||
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
|
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
|
||||||
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */,
|
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */,
|
||||||
@ -620,6 +639,7 @@
|
|||||||
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */,
|
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */,
|
||||||
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
||||||
A53A6C032CCC1B7F00943E98 /* Ghostty.Action.swift in Sources */,
|
A53A6C032CCC1B7F00943E98 /* Ghostty.Action.swift in Sources */,
|
||||||
|
A54B0CED2D0CFB7700CBEFF8 /* ColorizedGhosttyIcon.swift in Sources */,
|
||||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
||||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
||||||
A599CDB02CF103F60049FA26 /* NSAppearance+Extension.swift in Sources */,
|
A599CDB02CF103F60049FA26 /* NSAppearance+Extension.swift in Sources */,
|
||||||
|
@ -519,6 +519,13 @@ class AppDelegate: NSObject,
|
|||||||
} else {
|
} else {
|
||||||
GlobalEventTap.shared.disable()
|
GlobalEventTap.shared.disable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let colorizedIcon = ColorizedGhosttyIcon(
|
||||||
|
screenColors: [.purple, .blue],
|
||||||
|
ghostColor: .yellow
|
||||||
|
).makeImage() {
|
||||||
|
NSApplication.shared.applicationIconImage = colorizedIcon
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sync the appearance of our app with the theme specified in the config.
|
/// Sync the appearance of our app with the theme specified in the config.
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
import Cocoa
|
||||||
|
|
||||||
|
struct ColorizedGhosttyIcon {
|
||||||
|
/// The colors that make up the gradient of the screen.
|
||||||
|
let screenColors: [NSColor]
|
||||||
|
|
||||||
|
/// The color of the ghost.
|
||||||
|
let ghostColor: NSColor
|
||||||
|
|
||||||
|
/// Make a custom colorized ghostty icon.
|
||||||
|
func makeImage() -> NSImage? {
|
||||||
|
// All of our layers (in order)
|
||||||
|
guard let base = NSImage(named: "CustomIconBase") else { return nil }
|
||||||
|
guard let screen = NSImage(named: "CustomIconScreen") else { return nil }
|
||||||
|
guard let screenMask = NSImage(named: "CustomIconScreenMask") else { return nil }
|
||||||
|
guard let ghost = NSImage(named: "CustomIconGhost") else { return nil }
|
||||||
|
guard let crt = NSImage(named: "CustomIconCRT") else { return nil }
|
||||||
|
guard let gloss = NSImage(named: "CustomIconGloss") else { return nil }
|
||||||
|
|
||||||
|
// Apply our color in various ways to our layers.
|
||||||
|
// NOTE: These functions are not built-in, they're implemented as an extension
|
||||||
|
// to NSImage in NSImage+Extension.swift.
|
||||||
|
guard let screenGradient = screenMask.gradient(colors: screenColors) else { return nil }
|
||||||
|
guard let tintedGhost = ghost.tint(color: ghostColor) else { return nil }
|
||||||
|
|
||||||
|
// Combine our layers using the proper blending modes
|
||||||
|
return.combine(images: [
|
||||||
|
base,
|
||||||
|
screen,
|
||||||
|
screenGradient,
|
||||||
|
ghost,
|
||||||
|
tintedGhost,
|
||||||
|
crt,
|
||||||
|
gloss,
|
||||||
|
], blendingModes: [
|
||||||
|
.normal,
|
||||||
|
.normal,
|
||||||
|
.color,
|
||||||
|
.normal,
|
||||||
|
.color,
|
||||||
|
.overlay,
|
||||||
|
.normal,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
// For testing.
|
||||||
|
struct ColorizedGhosttyIconView: View {
|
||||||
|
var body: some View {
|
||||||
|
Image(nsImage: ColorizedGhosttyIcon(
|
||||||
|
screenColors: [.purple, .blue],
|
||||||
|
ghostColor: .yellow
|
||||||
|
).makeImage()!)
|
||||||
|
}
|
||||||
|
}
|
90
macos/Sources/Helpers/NSImage+Extension.swift
Normal file
90
macos/Sources/Helpers/NSImage+Extension.swift
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import Cocoa
|
||||||
|
|
||||||
|
extension NSImage {
|
||||||
|
/// Combine multiple images with the given blend modes. This is useful given a set
|
||||||
|
/// of layers to create a final rasterized image.
|
||||||
|
static func combine(images: [NSImage], blendingModes: [CGBlendMode]) -> NSImage? {
|
||||||
|
guard images.count == blendingModes.count else { return nil }
|
||||||
|
guard images.count > 0 else { return nil }
|
||||||
|
|
||||||
|
// The final size will be the same size as our first image.
|
||||||
|
let size = images.first!.size
|
||||||
|
|
||||||
|
// Create a bitmap context manually
|
||||||
|
guard let bitmapContext = CGContext(
|
||||||
|
data: nil,
|
||||||
|
width: Int(size.width),
|
||||||
|
height: Int(size.height),
|
||||||
|
bitsPerComponent: 8,
|
||||||
|
bytesPerRow: 0,
|
||||||
|
space: CGColorSpaceCreateDeviceRGB(),
|
||||||
|
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
|
||||||
|
) else { return nil }
|
||||||
|
|
||||||
|
// Clear the context
|
||||||
|
bitmapContext.setFillColor(.clear)
|
||||||
|
bitmapContext.fill(.init(origin: .zero, size: size))
|
||||||
|
|
||||||
|
// Draw each image with its corresponding blend mode
|
||||||
|
for (index, image) in images.enumerated() {
|
||||||
|
guard let cgImage = image.cgImage(
|
||||||
|
forProposedRect: nil,
|
||||||
|
context: nil,
|
||||||
|
hints: nil
|
||||||
|
) else { return nil }
|
||||||
|
|
||||||
|
let blendMode = blendingModes[index]
|
||||||
|
bitmapContext.setBlendMode(blendMode)
|
||||||
|
bitmapContext.draw(cgImage, in: CGRect(origin: .zero, size: size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a CGImage from the context
|
||||||
|
guard let combinedCGImage = bitmapContext.makeImage() else { return nil }
|
||||||
|
|
||||||
|
// Wrap the CGImage in an NSImage
|
||||||
|
return NSImage(cgImage: combinedCGImage, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply a gradient onto this image, using this image as a mask.
|
||||||
|
func gradient(colors: [NSColor]) -> NSImage? {
|
||||||
|
let resultImage = NSImage(size: size)
|
||||||
|
resultImage.lockFocus()
|
||||||
|
defer { resultImage.unlockFocus() }
|
||||||
|
|
||||||
|
// Draw the gradient
|
||||||
|
guard let gradient = NSGradient(colors: colors) else { return nil }
|
||||||
|
gradient.draw(in: .init(origin: .zero, size: size), angle: 90)
|
||||||
|
|
||||||
|
// Apply the mask
|
||||||
|
draw(at: .zero, from: .zero, operation: .destinationIn, fraction: 1.0)
|
||||||
|
|
||||||
|
return resultImage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tint an NSImage with the given color by applying a basic fill on top of it.
|
||||||
|
func tint(color: NSColor) -> NSImage? {
|
||||||
|
// Create a new image with the same size as the base image
|
||||||
|
let newImage = NSImage(size: size)
|
||||||
|
|
||||||
|
// Draw into the new image
|
||||||
|
newImage.lockFocus()
|
||||||
|
defer { newImage.unlockFocus() }
|
||||||
|
|
||||||
|
// Set up the drawing context
|
||||||
|
guard let context = NSGraphicsContext.current?.cgContext else { return nil }
|
||||||
|
defer { context.restoreGState() }
|
||||||
|
|
||||||
|
// Draw the base image
|
||||||
|
guard let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) else { return nil }
|
||||||
|
context.draw(cgImage, in: .init(origin: .zero, size: size))
|
||||||
|
|
||||||
|
// Set the tint color and blend mode
|
||||||
|
context.setFillColor(color.cgColor)
|
||||||
|
context.setBlendMode(.sourceAtop)
|
||||||
|
|
||||||
|
// Apply the tint color over the entire image
|
||||||
|
context.fill(.init(origin: .zero, size: size))
|
||||||
|
|
||||||
|
return newImage
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user