Skip to content

Commit

Permalink
Dithering Engine 1.8.1
Browse files Browse the repository at this point in the history
Merge pull request #9 from Eskils/develop
  • Loading branch information
Eskils authored Jul 28, 2024
2 parents dc9c401 + 958a3bd commit d97c13c
Show file tree
Hide file tree
Showing 13 changed files with 63 additions and 32 deletions.
8 changes: 4 additions & 4 deletions Documentation/Demo/Dithering.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Dithering/Dithering.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 9;
CURRENT_PROJECT_VERSION = 11;
DEVELOPMENT_ASSET_PATHS = "\"Dithering/Preview Content\"";
DEVELOPMENT_TEAM = EUKTZ7725R;
ENABLE_HARDENED_RUNTIME = NO;
Expand All @@ -373,7 +373,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.7.0;
MARKETING_VERSION = 1.8.1;
PRODUCT_BUNDLE_IDENTIFIER = com.skillbreak.Dithering;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
Expand All @@ -392,7 +392,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Dithering/Dithering.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 9;
CURRENT_PROJECT_VERSION = 11;
DEVELOPMENT_ASSET_PATHS = "\"Dithering/Preview Content\"";
DEVELOPMENT_TEAM = EUKTZ7725R;
ENABLE_HARDENED_RUNTIME = NO;
Expand All @@ -409,7 +409,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.7.0;
MARKETING_VERSION = 1.8.1;
PRODUCT_BUNDLE_IDENTIFIER = com.skillbreak.Dithering;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,9 @@ class CustomPaletteSettingsConfigurationWithView: PaletteSettingsConfigurationWi
case is BayerSettingsConfiguration:
let settingsConfiguration = paletteSettingsConfiguration as! BayerSettingsConfiguration

let intensitySubject: CurrentValueSubject<Float, Never> = CurrentValueSubject(0.5)

let views: [any SettingView] = [
NumberSettingViewDescription(subject: settingsConfiguration.thresholdMapSize, title: "Threshold Map Size", min: 0, max: 8),
OptionalSettingViewDescription(
subject: settingsConfiguration.intensity,
title: "Use intensity",
viewDescription: NumberSettingViewDescription(subject: intensitySubject, title: "Intensity", min: 0, max: 1)),
NumberSettingViewDescription(subject: settingsConfiguration.intensity, title: "Intensity", min: 0, max: 1),
BooleanSettingViewDescription(isOn: settingsConfiguration.performOnCPU, title: "Perform on CPU"),
]

