Image color bug? - ios

I'd like to generate this cool like on an UIImage using the code I've added below. But there seems to be a crazy bug in relation to the color:
So I'd like my function to draw lines to the image with a given RGB value (in my case r:76, g:255, b:0 to get this green look) as it's color:
But the code I'm using just colors the image kind of magenta instead of green:
Please have a look at my code:
func colorImage(image: UIImage) -> UIImage {
let color = RGBA32(red: 76, green: 255, blue: 0, alpha: 255)
let img: CGImage = image.cgImage!
let context = CGContext(data: nil, width: img.width, height: img.height, bitsPerComponent: 8, bytesPerRow: 4 * img.width, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
context.draw(img, in: CGRect(x: 0, y: 0, width: img.width, height: img.height))
let binaryData = context.data!.bindMemory(to: RGBA32.self, capacity: img.width * img.height)
for y in stride(from: 0, through: img.height, by: 10) {
for x in 0..<img.width {
let pixel = (y*img.width) + x
binaryData[pixel] = color
}
}
let output = context.makeImage()!
return UIImage(cgImage: output, scale: image.scale, orientation: image.imageOrientation)
}
struct RGBA32: Equatable {
private var color: UInt32
init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
let red = UInt32(red)
let green = UInt32(green)
let blue = UInt32(blue)
let alpha = UInt32(alpha)
color = (red << 24) | (green << 16) | (blue << 8) | (alpha << 0)
}
static func ==(lhs: RGBA32, rhs: RGBA32) -> Bool {
return lhs.color == rhs.color
}
}
Tbh I'm absolutely clueless why this issue appears - so any help how to fix this would be very appreciated. Thanks :)

RGBA is a confusing word. It is not clear if R is the most significant byte in the 32-bit data or the first byte in the 4-byte memory region.
But with your bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue,
R takes the first place and G, B, and then Alpha last.
In little-endian representation of UInt32, R for the least significant byte.
Try this version of RGBA:
struct RGBA32: Equatable {
private var color: (red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8)
init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
color = (red: red, green: green, blue: blue, alpha: alpha)
}
static func ==(lhs: RGBA32, rhs: RGBA32) -> Bool {
return lhs.color == rhs.color
}
}
(Memory allocations of Swift structs or tuples are not clearly defined, but the above code works as expected.)

Related

Swift self image creation and change

In Swift I have an image: (but {I think} the problem is how the 'self' is implemented)
(in my viewDidLoad)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bytesPerPixel = 4
let bitsPerComponent = 8
let bytesPerRow = bytesPerPixel * self.cols // width
let bitmapInfo = RGBA32.bitmapInfo
let minimapContext = CGContext(data: nil, width: self.cols, height: self.rows, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo)
let buffer = minimapContext!.data
self.pixelBuffer = buffer!.bindMemory(to: RGBA32.self, capacity: self.cols * self.rows) // was let
var pixelIndex = 1
Then I loop over an array of like:
self.pixelBuffer[pixelIndex] = .blue
This all works fine without the 'self'.
I want to change some of the pixels, so I added the 'self' and defined it at the top of the ViewController class
Now change the pixels like:
self.pixelBuffer[newPixelIndex] = .lightblue
But I get a variety of errors:
Cannot assign value of type 'UnsafeMutablePointer' to type 'RGBA32'
Cannot infer contextual base in reference to member 'lightblue'
Value of type 'RGBA32?' has no subscripts
Don't know if this helps but the pixelBuffer is defined ike:
var pixelBuffer: RGBA32? = nil
and here is RGBA32:
struct RGBA32: Equatable {
private var color: UInt32
init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
color = (UInt32(red) << 24) | (UInt32(green) << 16) | (UInt32(blue) << 8) | (UInt32(alpha) << 0)
}
static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
static func ==(lhs: RGBA32, rhs: RGBA32) -> Bool {
return lhs.color == rhs.color
}
static let black = RGBA32(red: 0, green: 0, blue: 0, alpha: 255)
static let red = RGBA32(red: 255, green: 0, blue: 0, alpha: 255)
static let green = RGBA32(red: 24, green: 183, blue: 3, alpha: 255)
static let darkgreen = RGBA32(red: 70, green: 105, blue: 35, alpha: 255)
static let blue = RGBA32(red: 0, green: 127, blue: 255, alpha: 255)
static let lightblue = RGBA32(red: 33, green: 255, blue: 255, alpha: 255)
static let brown = RGBA32(red: 127, green: 63, blue: 0, alpha: 255)
}
THANKS!!
The main problem is that you have declared self.pixelBuffer to be an Optional RGBA32. But that is not what pixelBuffer was before. It was a UnsafeMutablePointer<RGBA32>. Here is the declaration you need:
var pixelBuffer : UnsafeMutablePointer<RGBA32>!
All your issues will then go away, I think. Your
self.pixelBuffer[newPixelIndex] = .lightblue
then compiles for me.

