@@ -45,11 +45,11 @@ public final class PNGEncoder {
4545 */
4646 public func encode( _ data: Data , width: Int , height: Int , sacrificePerformanceToShrinkData: Bool = false ) throws ( GateEngineError) -> Data {
4747#if canImport(LibSPNG)
48- // if sacrificePerformanceToShrinkData {
49- // try LibSPNG.encodeSmallest(data: data, width: width, height: height)
50- // }else{
48+ if sacrificePerformanceToShrinkData {
49+ try LibSPNG . encodeSmallest ( data: data, width: width, height: height)
50+ } else {
5151 try LibSPNG . encodeRGBA ( data: data, width: width, height: height, optimizeAlpha: false )
52- // }
52+ }
5353#else
5454 fatalError ( " PNGEncoder is not supported on this platform. " )
5555#endif
@@ -71,19 +71,44 @@ enum LibSPNG {
7171 // TODO: Give users more customizability to allow for more predictability for runtime use.
7272 @inlinable
7373 static func encodeSmallest( data: Data , width: Int , height: Int ) throws ( GateEngineError) -> Data {
74- let indexed = try encodeIndexed ( data: data, width: width, height: height)
7574 let rgba : Data = try encodeRGBA ( data: data, width: width, height: height, optimizeAlpha: true )
76-
77- if rgba. count <= indexed. count {
78- return rgba
75+ if let indexed = try encodeIndexed ( data: data, width: width, height: height) {
76+ if indexed. count < rgba. count {
77+ return indexed
78+ }
7979 }
80- return indexed
80+ return rgba
8181 }
8282
8383 /// Makes a PNG with full color data. This creates efficient PNG data representing photos or images with many unique colors.
8484 @inlinable
8585 static func encodeRGBA( data: Data , width: Int , height: Int , optimizeAlpha: Bool ) throws ( GateEngineError) -> Data {
8686 do {
87+ var data : Data = data
88+
89+ let colorType : UInt8
90+ if optimizeAlpha {
91+ var hasAlpha : Bool = false
92+ for index in stride ( from: 3 , to: data. count, by: 4 ) {
93+ if data [ index] < . max {
94+ // If any alpha value is less then 100% we need to store the alpha
95+ hasAlpha = true
96+ break
97+ }
98+ }
99+ if hasAlpha {
100+ colorType = UInt8 ( SPNG_COLOR_TYPE_TRUECOLOR_ALPHA . rawValue)
101+ } else {
102+ colorType = UInt8 ( SPNG_COLOR_TYPE_TRUECOLOR . rawValue)
103+ // Remove the alpha values since they are all 100%
104+ for index in stride ( from: 3 , to: data. count, by: 4 ) . reversed ( ) {
105+ data. remove ( at: index)
106+ }
107+ }
108+ } else {
109+ colorType = UInt8 ( SPNG_COLOR_TYPE_TRUECOLOR_ALPHA . rawValue)
110+ }
111+
87112 return try data. withUnsafeBytes ( { ( bytes: UnsafeRawBufferPointer ) throws -> Data in
88113 /* Create a context */
89114 let ctx : OpaquePointer ? = spng_ctx_new ( Int32 ( SPNG_CTX_ENCODER . rawValue) )
@@ -94,25 +119,6 @@ enum LibSPNG {
94119
95120 spng_set_option ( ctx, SPNG_ENCODE_TO_BUFFER, 1 )
96121
97- let colorType : UInt8
98- if optimizeAlpha {
99- var hasAlpha : Bool = false
100- for index in stride ( from: 3 , to: data. count, by: 4 ) {
101- if data [ index] < . max {
102- // If any alpha value is less then 100% we need to store the alpha
103- hasAlpha = true
104- break
105- }
106- }
107- if hasAlpha {
108- colorType = UInt8 ( SPNG_COLOR_TYPE_TRUECOLOR_ALPHA . rawValue)
109- } else {
110- colorType = UInt8 ( SPNG_COLOR_TYPE_TRUECOLOR . rawValue)
111- }
112- } else {
113- colorType = UInt8 ( SPNG_COLOR_TYPE_TRUECOLOR_ALPHA . rawValue)
114- }
115-
116122 var ihdr = spng_ihdr (
117123 width: UInt32 ( width) ,
118124 height: UInt32 ( height) ,
@@ -145,9 +151,42 @@ enum LibSPNG {
145151
146152 /// Makes a PNG with an color table backend. This creates efficient PNG data representing pixel art or other images with few unique colors.
147153 @inlinable
148- static func encodeIndexed( data: Data , width: Int , height: Int ) throws ( GateEngineError) -> Data {
154+ static func encodeIndexed( data: Data , width: Int , height: Int ) throws ( GateEngineError) -> Data ? {
149155 do {
150- return try data. withUnsafeBytes ( { ( bytes: UnsafeRawBufferPointer ) throws -> Data in
156+ struct Color : Equatable , Hashable {
157+ let red : UInt8
158+ let green : UInt8
159+ let blue : UInt8
160+ let alpha : UInt8
161+ }
162+ var colors : [ ( Color ) ] = [ ]
163+ colors. reserveCapacity ( width * height)
164+ for index in stride ( from: 0 , to: data. count, by: 4 ) {
165+ colors. append (
166+ Color (
167+ red: data [ index + 0 ] ,
168+ green: data [ index + 1 ] ,
169+ blue: data [ index + 2 ] ,
170+ alpha: data [ index + 3 ]
171+ )
172+ )
173+ }
174+
175+ let colorTable = Array ( Set ( colors) )
176+ guard colorTable. count <= 256 else { return nil }
177+
178+ let indexedData = colors. map ( { UInt8 ( colorTable. firstIndex ( of: $0) !) } )
179+
180+ var plte : spng_plte = . init( )
181+ plte. n_entries = UInt32 ( colorTable. count)
182+ withUnsafeMutableBytes ( of: & plte) { plte in
183+ let entires = colorTable. map ( { spng_plte_entry ( red: $0. red, green: $0. green, blue: $0. blue, alpha: $0. alpha) } )
184+ entires. withUnsafeBytes { entriesBytes in
185+ plte. baseAddress!. advanced ( by: MemoryLayout< UInt32> . size) . copyMemory ( from: entriesBytes. baseAddress!, byteCount: entriesBytes. count)
186+ }
187+ }
188+
189+ return try indexedData. withUnsafeBytes ( { ( bytes: UnsafeRawBufferPointer ) throws -> Data in
151190 /* Create a context */
152191 let ctx : OpaquePointer ? = spng_ctx_new ( Int32 ( SPNG_CTX_ENCODER . rawValue) )
153192 defer {
@@ -156,7 +195,7 @@ enum LibSPNG {
156195 }
157196
158197 spng_set_option ( ctx, SPNG_ENCODE_TO_BUFFER, 1 )
159-
198+
160199 var ihdr = spng_ihdr (
161200 width: UInt32 ( width) ,
162201 height: UInt32 ( height) ,
@@ -166,9 +205,17 @@ enum LibSPNG {
166205 filter_method: UInt8 ( SPNG_FILTER_NONE . rawValue) ,
167206 interlace_method: UInt8 ( SPNG_INTERLACE_NONE . rawValue)
168207 )
169- spng_set_ihdr ( ctx, & ihdr)
208+ if spng_set_ihdr ( ctx, & ihdr) != SPNG_OK . rawValue {
209+ throw GateEngineError . failedToEncode ( " spng_set_ihdr " )
210+ }
170211
171- spng_encode_image ( ctx, bytes. baseAddress, data. count, Int32 ( SPNG_FMT_PNG . rawValue) , Int32 ( SPNG_ENCODE_FINALIZE . rawValue) )
212+ if spng_set_plte ( ctx, & plte) != SPNG_OK . rawValue {
213+ throw GateEngineError . failedToEncode ( " spng_set_plte " )
214+ }
215+
216+ if spng_encode_image ( ctx, bytes. baseAddress!, colors. count, Int32 ( SPNG_FMT_PNG . rawValue) , Int32 ( SPNG_ENCODE_FINALIZE . rawValue) ) != SPNG_OK . rawValue {
217+ throw GateEngineError . failedToEncode ( " spng_encode_image " )
218+ }
172219
173220 var length : Int = 0
174221 var error : Int32 = 0
@@ -178,7 +225,7 @@ enum LibSPNG {
178225 return data
179226 }
180227
181- throw GateEngineError . failedToEncode ( String ( cString: spng_strerror ( error) ) )
228+ throw GateEngineError . failedToEncode ( " spng_get_png_buffer \( String ( cString: spng_strerror ( error) ) ) " )
182229 } )
183230 } catch let error as GateEngineError {
184231 throw error // Typed throws not supported by closures as of Swift 6.2
0 commit comments