Expand Down
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ let package = Package(
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "DitheringEngine",
dependencies: []),
dependencies: [],
resources: [.process("PrivacyInfo.xcprivacy")]),
.testTarget(
name: "DitheringEngineTests",
dependencies: ["DitheringEngine"]),
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ let package = Package(
dependencies: [
.package(
url: "https://github.com/Eskils/DitheringEngine",
.upToNextMinor(from: "1.8.0") // or `.upToNextMajor
.upToNextMinor(from: "1.8.1") // or `.upToNextMajor
)
],
targets: [
Expand Down Expand Up @@ -239,7 +239,7 @@ Bayer dithering is a type of ordered dithering which adds a precalculated thresh
| Name | Type | Default | Description |
|------|------|---------|-------------|
| thresholdMapSize | Int | `4` | Specifies the size of the square threshold matrix. Default is 4x4. |
| intensity | Float? | `nil` | Specifies the intensity of the noise pattern. When nil, the intensity is calculated from the thresholdMapSize. |
| intensity | Float | `1` | Specifies the intensity of the noise pattern. Intensity is calculated from the thresholdMapSize, and this property specifies the fraction of the calculated intensity to apply. |
| performOnCPU | Bool | `false` | Determines wether to perform the computation on the CPU. If false, the GPU is used for quicker performance. |

Example:
Expand Down
2 changes: 1 addition & 1 deletion Sources/DitheringEngine/DitherMethods/Dither-Bayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extension DitherMethods {
let thresholdMap = FloatingThresholdMap.generateBayerThresholdMap(n: thresholdMapSize)
let normalizationOffset = Float(thresholdMap.count) / 2
let intensityFromSize = 8 / Float(thresholdMapSize)
let thresholdMultiplier = intensity ?? intensityFromSize
let thresholdMultiplier = intensityFromSize * (intensity ?? 1)

ordered(
palette: palette,
Expand Down
21 changes: 12 additions & 9 deletions Sources/DitheringEngine/Metal/MetalOrderedDithering.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,18 @@ struct MetalFunction {
return MetalFunction(commandQueue: commandQueue, pipelineState: pipelineState, maxThreads: maxThreads)
}

static func makeTexture(width: Int, height: Int, device: MTLDevice) -> MTLTexture? {
static func makeTexture(width: Int, height: Int, usage: MTLTextureUsage, device: MTLDevice) -> MTLTexture? {
let descriptor = MTLTextureDescriptor()
descriptor.width = width
descriptor.height = height
descriptor.textureType = .type2D
descriptor.pixelFormat = .rgba8Unorm
descriptor.storageMode = .shared
descriptor.usage = [.shaderRead, .shaderWrite]
descriptor.usage = usage
return device.makeTexture(descriptor: descriptor)
}

func perform(numWidth: Int, numHeight: Int, texture: MTLTexture, commandEncoderConfiguration: @escaping (MTLComputeCommandEncoder) -> Void) throws {
func perform(numWidth: Int, numHeight: Int, commandEncoderConfiguration: @escaping (MTLComputeCommandEncoder) -> Void) throws {
guard let commandBuffer = commandQueue.makeCommandBuffer() else {
throw PerformMetalError.cannotMakeCommandBuffer
}
Expand Down Expand Up @@ -124,7 +124,10 @@ class MetalOrderedDithering {
let width = imageDescription.width
let height = imageDescription.height

guard let texture = MetalFunction.makeTexture(width: width, height: height, device: device) else {
guard
let inTexture = MetalFunction.makeTexture(width: width, height: height, usage: .shaderRead, device: device),
let outTexture = MetalFunction.makeTexture(width: width, height: height, usage: .shaderWrite, device: device)
else {
throw MetalOrderedDitheringError.cannotMakeTexture
}

Expand All @@ -133,7 +136,7 @@ class MetalOrderedDithering {
size: MTLSize(width: width, height: height, depth: 1)
)

texture.replace(
inTexture.replace(
region: fullImageRegion,
mipmapLevel: 0,
withBytes: imageDescription.buffer,
Expand All @@ -149,10 +152,10 @@ class MetalOrderedDithering {

try orderedDitheringFunction.perform(
numWidth: width,
numHeight: height,
texture: texture
numHeight: height
) { commandEncoder in
commandEncoder.setTexture(texture, index: 0)
commandEncoder.setTexture(inTexture, index: 0)
commandEncoder.setTexture(outTexture, index: 1)

let thresholdMapBuffer = device.makeBuffer(bytes: thresholdMap.buffer, length: thresholdMap.count * MemoryLayout<Float>.size, options: .storageModeShared)
commandEncoder.setBuffer(thresholdMapBuffer, offset: 0, index: 0)
Expand Down Expand Up @@ -182,7 +185,7 @@ class MetalOrderedDithering {
}

let buffer = resultImageDescription.buffer
texture.getBytes(
outTexture.getBytes(
buffer,
bytesPerRow: resultImageDescription.bytesPerRow,
from: fullImageRegion,
Expand Down
7 changes: 4 additions & 3 deletions Sources/DitheringEngine/Metal/OrderedDithering.metal
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ float4 pickColor(PaletteType paletteType, float4 baseColor, device const uint8_t
}

kernel void orderedDithering(
texture2d<float, access::read_write> texture [[texture(0)]],
texture2d<float, access::read> inTexture [[texture(0)]],
texture2d<float, access::write> outTexture [[texture(1)]],
device const float *thresholdMap [[buffer(0)]],
device const int *thresholdMapSize [[buffer(1)]],
device const uint8_t *palette [[buffer(2)]],
Expand All @@ -86,7 +87,7 @@ kernel void orderedDithering(
device const float *thresholdMultiplier [[buffer(6)]],
uint2 gid [[thread_position_in_grid]]
) {
float4 colorIn = texture.read(gid) * 255;
float4 colorIn = inTexture.read(gid) * 255;

int thresholdMapNum = *thresholdMapSize;
int thresholdMapIndex = (gid.y % thresholdMapNum) * thresholdMapNum + gid.x % thresholdMapNum;
Expand All @@ -98,6 +99,6 @@ kernel void orderedDithering(
float4 pickedColor = pickColor(*paletteType, clampedNewColor, palette, *paletteCount) / 255;
float4 resultColor = float4(pickedColor.xyz, 1);

texture.write(resultColor, gid);
outTexture.write(resultColor, gid);
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ import Combine

public protocol OrderedDitheringThresholdConfiguration: SettingsConfiguration {
var thresholdMapSize: CurrentValueSubject<Int, Never> { get }
var intensity: CurrentValueSubject<Float, Never> { get }
var size: Int { get }
}
2 changes: 1 addition & 1 deletion Sources/DitheringEngine/Palette.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ extension Palette {
case .gameBoy:
return palettes.gameBoy()
case .intellivision:
return palettes.intellivision()
return palettes.intellivision(preferNoGray: preferNoGray)
case .custom:
let settings = (settings as? CustomPaletteSettingsConfiguration) ?? .init()
return settings.palette.value
Expand Down
3 changes: 2 additions & 1 deletion Sources/DitheringEngine/Palettes/Palette-Intellivision.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
import Foundation

extension Palettes {
public func intellivision() -> BytePalette {
public func intellivision(preferNoGray: Bool) -> BytePalette {
.from(lutCollection: ByteLUTCollection(entries: [
.from32Bits(0x000000),
.from32Bits(0xFFFCFF),
.from32Bits(0x002DFF),
.from32Bits(preferNoGray ? 0x000000 : 0xBDACC8),

.from32Bits(0xFF3E00),
.from32Bits(0xC9D464),
Expand Down
29 changes: 29 additions & 0 deletions Sources/DitheringEngine/PrivacyInfo.xcprivacy
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string></string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string></string>
</array>
</dict>
</array>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict/>
</array>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public final class BayerSettingsConfiguration: SettingsConfiguration, OrderedDit
public let thresholdMapSize: CurrentValueSubject<Int, Never>

/// The intensity value to multiply the threshold map by.
public let intensity: CurrentValueSubject<Float?, Never>
public let intensity: CurrentValueSubject<Float, Never>

/// Determines wether to perform the computation on the CPU. If false, the GPU is used for quicker performance.
public let performOnCPU: CurrentValueSubject<Bool, Never>
Expand All @@ -23,7 +23,7 @@ public final class BayerSettingsConfiguration: SettingsConfiguration, OrderedDit
return 2 << (exponent - 1)
}

public init(thresholdMapSize: Int = 4, intensity: Float? = nil, performOnCPU: Bool = false) {
public init(thresholdMapSize: Int = 4, intensity: Float = 1, performOnCPU: Bool = false) {
self.thresholdMapSize = CurrentValueSubject(thresholdMapSize)
self.intensity = CurrentValueSubject(intensity)
self.performOnCPU = CurrentValueSubject(performOnCPU)
Expand Down Expand Up @@ -58,7 +58,7 @@ extension BayerSettingsConfiguration: Codable {

let thresholdMapSize = try container.decode(Int.self, forKey: .thresholdMapSize)
let performOnCPU = try container.decode(Bool.self, forKey: .performOnCPU)
let intensity = try container.decodeIfPresent(Float.self, forKey: .intensity)
let intensity = try container.decode(Float.self, forKey: .intensity)

self.init(thresholdMapSize: thresholdMapSize, intensity: intensity, performOnCPU: performOnCPU)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/DitheringEngine/ThresholdMap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ extension ThresholdMap {

let i = y * num + x

buffer[i] = Float(4 * term + offset + 1)
buffer[i] = Float(4 * term + offset)
}
}

Expand Down

0 comments on commit d97c13c

Please sign in to comment.