@@ -12,7 +12,8 @@ import gd
1212public enum ImportableFormat {
1313 case bmp, gif, jpg, png, tiff, tga, wbmp, webp, avif, any
1414
15- public func imagePtr( of data: Data ) throws -> gdImagePtr {
15+ /// Returns a managed GD image that will automatically be destroyed when deallocated
16+ public func image( of data: Data ) throws -> GDImage {
1617 if case . any = self {
1718 return try tryAllFormats ( data: data)
1819 }
@@ -32,16 +33,16 @@ public enum ImportableFormat {
3233 case . any: gdImageCreateFromPngPtr // Never reached
3334 }
3435
35- guard let image = creator ( size, ptr) else {
36+ guard let imagePtr = creator ( size, ptr) else {
3637 throw Error . invalidFormat
3738 }
38- return image
39+ return GDImage ( imagePtr )
3940 }
4041
41- private func tryAllFormats( data: Data ) throws -> gdImagePtr {
42+ private func tryAllFormats( data: Data ) throws -> GDImage {
4243 let formats : [ ImportableFormat ] = [ . jpg, . png, . gif, . webp, . tiff, . bmp, . wbmp]
4344 for format in formats {
44- if let image = try ? format. imagePtr ( of: data) {
45+ if let image = try ? format. image ( of: data) {
4546 return image
4647 }
4748 }
@@ -61,45 +62,49 @@ public enum ExportableFormat {
6162 case webp
6263 case avif
6364
64- public func data( of imagePtr : gdImagePtr ) throws -> Data {
65+ public func data( of gdImage : GDImage ) throws -> Data {
6566 var size : Int32 = 0
6667
6768 let bytes : UnsafeMutableRawPointer ? = switch self {
6869 case . bmp( let compress) :
69- gdImageBmpPtr ( imagePtr , & size, compress ? 1 : 0 )
70+ gdImageBmpPtr ( gdImage . ptr , & size, compress ? 1 : 0 )
7071 case . gif:
71- gdImageGifPtr ( imagePtr , & size)
72+ gdImageGifPtr ( gdImage . ptr , & size)
7273 case . jpg( let quality) :
73- gdImageJpegPtr ( imagePtr , & size, quality)
74+ gdImageJpegPtr ( gdImage . ptr , & size, quality)
7475 case . png:
75- gdImagePngPtr ( imagePtr , & size)
76+ gdImagePngPtr ( gdImage . ptr , & size)
7677 case . tiff:
77- gdImageTiffPtr ( imagePtr , & size)
78+ gdImageTiffPtr ( gdImage . ptr , & size)
7879 case . wbmp( let index) :
79- gdImageWBMPPtr ( imagePtr , & size, index)
80+ gdImageWBMPPtr ( gdImage . ptr , & size, index)
8081 case . webp:
81- gdImageWebpPtr ( imagePtr , & size)
82+ gdImageWebpPtr ( gdImage . ptr , & size)
8283 case . avif:
83- gdImageAvifPtr ( imagePtr , & size)
84+ gdImageAvifPtr ( gdImage . ptr , & size)
8485 }
8586
8687 guard let bytes = bytes else {
8788 throw Error . invalidFormat
8889 }
8990
90- // Use custom deallocator for formats that need gdFree
91- if case . bmp = self {
92- return Data ( bytesNoCopy: bytes, count: Int ( size) ,
93- deallocator: . custom( { ptr, _ in gdFree ( ptr) } ) )
94- } else if case . jpg = self {
95- return Data ( bytesNoCopy: bytes, count: Int ( size) ,
96- deallocator: . custom( { ptr, _ in gdFree ( ptr) } ) )
97- } else if case . wbmp = self {
98- return Data ( bytesNoCopy: bytes, count: Int ( size) ,
99- deallocator: . custom( { ptr, _ in gdFree ( ptr) } ) )
100- }
91+ return Data ( bytesNoCopy: bytes, count: Int ( size) , deallocator: . custom { ptr, _ in
92+ gdFree ( ptr)
93+ } )
94+ }
95+ }
96+
97+ // MARK: - Memory-Safe GD Image Wrapper
98+ /// Wraps `gdImagePtr` and automatically destroys the image on deinit
99+ public final class GDImage {
100+ public var ptr : gdImagePtr
101+
102+ public init ( _ ptr: gdImagePtr ) {
103+ self . ptr = ptr
104+ }
101105
102- return Data ( bytes: bytes, count: Int ( size) )
106+ deinit {
107+ gdImageDestroy ( ptr)
103108 }
104109}
105110
0 commit comments