From e5d56bb505f77ef41b8cfcb8e6281b10e9aaaa53 Mon Sep 17 00:00:00 2001 From: Eskil Gjerde Sviggum Date: Thu, 25 Jul 2024 20:22:55 +0200 Subject: [PATCH 1/9] Increment verison --- Documentation/Demo/Dithering.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/Demo/Dithering.xcodeproj/project.pbxproj b/Documentation/Demo/Dithering.xcodeproj/project.pbxproj index a2cfeaf..66fc9fa 100644 --- a/Documentation/Demo/Dithering.xcodeproj/project.pbxproj +++ b/Documentation/Demo/Dithering.xcodeproj/project.pbxproj @@ -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 = 10; DEVELOPMENT_ASSET_PATHS = "\"Dithering/Preview Content\""; DEVELOPMENT_TEAM = EUKTZ7725R; ENABLE_HARDENED_RUNTIME = NO; @@ -373,7 +373,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.7.0; + MARKETING_VERSION = 1.8.0; PRODUCT_BUNDLE_IDENTIFIER = com.skillbreak.Dithering; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -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 = 10; DEVELOPMENT_ASSET_PATHS = "\"Dithering/Preview Content\""; DEVELOPMENT_TEAM = EUKTZ7725R; ENABLE_HARDENED_RUNTIME = NO; @@ -409,7 +409,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.7.0; + MARKETING_VERSION = 1.8.0; PRODUCT_BUNDLE_IDENTIFIER = com.skillbreak.Dithering; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; From 8a2ba15beb107a85f415efb0d8f7510943a521a1 Mon Sep 17 00:00:00 2001 From: Eskil Gjerde Sviggum Date: Thu, 25 Jul 2024 20:23:38 +0200 Subject: [PATCH 2/9] Minor fixes on the Bayer dither algorithm --- Sources/DitheringEngine/DitherMethods/Dither-Bayer.swift | 2 +- Sources/DitheringEngine/ThresholdMap.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/DitheringEngine/DitherMethods/Dither-Bayer.swift b/Sources/DitheringEngine/DitherMethods/Dither-Bayer.swift index 4fcc4c4..e018aa2 100644 --- a/Sources/DitheringEngine/DitherMethods/Dither-Bayer.swift +++ b/Sources/DitheringEngine/DitherMethods/Dither-Bayer.swift @@ -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, diff --git a/Sources/DitheringEngine/ThresholdMap.swift b/Sources/DitheringEngine/ThresholdMap.swift index 9bd8618..c96da30 100644 --- a/Sources/DitheringEngine/ThresholdMap.swift +++ b/Sources/DitheringEngine/ThresholdMap.swift @@ -89,7 +89,7 @@ extension ThresholdMap { let i = y * num + x - buffer[i] = Float(4 * term + offset + 1) + buffer[i] = Float(4 * term + offset) } } From 469af9d711beb70ce6e0195f19a21312dacfb7fa Mon Sep 17 00:00:00 2001 From: Eskil Gjerde Sviggum Date: Thu, 25 Jul 2024 20:39:37 +0200 Subject: [PATCH 3/9] Add privacy manifest --- Package.swift | 3 +- Sources/DitheringEngine/PrivacyInfo.xcprivacy | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 Sources/DitheringEngine/PrivacyInfo.xcprivacy diff --git a/Package.swift b/Package.swift index d870434..f988fb0 100644 --- a/Package.swift +++ b/Package.swift @@ -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"]), diff --git a/Sources/DitheringEngine/PrivacyInfo.xcprivacy b/Sources/DitheringEngine/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..9ac078d --- /dev/null +++ b/Sources/DitheringEngine/PrivacyInfo.xcprivacy @@ -0,0 +1,29 @@ + + + + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + + + + + NSPrivacyAccessedAPITypes + + + + NSPrivacyTrackingDomains + + NSPrivacyTracking + + + From 26d52a3c42d68cfacf59589124d98b2f0f550c6b Mon Sep 17 00:00:00 2001 From: Eskil Gjerde Sviggum Date: Fri, 26 Jul 2024 17:11:29 +0200 Subject: [PATCH 4/9] Make intensity non-optional in BayerSettingsConfiguration --- .../OrderedDitheringThresholdConfiguration.swift | 1 + .../SettingsConfigurations/BayerSettingsConfiguration.swift | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Sources/DitheringEngine/OrderedDitheringThresholdConfiguration.swift b/Sources/DitheringEngine/OrderedDitheringThresholdConfiguration.swift index 85fb2ca..a0cfa50 100644 --- a/Sources/DitheringEngine/OrderedDitheringThresholdConfiguration.swift +++ b/Sources/DitheringEngine/OrderedDitheringThresholdConfiguration.swift @@ -9,5 +9,6 @@ import Combine public protocol OrderedDitheringThresholdConfiguration: SettingsConfiguration { var thresholdMapSize: CurrentValueSubject { get } + var intensity: CurrentValueSubject { get } var size: Int { get } } diff --git a/Sources/DitheringEngine/SettingsConfigurations/BayerSettingsConfiguration.swift b/Sources/DitheringEngine/SettingsConfigurations/BayerSettingsConfiguration.swift index 4cad60d..358b21e 100644 --- a/Sources/DitheringEngine/SettingsConfigurations/BayerSettingsConfiguration.swift +++ b/Sources/DitheringEngine/SettingsConfigurations/BayerSettingsConfiguration.swift @@ -13,7 +13,7 @@ public final class BayerSettingsConfiguration: SettingsConfiguration, OrderedDit public let thresholdMapSize: CurrentValueSubject /// The intensity value to multiply the threshold map by. - public let intensity: CurrentValueSubject + public let intensity: CurrentValueSubject /// Determines wether to perform the computation on the CPU. If false, the GPU is used for quicker performance. public let performOnCPU: CurrentValueSubject @@ -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) @@ -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) } From 6e0188eacff4698f8063013937a9886d94cf75f4 Mon Sep 17 00:00:00 2001 From: Eskil Gjerde Sviggum Date: Fri, 26 Jul 2024 17:11:47 +0200 Subject: [PATCH 5/9] Add gray to Intellivision palette --- Sources/DitheringEngine/Palette.swift | 2 +- Sources/DitheringEngine/Palettes/Palette-Intellivision.swift | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/DitheringEngine/Palette.swift b/Sources/DitheringEngine/Palette.swift index 6bba33c..97761e1 100644 --- a/Sources/DitheringEngine/Palette.swift +++ b/Sources/DitheringEngine/Palette.swift @@ -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 diff --git a/Sources/DitheringEngine/Palettes/Palette-Intellivision.swift b/Sources/DitheringEngine/Palettes/Palette-Intellivision.swift index b90adff..772cda7 100644 --- a/Sources/DitheringEngine/Palettes/Palette-Intellivision.swift +++ b/Sources/DitheringEngine/Palettes/Palette-Intellivision.swift @@ -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), From ae48ae05f8f0bbd0bb0b7a5aa5794ebb474d8bee Mon Sep 17 00:00:00 2001 From: Eskil Gjerde Sviggum Date: Sun, 28 Jul 2024 20:41:27 +0200 Subject: [PATCH 6/9] Fix ordered dithering on the GPU for simulators --- .../Metal/MetalOrderedDithering.swift | 21 +++++++++++-------- .../Metal/OrderedDithering.metal | 7 ++++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Sources/DitheringEngine/Metal/MetalOrderedDithering.swift b/Sources/DitheringEngine/Metal/MetalOrderedDithering.swift index 83e7d00..c85831b 100644 --- a/Sources/DitheringEngine/Metal/MetalOrderedDithering.swift +++ b/Sources/DitheringEngine/Metal/MetalOrderedDithering.swift @@ -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 } @@ -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 } @@ -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, @@ -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.size, options: .storageModeShared) commandEncoder.setBuffer(thresholdMapBuffer, offset: 0, index: 0) @@ -182,7 +185,7 @@ class MetalOrderedDithering { } let buffer = resultImageDescription.buffer - texture.getBytes( + outTexture.getBytes( buffer, bytesPerRow: resultImageDescription.bytesPerRow, from: fullImageRegion, diff --git a/Sources/DitheringEngine/Metal/OrderedDithering.metal b/Sources/DitheringEngine/Metal/OrderedDithering.metal index 650f57e..d7ce1f8 100644 --- a/Sources/DitheringEngine/Metal/OrderedDithering.metal +++ b/Sources/DitheringEngine/Metal/OrderedDithering.metal @@ -76,7 +76,8 @@ float4 pickColor(PaletteType paletteType, float4 baseColor, device const uint8_t } kernel void orderedDithering( - texture2d texture [[texture(0)]], + texture2d inTexture [[texture(0)]], + texture2d outTexture [[texture(1)]], device const float *thresholdMap [[buffer(0)]], device const int *thresholdMapSize [[buffer(1)]], device const uint8_t *palette [[buffer(2)]], @@ -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; @@ -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); } From 037d1f58976405d29a5bb8b0ec3fbafccffac45d Mon Sep 17 00:00:00 2001 From: Eskil Gjerde Sviggum Date: Sun, 28 Jul 2024 21:45:07 +0200 Subject: [PATCH 7/9] Update docs --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 791e8f2..2541e6c 100644 --- a/README.md +++ b/README.md @@ -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: [ @@ -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: From 97785795e5e7733bb4d421f789930b08625f85d5 Mon Sep 17 00:00:00 2001 From: Eskil Gjerde Sviggum Date: Sun, 28 Jul 2024 21:45:20 +0200 Subject: [PATCH 8/9] Fix demo app --- .../Dithering/PaletteSettingsConfiguration+Extension.swift | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Documentation/Demo/Dithering/PaletteSettingsConfiguration+Extension.swift b/Documentation/Demo/Dithering/PaletteSettingsConfiguration+Extension.swift index 316183e..69e04ff 100644 --- a/Documentation/Demo/Dithering/PaletteSettingsConfiguration+Extension.swift +++ b/Documentation/Demo/Dithering/PaletteSettingsConfiguration+Extension.swift @@ -153,14 +153,9 @@ class CustomPaletteSettingsConfigurationWithView: PaletteSettingsConfigurationWi case is BayerSettingsConfiguration: let settingsConfiguration = paletteSettingsConfiguration as! BayerSettingsConfiguration - let intensitySubject: CurrentValueSubject = 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"), ] From 958a3bd5ecefad7452647d479c447898f009961b Mon Sep 17 00:00:00 2001 From: Eskil Gjerde Sviggum Date: Sun, 28 Jul 2024 21:45:28 +0200 Subject: [PATCH 9/9] Increment version --- Documentation/Demo/Dithering.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/Demo/Dithering.xcodeproj/project.pbxproj b/Documentation/Demo/Dithering.xcodeproj/project.pbxproj index 66fc9fa..9bf843a 100644 --- a/Documentation/Demo/Dithering.xcodeproj/project.pbxproj +++ b/Documentation/Demo/Dithering.xcodeproj/project.pbxproj @@ -356,7 +356,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Dithering/Dithering.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 10; + CURRENT_PROJECT_VERSION = 11; DEVELOPMENT_ASSET_PATHS = "\"Dithering/Preview Content\""; DEVELOPMENT_TEAM = EUKTZ7725R; ENABLE_HARDENED_RUNTIME = NO; @@ -373,7 +373,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.8.0; + MARKETING_VERSION = 1.8.1; PRODUCT_BUNDLE_IDENTIFIER = com.skillbreak.Dithering; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -392,7 +392,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Dithering/Dithering.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 10; + CURRENT_PROJECT_VERSION = 11; DEVELOPMENT_ASSET_PATHS = "\"Dithering/Preview Content\""; DEVELOPMENT_TEAM = EUKTZ7725R; ENABLE_HARDENED_RUNTIME = NO; @@ -409,7 +409,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.8.0; + MARKETING_VERSION = 1.8.1; PRODUCT_BUNDLE_IDENTIFIER = com.skillbreak.Dithering; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";