How convert RGBA to UIColor and back

How can i convert my struct RGBA to UIColor and back
struct RGBA {
var r: UInt8
var g: UInt8
var b: UInt8
var a: UInt8
var toUIColor: UIColor {
let red = CGFloat(r) / CGFloat(255)
let green = CGFloat(g) / CGFloat(255)
let blue = CGFloat(b) / CGFloat(255)
let alpha = CGFloat(a) / CGFloat(255)
return UIColor(displayP3Red: CGFloat(red),
green: CGFloat(green),
blue: CGFloat(blue),
alpha: CGFloat(alpha))
}
}
extension UIColor {
var toRGBA: RGBA {
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
getRed(&red, green: &green, blue: &blue, alpha: &alpha)
return RGBA(r: UInt8(max(0, min(255, red * 255))),
g: UInt8(max(0, min(255, green * 255))),
b: UInt8(max(0, min(255, blue * 255))),
a: UInt8(max(0, min(255, alpha * 255))))
}
}
When I use t his code, im loose color accuracy
Draw pipline
select UIColor -> toRGBA ->
0.7490196078431373 0.35294117647058826 0.9490196078431372 1.0
select color on canvas -> toUIColor
draw line -> toRGBA ->
0.803921568627451 0.3215686274509804 0.9803921568627451 1.0
var canvasSize = PixelSize(width: 16, height: 16) // (Int, Int)
func createImage(by pixels: [RGBA]) -> UIImage? {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let data = UnsafeMutableRawPointer(mutating: pixels)
let bitmapContext = CGContext(data: data,
width: canvasSize.width,
height: canvasSize.height,
bitsPerComponent: 8,
bytesPerRow: 4 * canvasSize.width,
space: colorSpace,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
guard let image = bitmapContext?.makeImage() else { return nil }
return UIImage(cgImage: image)
}
Whats wrong?
Welcome!
The issue is that you use two different color spaces:
When converting from RGBA to UIColor, you assume the RGBA values are in Display P3 (UIColor(displayP3Red:...).
But the CGContext you create with the "device RGB" color space, which is an sRGB color space as far as I know.
If you'd use the init(red:, green:, blue:, alpha:) initializer of UIColor it should work.

How to apply CIFilter on Image Pixel

I know it is possible to change color of pixel using pixel buffer, like in the below code, but I just want to blur a pixel using 'CIFilter' rather than changing color. I don't want to apply a 'CIFilter' on whole image.
//data pointer – stores an array of the pixel components. For example (r0, b0, g0, a0, r1, g1, b1, a1 .... rn, gn, bn, an)
let data : UnsafeMutablePointer<UInt8> = calloc(bytesPerRow, height)!.assumingMemoryBound(to: UInt8.self)
//get the index of the pixel (4 components times the x position plus the y position times the row width)
let pixelIndex = 4 * (location.x + (location.y * width))
//set the pixel components to the color components
data[pixelIndex] = red
data[pixelIndex+1] = green
data[pixelIndex+2] = blue
data[pixelIndex+3] = alpha
Also can we use below code for applying CIFilter on Pixel?
if let pixelData = self.cgImage?.dataProvider?.data {
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
let pixelInfo: Int = ((Int(self.size.width) * Int(pos.y)) + Int(pos.x)) * 4
let red = CGFloat(data[pixelInfo]) / CGFloat(255.0)
let green = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
let blue = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
let alpha = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)
}
Check out the CIBlendWithMask filter. It allows you to create a mask of any shape (even a single pixel) and use that to blend the input with another input. If you make inputBackgroundImage be the original image, and make inputImage be the original image with your desired filter applied, the inputImageMask is an all-black image with only the single pixel you want to change in white.
I typed this up pretty quick without testing code - could be a few errors. I have done something very similar recently, so shouldn't be too off. I'd like to know whatcha get and if this doesn't work, I bet it's close.
/*
Implementations
Notes:
- I don't know what kind of `CIFilter` blur you'd like to apply, so I'm just using one from here
- https://developer.apple.com/library/archive/documentation/GraphicsImaging/Reference/CoreImageFilterReference/#//apple_ref/doc/filter/ci/CIBoxBlur
*/
//Normal Image
let inputImage:UIImage = originalImage
//Blurred Image of only the BLURRED PIXELS -- we change the rest of the pixels to clear -- thus we can use this as the backgroundImage and the maskedImage
let unblurredImage = getBackgroundImage()
let filter = CIFilter(name: "CIBoxBlur")
filter?.setValue(unblurredImage, kCIInputImageKey)
let blurredImage = filter?.outputImage
//Now we can blend the 2 images
let blendFilter = CIFilter(name: "CIBlendWithAlphaMask")
blendFilter?.setValue(CIImage(image: inputImage), kCIInputImageKey)
blendFilter?.setValue(blurredImage, "inputBackgroundImage")
blendFilter?.setValue(blurredImage, "inputMaskImage")
let finalCIImage = blendFilter?.outputImage
let finalImage = UIImage(ciImage: finalCIImage)
/*
Functions used in the process
*/
//Create an image of only the pixels we want to blur
func getBackgroundImage(ciimage: CIImage) -> UIImage {
let inputCGImage = ciimage.convertCIImageToCGImage()!
let colorSpace = CGColorSpaceCreateDeviceRGB()
let width = inputCGImage.width
let height = inputCGImage.height
let bytesPerPixel = 4
let bitsPerComponent = 8
let bytesPerRow = bytesPerPixel * width
let bitmapInfo = RGBA32.bitmapInfo
guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
print("Couldn't create CGContext")
return nil
}
context.draw(inputCGImage, in: CGRect(x: 0, y: 0, width: width, height: height))
let pixelBuffer = buffer.bindMemory(to: RGBA32.self, capacity: width * height)
for row in 0 ..< Int(height) {
for column in 0 ..< Int(width) {
let offset = row * width + column
/*
You need to define aPixelIWantBlurred however you desire
Also, we don't edit the pixels we want to blur - we edit the other pixels to a transparent value. This allows us to use this as the background image and the masked image
*/
if pixelBuffer[offset] != aPixelIWantBlurred {
pixelBuffer[offset] = RGBA32.init(red: 0, green: 0, blue: 0, alpha: 0)
}
}
}
let outputCGImage = context.makeImage()!
let outputImage = UIImage(cgImage: outputCGImage, scale: image.scale, orientation: image.imageOrientation)
return outputImage
}
extension CIImage {
func convertCIImageToCGImage() -> CGImage! {
let context = CIContext(options: nil)
return context.createCGImage(self, from: self.extent)
}
}
struct RGBA32: Equatable {
private var color: UInt32
var redComponent: UInt8 {
return UInt8((color >> 24) & 255)
}
var greenComponent: UInt8 {
return UInt8((color >> 16) & 255)
}
var blueComponent: UInt8 {
return UInt8((color >> 8) & 255)
}
var alphaComponent: UInt8 {
return UInt8((color >> 0) & 255)
}
init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
let red = UInt32(red)
let green = UInt32(green)
let blue = UInt32(blue)
let alpha = UInt32(alpha)
color = (red << 24) | (green << 16) | (blue << 8) | (alpha << 0)
}
static let red = RGBA32(red: 255, green: 0, blue: 0, alpha: 255)
static let green = RGBA32(red: 0, green: 255, blue: 0, alpha: 255)
static let blue = RGBA32(red: 0, green: 0, blue: 255, alpha: 255)
static let white = RGBA32(red: 255, green: 255, blue: 255, alpha: 255)
static let black = RGBA32(red: 0, green: 0, blue: 0, alpha: 255)
static let magenta = RGBA32(red: 255, green: 0, blue: 255, alpha: 255)
static let yellow = RGBA32(red: 255, green: 255, blue: 0, alpha: 255)
static let cyan = RGBA32(red: 0, green: 255, blue: 255, alpha: 255)
static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
static func ==(lhs: RGBA32, rhs: RGBA32) -> Bool {
return lhs.color == rhs.color
}
}

