ghostty/macos/Sources/Helpers/NSImage+Extension.swift
2024-12-21 19:31:30 -08:00

91 lines
3.2 KiB
Swift

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
}
}