Skip to content

Commit 6f83eca

Browse files
committed
Implement Image.withCGImage(_:)
1 parent 44956cb commit 6f83eca

4 files changed

Lines changed: 99 additions & 1 deletion

File tree

Sources/EasyImagy/ImageCoreGraphics.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,15 @@
332332
bitmapInfo: Image<UInt8>.bitmapInfo
333333
)
334334
}
335+
336+
public func withCGImage<R>(_ body: (CGImage) throws -> R) rethrows -> R {
337+
return try Image.withGeneratedCGImage(
338+
image: self,
339+
colorSpace: Image<UInt8>.colorSpace,
340+
bitmapInfo: Image<UInt8>.bitmapInfo,
341+
body: body
342+
)
343+
}
335344
}
336345

337346
extension Image where Pixel == UInt16 {
@@ -373,6 +382,15 @@
373382
bitmapInfo: Image<UInt16>.bitmapInfo
374383
)
375384
}
385+
386+
public func withCGImage<R>(_ body: (CGImage) throws -> R) rethrows -> R {
387+
return try Image.withGeneratedCGImage(
388+
image: self,
389+
colorSpace: Image<UInt16>.colorSpace,
390+
bitmapInfo: Image<UInt16>.bitmapInfo,
391+
body: body
392+
)
393+
}
376394
}
377395

378396
extension Image where Pixel == UInt32 {
@@ -414,6 +432,15 @@
414432
bitmapInfo: Image<UInt32>.bitmapInfo
415433
)
416434
}
435+
436+
public func withCGImage<R>(_ body: (CGImage) throws -> R) rethrows -> R {
437+
return try Image.withGeneratedCGImage(
438+
image: self,
439+
colorSpace: Image<UInt32>.colorSpace,
440+
bitmapInfo: Image<UInt32>.bitmapInfo,
441+
body: body
442+
)
443+
}
417444
}
418445

419446
extension Image where Pixel == Float {

Sources/EasyImagy/ImageCoreGraphics.swift.gyb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,15 @@
198198
bitmapInfo: Image<${type}>.bitmapInfo
199199
)
200200
}
201+
202+
public func withCGImage<R>(_ body: (CGImage) throws -> R) rethrows -> R {
203+
return try Image.withGeneratedCGImage(
204+
image: self,
205+
colorSpace: Image<${type}>.colorSpace,
206+
bitmapInfo: Image<${type}>.bitmapInfo,
207+
body: body
208+
)
209+
}
201210
}
202211
% end
203212
%

Sources/EasyImagy/ImageInternal.swift

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,47 @@ extension Image { // Gray
173173
intent: CGColorRenderingIntent.defaultIntent
174174
)!
175175
}
176-
176+
177+
internal static func withGeneratedCGImage<R, Pixel>(
178+
image: Image<Pixel>,
179+
colorSpace: CGColorSpace,
180+
bitmapInfo: CGBitmapInfo,
181+
body: (CGImage) throws -> R
182+
) rethrows -> R {
183+
var image = image
184+
let bytesPerPixel = MemoryLayout<Pixel>.size
185+
let length = image.count * bytesPerPixel
186+
let width = image.width
187+
let height = image.height
188+
189+
return try image.pixels.withUnsafeMutableBytes { bytes in
190+
let provider: CGDataProvider = CGDataProvider(data: Data(
191+
bytesNoCopy: bytes.baseAddress!,
192+
// `baseAddress` is `nil` if `bytes.count` is `0`.
193+
// However creating a `CGImage` with 0 pixels is illegal.
194+
// So calling this method with empty images are logic failures
195+
// and using `!` is justified.
196+
count: length,
197+
deallocator: .none
198+
) as CFData)!
199+
200+
let cgImage = CGImage(
201+
width: width,
202+
height: height,
203+
bitsPerComponent: bytesPerPixel * 8,
204+
bitsPerPixel: bytesPerPixel * 8,
205+
bytesPerRow: bytesPerPixel * width,
206+
space: colorSpace,
207+
bitmapInfo: bitmapInfo,
208+
provider: provider,
209+
decode: nil,
210+
shouldInterpolate: false,
211+
intent: CGColorRenderingIntent.defaultIntent
212+
)!
213+
214+
return try body(cgImage)
215+
}
216+
}
177217
}
178218

179219
#endif

Tests/EasyImagyTests/ImageCoreGraphicsTest.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,28 @@ import EasyImagy
9595
XCTAssertEqual(restored, image)
9696
}
9797
}
98+
99+
func testWithCGImage() {
100+
let image = Image<UInt8>(width: 3, height: 2, pixels: [
101+
1, 2, 3,
102+
4, 5, 6,
103+
])
104+
105+
image.withCGImage { cgImage in
106+
let restored = Image<UInt8>(cgImage: cgImage)
107+
108+
XCTAssertEqual(restored.width, 3)
109+
XCTAssertEqual(restored.height, 2)
110+
111+
XCTAssertEqual(restored[0, 0], 1)
112+
XCTAssertEqual(restored[1, 0], 2)
113+
XCTAssertEqual(restored[2, 0], 3)
114+
115+
XCTAssertEqual(restored[0, 1], 4)
116+
XCTAssertEqual(restored[1, 1], 5)
117+
XCTAssertEqual(restored[2, 1], 6)
118+
}
119+
}
98120
}
99121

100122
#endif

0 commit comments

Comments
 (0)