Home > Net >  UIImageView Is Blending Colors With No Alpha
UIImageView Is Blending Colors With No Alpha

Time:10-25

I have been stumped by this for a few days now and can't find a solution. I have a UIImageView with a transparent background. It is on top of another view (in the example just a UIView with blue background color). For some reason it will blend a color even if it has an alpha value of 0 (in this case full red no alpha values comes out pink). The desired output is that it will show clear just like black with no alpha value.

If over white it behaves as intended (it is clear and shows white through). I have tried different ways of creating the CGContext and different CALayer blend modes and can't get it to not blend colors that don't have alpha values on them.

enter image description here

Here is sample code to replicate the issue.

import UIKit
import PlaygroundSupport
import CoreGraphics

class MyViewController : UIViewController {
    var pixelPointer: UnsafeMutablePointer<UInt8>!
    
    let imageWidth = 20
    let imageHeight = 20
    
    override func loadView() {
        let view = UIView()
        view.backgroundColor = .blue
        
        let viewSize: CGFloat = 150
        
        // Test Image
        pixelPointer  =  UnsafeMutablePointer<UInt8>.allocate(capacity: imageWidth * imageWidth * 4)
        let ctx  = CGContext(data: pixelPointer,
                             width: imageWidth,
                             height: imageHeight,
                             bitsPerComponent: 8,
                             bytesPerRow: 4 * imageWidth,
                             space: CGColorSpaceCreateDeviceRGB(),
                             bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
        for x in 0 ..< imageWidth {
            for y in 0 ..< imageHeight {
                if y == 7 || y == 8 || y == 9 {
                    // Red No Alpha -> Expected to be transparent
                    setColorFor(255, g: 0, b: 0, a: 0, x: x, y: y)
                } else if y == 10 || y == 11 || y == 12 {
                    // Black No Alpha -> Is transparent
                    setColorFor(0, g: 0, b: 0, a: 0, x: x, y: y)
                } else {
                    // Red
                    setColorFor(255, g: 0, b: 0, a: 255, x: x, y: y)
                }
                
            }
        }
        let cgImage = ctx.makeImage()!
        
        // ImageView with Clear Background
        let imageViewClearBackground = UIImageView()
        imageViewClearBackground.backgroundColor = .clear
        imageViewClearBackground.frame.origin = CGPoint(x: 10, y: 10)
        imageViewClearBackground.layer.borderColor = UIColor.black.cgColor
        imageViewClearBackground.layer.borderWidth = 1
        imageViewClearBackground.frame.size = CGSize(width: viewSize, height: viewSize)
        imageViewClearBackground.layer.magnificationFilter = CALayerContentsFilter.nearest
        
        imageViewClearBackground.image = UIImage(cgImage: cgImage)
        view.addSubview(imageViewClearBackground)
        
        // ImageView with White Background
        let imageViewWhiteBackground = UIImageView()
        imageViewWhiteBackground.layer.borderColor = UIColor.black.cgColor
        imageViewWhiteBackground.layer.borderWidth = 1
        imageViewWhiteBackground.backgroundColor = .white
        imageViewWhiteBackground.frame.size = CGSize(width: viewSize, height: viewSize)
        imageViewWhiteBackground.frame.origin = CGPoint(x: viewSize   20, y: 10)
        imageViewWhiteBackground.layer.magnificationFilter = CALayerContentsFilter.nearest
        
        imageViewWhiteBackground.image = UIImage(cgImage: cgImage)
        view.addSubview(imageViewWhiteBackground)
        
        self.view = view
    }
    
    func setColorFor(_ r: Int, g: Int, b: Int, a: Int, x: Int, y: Int) {
        let offset = (y * Int(imageWidth) * 4)   x * 4
        pixelPointer[offset 0] = UInt8(r)
        pixelPointer[offset 1] = UInt8(g)
        pixelPointer[offset 2] = UInt8(b)
        pixelPointer[offset 3] = UInt8(a)
    }
}

// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

CodePudding user response:

The problem is that this:

setColorFor(255, g: 0, b: 0, a: 0, x: x, y: y)

is not a valid .premultipliedLast color value. Premultiplied means that the colors have already been multiplied by the alpha. If you multiply 255 by 0 you get 0, so that is the correct red value here — as the result in rows 10-12 demonstrates.

You would probably confuse yourself a lot less if you would just construct the image using UIGraphicsImageRenderer in the normal way rather than a raw bitmap context. But of course if your use case precludes that, then by all means use the bitmap — but then there is a lot more room for you to use it incorrectly.

  • Related