How to draw a gradient color wheel using CAGradientLayer?

I got some reference from these links-
What is the Algorithm behind a color wheel?
Math behind the Colour Wheel
Basic color schemes
How to fill a bezier path with gradient color
I have gone through the concept of "HSV colour space". But I want to draw a color wheel using RGB with the help of CAGradientLayer.
Here is the code snippet of making a color wheel by using simple RGB color array and UIBezierPath -
func drawColorWheel()
{
context?.saveGState()
range = CGFloat(100.00 / CGFloat(colorArray.count))
for k in 0 ..< colorArray.count
{
drawSlice(startPercent: CGFloat(k) * range, endPercent: CGFloat(CGFloat(k + 1) * range), color: colorArray.object(at: k) as! UIColor)
}
context?.restoreGState()
}
func drawSlice(startPercent: CGFloat, endPercent: CGFloat, color: UIColor)
{
let startAngle = getAngleAt(percentage: startPercent)
let endAngle = getAngleAt(percentage: endPercent)
let path = getArcPath(startAngle: startAngle, endAngle: endAngle)
color.setFill()
path.fill()
}
Where getAngleAt() and getArcPath() are the private functions to draw the path with an angle.
Here is the final output of my code -
Now, my question is the how to give these colors a gradient effect so that each colors mix up with gradient color layer?
One approach is to build an image and manipulate the pixel buffer manually:
Create CGContext of certain size and certain type;
Access its data buffer via data property;
Rebind that to something that makes it easy to manipulate that buffer (I use a Pixel, a struct for the 32-bit representation of a pixel);
Loop through the pixels, one by one, converting that to an angle and radius for the circle within this image; and
Create a pixel of the appropriate color if it's inside the circle; make it a zero-alpha pixel if not.
So in Swift 3:
func buildHueCircle(in rect: CGRect, radius: CGFloat, scale: CGFloat = UIScreen.main.scale) -> UIImage? {
let width = Int(rect.size.width * scale)
let height = Int(rect.size.height * scale)
let center = CGPoint(x: width / 2, y: height / 2)
let space = CGColorSpaceCreateDeviceRGB()
let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: width * 4, space: space, bitmapInfo: Pixel.bitmapInfo)!
let buffer = context.data!
let pixels = buffer.bindMemory(to: Pixel.self, capacity: width * height)
var pixel: Pixel
for y in 0 ..< height {
for x in 0 ..< width {
let angle = fmod(atan2(CGFloat(x) - center.x, CGFloat(y) - center.y) + 2 * .pi, 2 * .pi)
let distance = hypot(CGFloat(x) - center.x, CGFloat(y) - center.y)
let value = UIColor(hue: angle / 2 / .pi, saturation: 1, brightness: 1, alpha: 1)
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
value.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
if distance <= (radius * scale) {
pixel = Pixel(red: UInt8(red * 255),
green: UInt8(green * 255),
blue: UInt8(blue * 255),
alpha: UInt8(alpha * 255))
} else {
pixel = Pixel(red: 255, green: 255, blue: 255, alpha: 0)
}
pixels[y * width + x] = pixel
}
}
let cgImage = context.makeImage()!
return UIImage(cgImage: cgImage, scale: scale, orientation: .up)
}
Where
struct Pixel: Equatable {
private var rgba: UInt32
var red: UInt8 {
return UInt8((rgba >> 24) & 255)
}
var green: UInt8 {
return UInt8((rgba >> 16) & 255)
}
var blue: UInt8 {
return UInt8((rgba >> 8) & 255)
}
var alpha: UInt8 {
return UInt8((rgba >> 0) & 255)
}
init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
rgba = (UInt32(red) << 24) | (UInt32(green) << 16) | (UInt32(blue) << 8) | (UInt32(alpha) << 0)
}
static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
static func ==(lhs: Pixel, rhs: Pixel) -> Bool {
return lhs.rgba == rhs.rgba
}
}
That yields:
Or you can tweak the brightness or saturation based upon the radius:

