Created
December 15, 2022 09:59
-
-
Save MojtabaHs/745e73afb2eb2e4322e4af55294cffcb to your computer and use it in GitHub Desktop.
UIImage Color Replace using pixel buffer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MARK: - Color Replacement | |
extension UIImage { | |
func image(byReplacing sourceColor: UIColor, with destinationColor: UIColor, minTolerance: Float, maxTolerance: Float) -> UIImage? { | |
guard let inputCGImage = cgImage else { | |
print("unable to get cgImage") | |
return self | |
} | |
let colorSpace = CGColorSpaceCreateDeviceRGB() | |
let width = inputCGImage.width | |
let height = inputCGImage.height | |
let bytesPerPixel = 4 | |
let bitsPerComponent = 8 | |
let bytesPerRow = bytesPerPixel * width | |
let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue | |
guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else { | |
print("unable to create context") | |
return self | |
} | |
context.draw(inputCGImage, in: CGRect(x: 0, y: 0, width: width, height: height)) | |
guard let buffer = context.data else { | |
print("unable to get context data") | |
return self | |
} | |
let pixelBuffer = buffer.bindMemory(to: UInt8.self, capacity: bytesPerRow*height) | |
// components of the source color | |
guard let sourceComponents = sourceColor.cgColor.components else { return self } | |
let source255Components: [Float] = sourceComponents.map({ | |
Float($0 * CGFloat(255)) | |
}) | |
// components of the destination color | |
guard let destinationComponents = destinationColor.cgColor.components else { return self } | |
let destination255Components: [Float] = destinationComponents.map({ | |
Float($0 * CGFloat(255)) | |
}) | |
// loop through each pixel's components | |
for byte in stride(from: 0, to: bytesPerRow*height, by: 4) { | |
var rgb: [Float] = [ Float(pixelBuffer[byte]), Float(pixelBuffer[byte+1]), Float(pixelBuffer[byte+2]) ] | |
// delta components | |
var ratio: Float = 0.0 | |
for subsequence in source255Components.enumerated() { | |
ratio += abs(rgb[subsequence.offset]-source255Components[subsequence.offset]) | |
} | |
// ratio of 'how far away' each component is from the source color | |
ratio /= (255.0*3.0) | |
if ratio>maxTolerance { ratio = 1.0 } // if ratio is too far away, set it to max. | |
if ratio<minTolerance { ratio = 0.0 } // if ratio isn't far enough away, set it to min. | |
// blend color components | |
for subsequence in destination255Components.enumerated() { | |
pixelBuffer[byte+subsequence.offset] = UInt8(ratio*rgb[subsequence.offset]+(1.0-ratio)*destination255Components[subsequence.offset]) | |
} | |
} | |
let outputCGImage = context.makeImage()! | |
let outputImage = UIImage(cgImage: outputCGImage, scale: self.scale, orientation: self.imageOrientation) | |
return outputImage | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment