2015-07-27 13 views
14

Per un dato multicolore PNG UIImage (con trasparenza), qual è il modo migliore/Swift-idiomatica a:Cambiare il colore di alcuni pixel in un UIImage

  1. creare un duplicato UIImage
  2. trovare tutti pixel neri nella copia e modificarli al rosso
  3. (restituiscono la copia modificata)

ci sono alcune domande correlate sul SO, ma non sono stati in grado di trovare qualcosa che funziona.

+0

Una cosa che posso suggerire è passare attraverso ogni pixel e modificarlo manualmente se è nero. – Aggressor

+0

In effetti ... la mia domanda è _come_ farlo :) Sono nuovo di Swift e sono così poco pratico con le API che non so nemmeno cosa sia su Google. – mjswensen

risposta

34

È necessario estrarre il buffer di pixel dell'immagine, a quel punto è possibile scorrere ciclicamente, modificando i pixel come si ritiene opportuno. Alla fine, crea una nuova immagine dal buffer.

In Swift 3, questo appare come:

func processPixels(in image: UIImage) -> UIImage? { 
    guard let inputCGImage = image.cgImage else { 
     print("unable to get cgImage") 
     return nil 
    } 
    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("unable to create context") 
     return nil 
    } 
    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 nil 
    } 

    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 
      if pixelBuffer[offset] == .black { 
       pixelBuffer[offset] = .red 
      } 
     } 
    } 

    let outputCGImage = context.makeImage()! 
    let outputImage = UIImage(cgImage: outputCGImage, scale: image.scale, orientation: image.imageOrientation) 

    return outputImage 
} 

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 
    } 
} 

Per Swift 2 rendition, vedere previous revision of this answer.

+0

Rob, grazie per la tua pronta e completa risposta! Sto eseguendo il seguente errore di runtime quando provo il tuo codice: ': CGBitmapContextCreate: combinazione di parametri non supportati: 8 bit interi/componente; 24 bit/pixel; Spazio colore a 3 componenti; kCGImageAlphaNone; 352 byte/riga. errore fatale: trovato inatteso nil mentre si scartava un valore facoltativo' - Potrebbe essere il 'colorSpace'? – mjswensen

+0

'iPad/iOS ridimensionabile 8.4 (12H141)'. Grazie ancora per il tuo aiuto. – mjswensen

+1

Sì, quel simulatore funziona bene per me. Il 'kCGImageAlphaNone' nel tuo messaggio di errore è altamente sospetto. È quasi come passare '0' per l'ultimo parametro di 'CGBitmapContextCreate'. Ricontrolla il parametro 'bitmapInfo'. Prova semplicemente 'CGBitmapInfo (CGImageAlphaInfo.PremultipliedLast.rawValue)' per 'bitmapInfo'. – Rob

Problemi correlati