├── Assets
├── in.jpg
├── banner.png
└── fx
│ ├── out_hue.jpg
│ ├── out_blur.jpg
│ ├── out_clamp.jpg
│ ├── out_edge.jpg
│ ├── out_gamma.jpg
│ ├── out_range.jpg
│ ├── out_sepia.jpg
│ ├── out_slope.jpg
│ ├── out_twirl.jpg
│ ├── out_contrast.jpg
│ ├── out_inverted.jpg
│ ├── out_opacity.jpg
│ ├── out_quantize.jpg
│ ├── out_sharpen.jpg
│ ├── out_brightness.jpg
│ ├── out_saturation.jpg
│ ├── out_threshold.jpg
│ └── out_kaleidoscope.jpg
├── .gitignore
├── .swiftpm
└── xcode
│ ├── package.xcworkspace
│ └── contents.xcworkspacedata
│ └── xcshareddata
│ └── xcschemes
│ └── ImageFX.xcscheme
├── Package.swift
├── LICENSE
├── Package.resolved
├── Sources
└── ImageFX
│ └── ImageFX.swift
└── README.md
/Assets/in.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/in.jpg
--------------------------------------------------------------------------------
/Assets/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/banner.png
--------------------------------------------------------------------------------
/Assets/fx/out_hue.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_hue.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_blur.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_blur.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_clamp.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_clamp.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_edge.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_edge.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_gamma.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_gamma.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_range.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_range.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_sepia.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_sepia.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_slope.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_slope.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_twirl.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_twirl.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_contrast.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_contrast.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_inverted.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_inverted.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_opacity.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_opacity.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_quantize.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_quantize.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_sharpen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_sharpen.jpg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 |
7 | Pods/*
8 | *.xcworkspace
--------------------------------------------------------------------------------
/Assets/fx/out_brightness.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_brightness.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_saturation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_saturation.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_threshold.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_threshold.jpg
--------------------------------------------------------------------------------
/Assets/fx/out_kaleidoscope.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/ImageFX/HEAD/Assets/fx/out_kaleidoscope.jpg
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.9
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "ImageFX",
7 | platforms: [
8 | .iOS(.v16),
9 | .macOS(.v13),
10 | ],
11 | products: [
12 | .library(
13 | name: "ImageFX",
14 | targets: ["ImageFX"]
15 | ),
16 | ],
17 | dependencies: [
18 | .package(
19 | url: "http://github.com/heestand-xyz/AsyncGraphics",
20 | from: "2.0.5"
21 | ),
22 | .package(
23 | url: "https://github.com/heestand-xyz/PixelColor",
24 | from: "2.2.2"
25 | ),
26 | ],
27 | targets: [
28 | .target(
29 | name: "ImageFX",
30 | dependencies: [
31 | "AsyncGraphics",
32 | "PixelColor",
33 | ]
34 | ),
35 | ]
36 | )
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Anton Heestand
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "asyncgraphics",
5 | "kind" : "remoteSourceControl",
6 | "location" : "http://github.com/heestand-xyz/AsyncGraphics",
7 | "state" : {
8 | "revision" : "cfb9dfbc0ec094084e4d9bd52a368c0fd8738994",
9 | "version" : "2.0.5"
10 | }
11 | },
12 | {
13 | "identity" : "coregraphicsextensions",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/heestand-xyz/CoreGraphicsExtensions",
16 | "state" : {
17 | "revision" : "10a0403455eff7b8217054960d6391c98c0dea37",
18 | "version" : "1.7.2"
19 | }
20 | },
21 | {
22 | "identity" : "pixelcolor",
23 | "kind" : "remoteSourceControl",
24 | "location" : "https://github.com/heestand-xyz/PixelColor",
25 | "state" : {
26 | "revision" : "985d12c8339bc0ae08803a0abde32c2e4696c4ce",
27 | "version" : "2.2.2"
28 | }
29 | },
30 | {
31 | "identity" : "spatialextensions",
32 | "kind" : "remoteSourceControl",
33 | "location" : "https://github.com/heestand-xyz/SpatialExtensions",
34 | "state" : {
35 | "revision" : "f2b83a6928847f8cc9fa792af5f7d003a34b33c9",
36 | "version" : "0.1.2"
37 | }
38 | },
39 | {
40 | "identity" : "swift-syntax",
41 | "kind" : "remoteSourceControl",
42 | "location" : "https://github.com/apple/swift-syntax",
43 | "state" : {
44 | "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d",
45 | "version" : "509.1.1"
46 | }
47 | },
48 | {
49 | "identity" : "texturemap",
50 | "kind" : "remoteSourceControl",
51 | "location" : "https://github.com/heestand-xyz/TextureMap",
52 | "state" : {
53 | "revision" : "f07a0127f59217933b9b89c2eda4dd26a2da701f",
54 | "version" : "1.0.2"
55 | }
56 | },
57 | {
58 | "identity" : "videoframes",
59 | "kind" : "remoteSourceControl",
60 | "location" : "https://github.com/heestand-xyz/VideoFrames",
61 | "state" : {
62 | "revision" : "3de476e958b765699eb2f98ca42336acfc262f49",
63 | "version" : "1.1.1"
64 | }
65 | }
66 | ],
67 | "version" : 2
68 | }
69 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/ImageFX.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
62 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/Sources/ImageFX/ImageFX.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | #if os(macOS)
3 | import Cocoa
4 | #else
5 | import UIKit
6 | #endif
7 | import AsyncGraphics
8 | import PixelColor
9 |
10 | #if os(macOS)
11 | public typealias FXImage = NSImage
12 | public typealias FXColor = NSColor
13 | #else
14 | public typealias FXImage = UIImage
15 | public typealias FXColor = UIColor
16 | #endif
17 |
18 |
19 | public extension FXImage {
20 |
21 | func fx(
22 | edit: (Graphic) async throws -> Graphic
23 | ) async throws -> FXImage {
24 | try await edit(Graphic.image(self)).image
25 | }
26 | }
27 |
28 | public extension FXImage {
29 |
30 | func fxBlur(radius: CGFloat) async throws -> FXImage {
31 | try await fx { try await $0.blurred(radius: radius) }
32 | }
33 |
34 | func fxRainbowBlur(radius: CGFloat) async throws -> FXImage {
35 | try await fx { try await $0.rainbowBlurredCircle(radius: radius) }
36 | }
37 |
38 | func fxEdge(amplitude: CGFloat = 1.0, distance: CGFloat = 1.0) async throws -> FXImage {
39 | try await fx { try await $0.edge(amplitude: amplitude, distance: distance) }
40 | }
41 |
42 | func fxClamp(low: CGFloat = 0.0, high: CGFloat = 1.0) async throws -> FXImage {
43 | try await fx { try await $0.clamp(low: low, high: high) }
44 | }
45 |
46 | func fxKaleidoscope(divisions: Int = 12, mirror: Bool = true) async throws -> FXImage {
47 | try await fx { try await $0.kaleidoscope(count: divisions, mirror: mirror) }
48 | }
49 |
50 | func fxBrightness(_ value: CGFloat) async throws -> FXImage {
51 | try await fx { try await $0.brightness(value) }
52 | }
53 |
54 | func fxDarkness(_ value: CGFloat) async throws -> FXImage {
55 | try await fx { try await $0.darkness(value) }
56 | }
57 |
58 | func fxContrast(_ value: CGFloat) async throws -> FXImage {
59 | try await fx { try await $0.contrast(value) }
60 | }
61 |
62 | func fxGamma(_ value: CGFloat) async throws -> FXImage {
63 | try await fx { try await $0.gamma(value) }
64 | }
65 |
66 | func fxInvert() async throws -> FXImage {
67 | try await fx { try await $0.inverted() }
68 | }
69 |
70 | func fxOpacity(_ value: CGFloat) async throws -> FXImage {
71 | try await fx { try await $0.opacity(value) }
72 | }
73 |
74 | func fxQuantize(fraction: CGFloat = 0.1) async throws -> FXImage {
75 | try await fx { try await $0.quantize(fraction) }
76 | }
77 |
78 | func fxSharpen(_ value: CGFloat = 2.0) async throws -> FXImage {
79 | try await fx { try await $0.sharpen(value) }
80 | }
81 |
82 | func fxSlope(_ value: CGFloat = 1.0) async throws -> FXImage {
83 | try await fx { try await $0.slope(amplitude: value) }
84 | }
85 |
86 | func fxThreshold(_ value: CGFloat = 0.5) async throws -> FXImage {
87 | try await fx { try await $0.threshold(value) }
88 | }
89 |
90 | func fxRange(
91 | inLow: CGFloat = 0.0,
92 | inHigh: CGFloat = 1.0,
93 | outLow: CGFloat = 0.0,
94 | outHigh: CGFloat = 1.0
95 | ) async throws -> FXImage {
96 | try await fx {
97 | try await $0.range(
98 | referenceLow: inLow,
99 | referenceHigh: inHigh,
100 | targetLow: outLow,
101 | targetHigh: outHigh
102 | )
103 | }
104 | }
105 |
106 | func fxSaturation(_ value: CGFloat) async throws -> FXImage {
107 | try await fx { try await $0.saturated(value) }
108 | }
109 |
110 | func fxMonochrome() async throws -> FXImage {
111 | try await fx { try await $0.monochrome() }
112 | }
113 |
114 | func fxHue(_ value: Angle) async throws -> FXImage {
115 | try await fx { try await $0.hue(value) }
116 | }
117 |
118 | func fxSepia(color: FXColor) async throws -> FXImage {
119 | try await fx { try await $0.sepia(color: PixelColor(color)) }
120 | }
121 |
122 | func fxFlipX() async throws -> FXImage {
123 | try await fx { try await $0.mirroredHorizontally() }
124 | }
125 |
126 | func fxFlipY() async throws -> FXImage {
127 | try await fx { try await $0.mirroredVertically() }
128 | }
129 |
130 | func fxFlopLeft() async throws -> FXImage {
131 | try await fx { try await $0.rotatedLeft() }
132 | }
133 |
134 | func fxFlopRight() async throws -> FXImage {
135 | try await fx { try await $0.rotatedRight() }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # ImageFX
4 |
5 | Powered by Metal with [AsyncGraphics](https://github.com/heestand-xyz/AsyncGraphics)
6 |
7 | ## Install
8 |
9 | ### Swift Package
10 |
11 | ```swift
12 | .package(url: "https://github.com/heestand-xyz/ImageFX", from: "2.0.0")
13 | ```
14 |
15 | > In version `2.0.0` spatial values are not relative anymore, they are absolute in pixel space
16 |
17 | ## Effects
18 |
19 | > All examples work with `UIImage` and `NSImage`
20 |
21 |
22 |
23 | ```swift
24 | let image: UIImage = UIImage(named: "Kite")!
25 | ```
26 |
27 | ### Blur
28 |
29 |
30 | ```swift
31 | let fxImage: UIImage = try await image.fxBlur(100)
32 | ```
33 |
34 | ### Edge
35 |
36 |
37 | ```swift
38 | let fxImage: UIImage = try await image.fxEdge()
39 | ```
40 |
41 | ### Clamp
42 |
43 |
44 | ```swift
45 | let fxImage: UIImage = try await image.fxClamp(low: 0.25, high: 0.75)
46 | ```
47 |
48 | ### Kaleidoscope
49 |
50 |
51 | ```swift
52 | let fxImage: UIImage = try await image.fxKaleidoscope()
53 | ```
54 |
55 | ### Levels: Brightness
56 |
57 |
58 | ```swift
59 | let fxImage: UIImage = try await image.fxBrightness(2.0)
60 | ```
61 |
62 | ### Levels: Gamma
63 |
64 |
65 | ```swift
66 | let fxImage: UIImage = try await image.fxGamma(0.5)
67 | ```
68 |
69 | ### Levels: Invert
70 |
71 |
72 | ```swift
73 | let fxImage: UIImage = try await image.fxInvert()
74 | ```
75 |
76 | ### Levels: Opacity
77 |
78 |
79 | ```swift
80 | let fxImage: UIImage = try await image.fxOpacity(0.5)
81 | ```
82 |
83 |
84 | ### Levels: Contrast
85 |
86 |
87 | ```swift
88 | let fxImage: UIImage = try await image.fxContrast(2.0)
89 | ```
90 |
91 | ### Quantize
92 |
93 |
94 | ```swift
95 | let fxImage: UIImage = try await image.fxQuantize(0.125)
96 | ```
97 |
98 | ### Sharpen
99 |
100 |
101 | ```swift
102 | let fxImage: UIImage = try await image.fxSharpen(2.0)
103 | ```
104 |
105 | ### Slope
106 |
107 |
108 | ```swift
109 | let fxImage: UIImage = try await image.fxSlope()
110 | ```
111 |
112 | ### Threshold
113 |
114 |
115 | ```swift
116 | let fxImage: UIImage = try await image.fxThreshold()
117 | ```
118 |
119 | ### Sepia
120 |
121 |
122 | ```swift
123 | let fxImage: UIImage = try await image.fxSepia(color: .orange)
124 | ```
125 |
126 | ### Range
127 |
128 |
129 | ```swift
130 | let fxImageA: UIImage = try await image.fxRange(inLow: 0.0, inHigh: 1.0, outLow: 0.0, outHigh: 0.5)
131 | ```
132 |
133 | ### Saturation
134 |
135 |
136 | ```swift
137 | let fxImageA: UIImage = try await image.fxSaturation(0.5)
138 | let fxImageB: UIImage = try await image.fxMonochrome()
139 | ```
140 |
141 | ### Hue
142 |
143 |
144 | ```swift
145 | let fxImage: UIImage = try await image.fxHue(.degrees(180))
146 | ```
147 |
148 | ## Custom Effects
149 |
150 | ```swift
151 | import AsyncGraphics
152 | ```
153 |
154 | ```swift
155 | let fxImage: UIImage = try await image
156 | .fx { graphic in
157 | let noise: Graphic = try await .coloredNoise(resolution: graphic.resolution)
158 | return try await graphic.displaced(with: noise, offset: 100)
159 | }
160 | ```
161 |
--------------------------------------------------------------------------------