change an UI Image into a String array

I have an UIImage made of only 4 types of pixel.
static let black = RGBA32(red: 0, green: 0, blue: 0, alpha: 255)
static let red = RGBA32(red: 255, green: 0, blue: 0, alpha: 255)
static let green = RGBA32(red: 0, green: 255, blue: 0, alpha: 255)
static let blue = RGBA32(red: 0, green: 0, blue: 255, alpha: 255)
( reference to the code used to create the Image https://stackoverflow.com/a/40207142/7010252 )
I saved the image in the phone's photo library then I open the image in my app again. I know that the number of pixel is the same.
So how can I get the RGBA data of all the pixels?
The first thing done here is to loop through your image and create a map of all the points in it.
func getPixelColor(image: UIImage) {
let width = image.size.width
let height = image.size.height
for x in 0..<Int(width) {
for y in 0..<Int(height) {
let color = image.pixelColor(CGPoint(x: CGFloat(x), y: CGFloat(y)))
print(color.RGBA)
}
}
}
Once it has the color it wants, it then calls the RGBA function in the UIColor extension to provide the string you want.
Next, we use an extension of UIImage to then take the points provided by the above function and query the image data for its UIColor data.
extension UIImage {
func pixelColor(_ point: CGPoint) -> UIColor {
let pixelData = cgImage?.dataProvider?.data
let pointerData: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
let pixelInfo = Int(((self.size.width * point.y) + point.x)) * 4
let maxValue: CGFloat = 255.0
let compare: CGFloat = 0.99
let r: CGFloat = (CGFloat(pointerData[pixelInfo]) / maxValue) > compare ? 1.0 : 0.0
let g: CGFloat = (CGFloat(pointerData[pixelInfo + 1]) / maxValue) > compare ? 1.0 : 0.0
let b: CGFloat = (CGFloat(pointerData[pixelInfo + 2]) / maxValue) > compare ? 1.0 : 0.0
let a = CGFloat(pointerData[pixelInfo + 3]) / maxValue
return UIColor(red: r, green: g, blue: b, alpha: a)
}
}
This extension uses the raw data to determine the RGB values of each pixel. It then performs a crude check to play safe with CGFloats to see if the value is 255.0 for that particular color. If it is, it will return a value of 1.0, otherwise it returns 0.0.
Next, there is an extension for UIColor that will provide the formatted string you are looking for.
extension UIColor {
var RGBA: String {
guard let components = cgColor.components, components.count == 4 else {
return ""
}
return "\(components[0])-\(components[1])-\(components[2])-\(components[3])"
}
}
This should provide the 1-0-0-1 type of values you seek. You can also modify this to include any additional information you need.

Resources