I created this extension for "bucket fill" (flood fill) of touch point:
extension UIImageView {
func bucketFill(startPoint: CGPoint, newColor: UIColor) {
var newRed, newGreen, newBlue, newAlpha: CUnsignedChar
let pixelsWide = CGImageGetWidth(self.image!.CGImage)
let pixelsHigh = CGImageGetHeight(self.image!.CGImage)
let rect = CGRect(x:0, y:0, width:Int(pixelsWide), height:Int(pixelsHigh))
let bitmapBytesPerRow = Int(pixelsWide) * 4
var context = self.image!.createARGBBitmapContext()
//Clear the context
CGContextClearRect(context, rect)
// Draw the image to the bitmap context. Once we draw, the memory
// allocated for the context for rendering will then contain the
// raw image data in the specified color space.
CGContextDrawImage(context, rect, self.image!.CGImage)
var data = CGBitmapContextGetData(context)
var dataType = UnsafeMutablePointer<UInt8>(data)
let newColorRef = CGColorGetComponents(newColor.CGColor)
if(CGColorGetNumberOfComponents(newColor.CGColor) == 2) {
newRed = CUnsignedChar(newColorRef[0] * 255) // CUnsignedChar
newGreen = CUnsignedChar(newColorRef[0] * 255)
newBlue = CUnsignedChar(newColorRef[0] * 255)
newAlpha = CUnsignedChar(newColorRef[1])
} else {
newRed = CUnsignedChar(newColorRef[0] * 255)
newGreen = CUnsignedChar(newColorRef[1] * 255)
newBlue = CUnsignedChar(newColorRef[2] * 255)
newAlpha = CUnsignedChar(newColorRef[3])
}
let newColorStr = ColorRGB(red: newRed, green: newGreen, blue: newBlue)
var stack = Stack()
let offset = 4*((Int(pixelsWide) * Int(startPoint.y)) + Int(startPoint.x))
//let alpha = dataType[offset]
let startRed: UInt8 = dataType[offset+1]
let startGreen: UInt8 = dataType[offset+2]
let startBlue: UInt8 = dataType[offset+3]
stack.push(startPoint)
while(!stack.isEmpty()) {
let point: CGPoint = stack.pop() as! CGPoint
let offset = 4*((Int(pixelsWide) * Int(point.y)) + Int(point.x))
let alpha = dataType[offset]
let red: UInt8 = dataType[offset+1]
let green: UInt8 = dataType[offset+2]
let blue: UInt8 = dataType[offset+3]
if (red == newRed && green == newGreen && blue == newBlue) {
continue
}
if (red.absoluteDifference(startRed) < 4 && green.absoluteDifference(startGreen) < 4 && blue.absoluteDifference(startBlue) < 4) {
dataType[offset] = 255
dataType[offset + 1] = newRed
dataType[offset + 2] = newGreen
dataType[offset + 3] = newBlue
if (point.x > 0) {
stack.push(CGPoint(x: point.x - 1, y: point.y))
}
if (point.x < CGFloat(pixelsWide)) {
stack.push(CGPoint(x: point.x + 1, y: point.y))
}
if (point.y > 0) {
stack.push(CGPoint(x: point.x, y: point.y - 1))
}
if (point.y < CGFloat(pixelsHigh)) {
stack.push(CGPoint(x: point.x, y: point.y + 1))
}
} else {
}
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue)
let finalContext = CGBitmapContextCreate(data, pixelsWide, pixelsHigh, CLong(8), CLong(bitmapBytesPerRow), colorSpace, bitmapInfo)
let imageRef = CGBitmapContextCreateImage(finalContext)
self.image = UIImage(CGImage: imageRef, scale: self.image!.scale,orientation: self.image!.imageOrientation)
}
}
Now I would like to improve performance. How can I make this algorithm work faster? UInt8.absoluteDifference extension is my attempt to include almost same colors to flood fill and it's working but this could be really improve and I know it but I don't know how.
extension UInt8 {
func absoluteDifference(subtrahend: UInt8) -> UInt8 {
if (self > subtrahend) {
return self - subtrahend;
} else {
return subtrahend - self;
}
}
}
My Stack class:
class Stack {
var count: Int = 0
var head: Node = Node()
init() {
}
func isEmpty() -> Bool {
return self.count == 0
}
func push(value: Any) {
if isEmpty() {
self.head = Node()
}
var node = Node(value: value)
node.next = self.head
self.head = node
self.count++
}
func pop() -> Any? {
if isEmpty() {
return nil
}
var node = self.head
self.head = node.next!
self.count--
return node.value
}
}
Thanks for help
Related
Guys I am new in swift and programming, I want to generate a Image to be shown for a ColorPicker I'm using SwiftColorPicker for that and have the following code for image creating:
private func createImageFromData(_ width:Int, height:Int) {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
provider = CGDataProvider(data: mutableData)
imageSource = CGImageSourceCreateWithDataProvider(provider, nil)
let cgImage = CGImage.init(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: width * 4, space: colorSpace, bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: true, intent: .defaultIntent)
if let cgimg = cgImage {
image = UIImage(cgImage: cgimg)
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAsset(from: self.image!)
}, completionHandler: { success, error in
if success {
print("succes")
}
else if let error = error {
print("error")
debugPrint(error as Any)
}
else {
print("woooot?")
}
})
} else { print("Where the hell is the image?") }
}
func changeSize(_ width:Int, height:Int) {
self.width = width
self.height = height
let size:Int = width * height * 4
CFDataSetLength(mutableData, size)
createImageFromData(width, height: height)
}
init(width:Int, height:Int) {
self.width = width
self.height = height
let size:Int = width * height * 4
mutableData = CFDataCreateMutable(kCFAllocatorDefault, size)
createImageFromData(width, height: height)
}
public func writeColorData(_ h:CGFloat, a:CGFloat) {
let d = CFDataGetMutableBytePtr(self.mutableData)
if width == 0 || height == 0 {
return
}
var i:Int = 0
let h360:CGFloat = ((h == 1 ? 0 : h) * 360) / 60.0
let sector:Int = Int(floor(h360))
let f:CGFloat = h360 - CGFloat(sector)
let f1:CGFloat = 1.0 - f
var p:CGFloat = 0.0
var q:CGFloat = 0.0
var t:CGFloat = 0.0
let sd:CGFloat = 1.0 / CGFloat(width)
let vd:CGFloat = 1 / CGFloat(height)
var double_s:CGFloat = 0
var pf:CGFloat = 0
let v_range = 0..<height
let s_range = 0..<width
for v in v_range {
pf = 255 * CGFloat(v) * vd
for s in s_range {
i = (v * width + s) * 4
d?[i] = UInt8(255)
if s == 0 {
q = pf
d?[i+1] = UInt8(q)
d?[i+2] = UInt8(q)
d?[i+3] = UInt8(q)
continue
}
double_s = CGFloat(s) * sd
p = pf * (1.0 - double_s)
q = pf * (1.0 - double_s * f)
t = pf * ( 1.0 - double_s * f1)
switch(sector) {
case 0:
d?[i+1] = UInt8(pf)
d?[i+2] = UInt8(t)
d?[i+3] = UInt8(p)
case 1:
d?[i+1] = UInt8(q)
d?[i+2] = UInt8(pf)
d?[i+3] = UInt8(p)
case 2:
d?[i+1] = UInt8(p)
d?[i+2] = UInt8(pf)
d?[i+3] = UInt8(t)
case 3:
d?[i+1] = UInt8(p)
d?[i+2] = UInt8(q)
d?[i+3] = UInt8(pf)
case 4:
d?[i+1] = UInt8(t)
d?[i+2] = UInt8(p)
d?[i+3] = UInt8(pf)
default:
d?[i+1] = UInt8(pf)
d?[i+2] = UInt8(p)
d?[i+3] = UInt8(q)
}
}
}
}
and:
// Write colors to data array
if self.data1Shown { self.pickerImage2!.writeColorData(self.h, a:self.a) }
else { self.pickerImage1!.writeColorData(self.h, a:self.a)}
with:
public var a:CGFloat = 1 {
didSet {
if a < 0 || a > 1 {
a = max(0, min(1, a))
}
}
}
public var h:CGFloat = 0 { // // [0,1]
didSet {
if h > 1 || h < 0 {
h = max(0, min(1, h))
}
renderBitmap()
setNeedsDisplay()
}
}
It fails when i try to save it to photo albums (i've done that cause I was not able to set the image so I tried to save it to understand if it's ok or it's a error), and voila! An graceful error appears:
Error Domain=NSCocoaErrorDomain Code=-1 "(null)"
So I wanna know maybe someone knows what to do, how I can fix this, here is the link on gitHub for the Source (maybe it will help):
https://github.com/MrMatthias/SwiftColorPicker
Thanks!
I've made a helpful swift extension to create a UIImage of a solid color.
Feel free to use it:
extension UIImage {
static func image(of color: UIColor, size: CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
color.set()
UIRectFill(CGRect(origin: CGPoint.zero, size: size))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
Then you can use it by:
let redImage = UIImage.image(of: .red, size: CGSize(width: 200, height: 200))
Hope that helps!
I need to read pixel values of an image and iterate to print in swift output, I have written this so far and used a RGBAImage class to read out pixels. I'm getting lost from CGContextRef to Iteration. I tried to write from CGImage, getting pixel data from objective C language to swift since I wanted to work in swift.
func createRGBAPixel(inImage: CGImageRef) -> CGContextRef {
//Image width, height
let pixelWidth = CGImageGetWidth(inImage)
let pixelHeight = CGImageGetHeight(inImage)
//Declaring number of bytes
let bytesPerRow = Int(pixelWidth) * 4
let byteCount = bytesPerRow * Int(pixelHeight)
//RGB color space
let colorSpace = CGColorSpaceCreateDeviceRGB()
//Allocating image data
let mapData = malloc(byteCount)
let mapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue)
//Create bitmap context
let context = CGBitmapContextCreate(mapData, pixelWidth, pixelHeight, Int(8), Int(bytesPerRow), colorSpace, mapInfo.rawValue)
let pixelImage = CGBitmapContextCreate(pixels, pixelWidth, pixelHeight, bitsPerComponent, bytesPerRow, colorSpace, mapInfo)
let CGContextRef = pixelImage
let CGContextDrawImage(context, CGRectMake(0, 0, pixelWidth, pixelHeight), inImage)
//Iterating and logging
print("Logging pixel counts")
let pixels = calloc(pixelHeight * pixelWidth, sizeof(UInt32))
let myImage = CGImageRef: inImage
let myRGBA = RGBAImage(image: myImage)! //RGBAImage class to read pixels.
var number = 0
var currentPixel:Int32 = 0
currentPixel = pixels * UInt32
for number in 0..<pixelHeight {
for number in 0..<pixelWidth {
var color = color * currentPixel
print((pixel.red + pixel.green + pixel.blue) / 3.0)
currentPixel++
}
}
return context!
}
I created small class for this:
class ImagePixelReader {
enum Component:Int {
case r = 0
case g = 1
case b = 2
case alpha = 3
}
struct Color {
var r:UInt8
var g:UInt8
var b:UInt8
var a:UInt8
var uiColor:UIColor {
return UIColor(red:CGFloat(r)/255.0,green:CGFloat(g)/255.0,blue:CGFloat(b)/255.0,alpha:CGFloat(alpha)/255.0)
}
}
let image:UIImage
private var data:CFData
private let pointer:UnsafePointer<UInt8>
private let scale:Int
init?(image:UIImage){
self.image = image
guard let cfdata = self.image.cgImage?.dataProvider?.data,
let pointer = CFDataGetBytePtr(cfdata) else {
return nil
}
self.scale = Int(image.scale)
self.data = cfdata
self.pointer = pointer
}
func componentAt(_ component:Component,x:Int,y:Int)->UInt8{
assert(CGFloat(x) < image.size.width)
assert(CGFloat(y) < image.size.height)
let pixelPosition = (Int(image.size.width) * y * scale + x) * 4 * scale
return pointer[pixelPosition + component.rawValue]
}
func colorAt(x:Int,y:Int)->Color{
assert(CGFloat(x) < image.size.width)
assert(CGFloat(y) < image.size.height)
let pixelPosition = (Int(image.size.width) * y * scale + x) * 4 * scale
return Color(r: pointer[pixelPosition + Component.r.rawValue],
g: pointer[pixelPosition + Component.g.rawValue],
b: pointer[pixelPosition + Component.b.rawValue],
a: pointer[pixelPosition + Component.alpha.rawValue])
}
}
How to use:
if let reader = ImagePixelReader(image: yourImage) {
//get alpha or color
let alpha = reader.componentAt(.alpha, x: 10, y:10)
let color = reader.colorAt(x:10, y: 10).uiColor
//getting all the pixels you need
var values = ""
//iterate over all pixels
for x in 0 ..< Int(image.size.width){
for y in 0 ..< Int(image.size.height){
let color = reader.colorAt(x: x, y: y)
values += "[\(x):\(y):\(color)] "
}
//add new line for every new row
values += "\n"
}
print(values)
}
My image for the first time looks like:
After pressing "Apply the Filter" button it becomes:
and I implement this ColorCube:
How you see, it implements color-cube, but very very insensibly.
This is my code:
func colorCubeFilterFromLUT(imageName : NSString) -> CIFilter? {
let kDimension : UInt = 64
let lutImage = UIImage(named: imageName as String)!.CGImage
let lutWidth: UInt = UInt(CGImageGetWidth(lutImage!))
let lutHeight: UInt = UInt(CGImageGetHeight(lutImage!))
let rowCount = lutHeight / kDimension
let columnCount = lutWidth / kDimension
if ((lutWidth % kDimension != 0) || (lutHeight % kDimension != 0) || (rowCount * columnCount != kDimension)) {
NSLog("Invalid colorLUT %#", imageName);
return nil
}
let bitmap = self.createRGBABitmapFromImage(lutImage!)
let size = Int(kDimension) * Int(kDimension) * Int(kDimension) * sizeof(Float) * 4
let data = UnsafeMutablePointer<Float>(malloc(Int(size)))
var bitmapOffset : Int = 0
var z : UInt = 0
for (var row: UInt = 0; row < rowCount; row++)
{
for (var y: UInt = 0; y < kDimension; y++)
{
var tmp = z
for (var col: UInt = 0; col < columnCount; col++)
{
for (var x: UInt = 0; x < kDimension; x++) {
let alpha = Float(bitmap[Int(bitmapOffset)]) / 255.0
let red = Float(bitmap[Int(bitmapOffset+1)]) / alpha
let green = Float(bitmap[Int(bitmapOffset+2)]) / alpha
let blue = Float(bitmap[Int(bitmapOffset+3)]) / alpha
var dataOffset = Int(z * kDimension * kDimension + y * kDimension + x) * 4
data[dataOffset] = red
data[dataOffset + 1] = green
data[dataOffset + 2] = blue
data[dataOffset + 3] = alpha
bitmapOffset += 4
}
z++
}
z = tmp
}
z += columnCount
}
let colorCubeData = NSData(bytesNoCopy: data, length: size, freeWhenDone: true)
// create CIColorCube Filter
var filter = CIFilter(name: "CIColorCube")
filter!.setValue(colorCubeData, forKey: "inputCubeData")
filter!.setValue(kDimension, forKey: "inputCubeDimension")
return filter
}
func createRGBABitmapFromImage(inImage: CGImage) -> UnsafeMutablePointer<Float> {
let pixelsWide = CGImageGetWidth(inImage)
let pixelsHigh = CGImageGetHeight(inImage)
let bitmapBytesPerRow = Int(pixelsWide) * 4
let bitmapByteCount = bitmapBytesPerRow * Int(pixelsHigh)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapData = malloc(Int(CUnsignedLong(bitmapByteCount))) // bitmap
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue | CGBitmapInfo.ByteOrder32Big.rawValue)
let context = CGBitmapContextCreate(bitmapData, 512, 512, 8, Int(bitmapBytesPerRow), colorSpace, bitmapInfo.rawValue)
let rect = CGRect(x:0, y:0, width:1000, height:1000)
CGContextDrawImage(context, rect, inImage)
var convertedBitmap = malloc(Int(bitmapByteCount * sizeof(Float)))
print("BBB \(convertedBitmap)")
vDSP_vfltu8(UnsafePointer<UInt8>(bitmapData), 1, UnsafeMutablePointer<Float>(convertedBitmap), 1, vDSP_Length(bitmapByteCount))
free(bitmapData)
return UnsafeMutablePointer<Float>(convertedBitmap)
}
I'm working over it from the morning and nothing. I cannot find why this filter does not implements to my image.
I have been trying to write a code to apply a filter for an image in swift. I am trying out the following code in the playground. It keeps failing for some reason, and I believe it is because I messed up somewhere in the variable referencing for functions. I have no idea if I am supposed to use inout in the initializer to make the values mutable or something like that. Please point out where I am making a mistake. THe code is giving no error whatsoever, but It is not resulting in an Image ouput that I desire.
//: Playground - noun: a place where people can play
import UIKit
class imageProcessor{
var avgRed = 0
var avgBlue = 0
var avgGreen = 0
var avgPixelValue = 0
var rgbaImage:RGBAImage? = nil
func averagePixelValue ( input inputImage: RGBAImage ) -> ( Int , Int , Int , Int ){
var totalRed = 0
var totalBlue = 0
var totalGreen = 0
for y in 0..<inputImage.height{
for x in 0..<inputImage.width{
let index = y * inputImage.height + x
let pixel = inputImage.pixels[index]
totalBlue += Int(pixel.blue)
totalRed += Int(pixel.red)
totalGreen += Int(pixel.green)
}
}
let totalPixels = inputImage.height * inputImage.width
let avgRed = totalRed/totalPixels
let avgBlue = totalBlue/totalPixels
let avgGreen = totalGreen/totalPixels
let avgValue = ( avgRed + avgGreen + avgBlue )/3
return ( avgRed , avgGreen , avgBlue , avgValue )
}
init ( inputImage: RGBAImage ) {
rgbaImage = inputImage
( avgRed , avgGreen , avgBlue , avgPixelValue ) = averagePixelValue(input: rgbaImage!)
}
func addColorTint (color:String , intensity: Int=1)->RGBAImage{
for i in 0..<self.rgbaImage!.height{
for j in 0..<self.rgbaImage!.width{
var pixel = self.rgbaImage!.pixels[i*self.rgbaImage!.height + j]
let avgPixel = (Int(pixel.red) + Int(pixel.green) + Int(pixel.blue))/3
if ( avgPixel > avgValue ){
switch color{
case "red" :
self.rgbaImage!.pixels[i*self.rgbaImage!.height + j].red = UInt8(max(0,min(255,Int(pixel.red) * intensity)))
case "blue":
self.rgbaImage!.pixels[i*self.rgbaImage!.height + j].blue = UInt8(max(0,min(255,Int(pixel.blue) * intensity)))
case "green":
self.rgbaImage!.pixels[i*self.rgbaImage!.height + j].green = UInt8(max(0,min(255,Int(pixel.green) * intensity)))
default:
print ("0")
}
}
}
}
return self.rgbaImage!
}
}
let image = UIImage(named: "sample")
var rgbaImage = RGBAImage(image: image!)
// Process the image!
var newInstance = imageProcessor(inputImage: rgbaImage!)
let newrgbaInstance = newInstance.addColorTint("red", intensity: 2)
let newImage = newrgbaInstance.toUIImage()
newImage
The code seems to be running fine, but it shows no output in the playground output panel ( Right side of the window ). Any suggestions on to how I am supposed to make this work is highly appreciated. Also It worked when I used it as a simple function but not as a class.
Also, the library/class I am using is included below
import UIKit
public struct Pixel {
public var value: UInt32
public var red: UInt8 {
get {
return UInt8(value & 0xFF)
}
set {
value = UInt32(newValue) | (value & 0xFFFFFF00)
}
}
public var green: UInt8 {
get {
return UInt8((value >> 8) & 0xFF)
}
set {
value = (UInt32(newValue) << 8) | (value & 0xFFFF00FF)
}
}
public var blue: UInt8 {
get {
return UInt8((value >> 16) & 0xFF)
}
set {
value = (UInt32(newValue) << 16) | (value & 0xFF00FFFF)
}
}
public var alpha: UInt8 {
get {
return UInt8((value >> 24) & 0xFF)
}
set {
value = (UInt32(newValue) << 24) | (value & 0x00FFFFFF)
}
}
}
public struct RGBAImage {
public var pixels: UnsafeMutableBufferPointer<Pixel>
public var width: Int
public var height: Int
public init?(image: UIImage) {
guard let cgImage = image.CGImage else { return nil }
// Redraw image for correct pixel format
let colorSpace = CGColorSpaceCreateDeviceRGB()
var bitmapInfo: UInt32 = CGBitmapInfo.ByteOrder32Big.rawValue
bitmapInfo |= CGImageAlphaInfo.PremultipliedLast.rawValue & CGBitmapInfo.AlphaInfoMask.rawValue
width = Int(image.size.width)
height = Int(image.size.height)
let bytesPerRow = width * 4
let imageData = UnsafeMutablePointer<Pixel>.alloc(width * height)
guard let imageContext = CGBitmapContextCreate(imageData, width, height, 8, bytesPerRow, colorSpace, bitmapInfo) else { return nil }
CGContextDrawImage(imageContext, CGRect(origin: CGPointZero, size: image.size), cgImage)
pixels = UnsafeMutableBufferPointer<Pixel>(start: imageData, count: width * height)
}
public func toUIImage() -> UIImage? {
let colorSpace = CGColorSpaceCreateDeviceRGB()
var bitmapInfo: UInt32 = CGBitmapInfo.ByteOrder32Big.rawValue
bitmapInfo |= CGImageAlphaInfo.PremultipliedLast.rawValue & CGBitmapInfo.AlphaInfoMask.rawValue
let bytesPerRow = width * 4
let imageContext = CGBitmapContextCreateWithData(pixels.baseAddress, width, height, 8, bytesPerRow, colorSpace, bitmapInfo, nil, nil)
guard let cgImage = CGBitmapContextCreateImage(imageContext) else {return nil}
let image = UIImage(CGImage: cgImage)
return image
}
}
I found the error. It was that I have been using an unidentified variable avgValue, which I renamed to avgPixelValue.
I wrote a simple algorithm that detects the edges in the UIImages. It works perfectly fine with the images taken from bundle (look at first image).
After I am doing some image manipulations (apply filters, masks, crop and etc) and I pass the image to the same function it comes up messed up (image 2). I assume that that the CoreGrahics is changing something internally in the image. The question is what?
That's how I start processing the image:
public struct PixelData {
var a:UInt8 = 255
var r:UInt8
var g:UInt8
var b:UInt8
}
func findEdges(cgImage:CGImageRef)->UIImage{
var pixelData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage))
//var data = CFDataGetMutableBytePtr
var mdata: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
var data = UnsafeMutablePointer<UInt8>(mdata)
let height = CGImageGetHeight(cgImage)
let width = CGImageGetWidth(cgImage)
var start = CACurrentMediaTime()
//create an empty buffer
let emptyPixel = PixelData(a: 0, r: 0, g: 0, b: 0)
let blackPixel = PixelData(a: 255, r: 255, g: 255, b: 255)
var buffer = [PixelData](count: Int(width * height), repeatedValue: emptyPixel)
var booleanArray = [Bool](count: Int(width * height), repeatedValue: false)
for var y = 0; y < height-1; y++ {
for var x = 0; x < width; x++ {
//Current one
var currentPixelInfo: Int = ((Int(width) * Int(y)) + Int(x)) * 4
var currentAlpha = CGFloat(data[currentPixelInfo+3]) / CGFloat(255.0)
var downPixelInfo: Int = ((Int(width) * Int(y+1)) + Int(x)) * 4
var downAlpha = CGFloat(data[downPixelInfo+3]) / CGFloat(255.0)
if y == 0 && currentAlpha != 0{ // Top Edge
booleanArray[currentPixelInfo/4] = true
buffer[currentPixelInfo/4] = blackPixel
}
if y > 0 && y < height - 2{
//one up
var topPixelInfo: Int = ((Int(width) * Int(y - 1)) + Int(x )) * 4
var topAlpha = CGFloat(data[topPixelInfo+3]) / CGFloat(255.0)
if downAlpha == 0 && currentAlpha != 0 {//edge
booleanArray[currentPixelInfo/4] = true
buffer[currentPixelInfo/4] = blackPixel
}
if topAlpha == 0 && currentAlpha != 0 {//edge
booleanArray[currentPixelInfo/4] = true
buffer[currentPixelInfo/4] = blackPixel
}
}
if y == height - 2 && downAlpha != 0 {
booleanArray[downPixelInfo/4] = true
buffer[downPixelInfo/4] = blackPixel
}
}
}
for var y = 0; y < height-1; y++ {
for var x = 0; x < width-1; x++ {
//Current one
var currentPixelInfo: Int = ((Int(width) * Int(y)) + Int(x)) * 4
var currentAlpha = CGFloat(data[currentPixelInfo+3]) / CGFloat(255.0)
//Next
var nextPixelInfo: Int = ((Int(width) * Int(y)) + Int(x + 1)) * 4
var nextAlpha = CGFloat(data[nextPixelInfo+3]) / CGFloat(255.0)
//check horizontally
if x == 0 && currentAlpha != 0{ // Edge case
booleanArray[currentPixelInfo/4] = true
buffer[currentPixelInfo/4] = blackPixel
}
if x > 0 && x < width - 2{
//One before
var previousPixelInfo: Int = ((Int(width) * Int(y)) + Int(x - 1)) * 4
var previousAlpha = CGFloat(data[previousPixelInfo+3]) / CGFloat(255.0)
if nextAlpha == 0 && currentAlpha != 0 {//Living on the edge
booleanArray[currentPixelInfo/4] = true
buffer[currentPixelInfo/4] = blackPixel
}
if previousAlpha == 0 && currentAlpha != 0 {//Living on the edge
booleanArray[currentPixelInfo/4] = true
buffer[currentPixelInfo/4] = blackPixel
}
}
if x == width - 2 && nextAlpha != 0 {
booleanArray[nextPixelInfo/4] = true
buffer[nextPixelInfo/4] = blackPixel
}
}
}
var stop = CACurrentMediaTime()
let image = imageFromARGB32Bitmap(buffer, width: width, height: height)
println(stop - start)
return image!;
//self.imageView.image = image
}
func imageFromARGB32Bitmap(pixels:[PixelData], width:Int, height:Int)->UIImage? {
let bitsPerComponent:Int = 8
let bitsPerPixel:Int = 32
assert(pixels.count == Int(width * height))
var data = pixels // Copy to mutable []
let providerRef = CGDataProviderCreateWithCFData(
NSData(bytes: &data, length: data.count * sizeof(PixelData))
)
// let redPixel = PixelData(a: 255, r: 192, g: 0, b: 0)
let cgim = CGImageCreate(
width,
height,
bitsPerComponent,
bitsPerPixel,
width * Int(sizeof(PixelData)),
rgbColorSpace,
bitmapInfo,
providerRef,
nil,
true,
kCGRenderingIntentDefault
)
return UIImage(CGImage: cgim)
}
[][3]
Although I didn't figure out what exactly UIImage is doing to raw pixel data, the function that I wrote fixes the problem. Key point here is using CGImageAlphaInfo.PremultipliedFirst value of bitmap info since my data structure is expecting ARGB format.
func imageFromBitmapContext(image:CGImageRef, width:Int, height:Int)->UIImage?
{
let colorSpace:CGColorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(CGImageAlphaInfo.PremultipliedFirst.rawValue)
let bytesPerRow = 4 * width
let context = CGBitmapContextCreate(nil, Int(width), Int(height), 8, Int(bytesPerRow), colorSpace, bitmapInfo)
CGContextDrawImage(context, CGRectMake(0, 0, CGFloat(width), CGFloat(height)), image)
let image = CGBitmapContextCreateImage(context)
return UIImage(CGImage: image)
}