UIImageView 在没有 Alpha 的情况下混合颜色
UIImageView Is Blending Colors With No Alpha
我已经被这个问题难住了几天了,找不到解决办法。我有一个透明背景的 UIImageView。它位于另一个视图之上(在示例中只是一个具有蓝色背景颜色的 UIView)。出于某种原因,它会混合一种颜色,即使它的 alpha 值为 0(在这种情况下,全红色没有 alpha 值会变成粉红色)。所需的输出是它将像黑色一样清晰显示,没有 alpha 值。
如果超过白色,它会按预期运行(它很清晰并且透白)。我尝试了不同的方法来创建 CGContext 和不同的 CALayer 混合模式,但无法让它不混合没有 alpha 值的颜色。
下面是一些示例代码来重现该问题。
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()
问题在于:
setColorFor(255, g: 0, b: 0, a: 0, x: x, y: y)
不是有效的 .premultipliedLast
颜色值。预乘意味着颜色已经乘以 alpha。如果将 255 乘以 0,则得到 0,因此 是此处正确的红色值 — 正如第 10-12 行中的结果所示。
如果您只是以正常方式使用 UIGraphicsImageRenderer 而不是原始位图上下文来构建图像,您可能会少很多混淆。但是当然,如果您的用例排除了这一点,那么一定要使用位图——但是这样您就有更多的空间不正确地使用它。
我已经被这个问题难住了几天了,找不到解决办法。我有一个透明背景的 UIImageView。它位于另一个视图之上(在示例中只是一个具有蓝色背景颜色的 UIView)。出于某种原因,它会混合一种颜色,即使它的 alpha 值为 0(在这种情况下,全红色没有 alpha 值会变成粉红色)。所需的输出是它将像黑色一样清晰显示,没有 alpha 值。
如果超过白色,它会按预期运行(它很清晰并且透白)。我尝试了不同的方法来创建 CGContext 和不同的 CALayer 混合模式,但无法让它不混合没有 alpha 值的颜色。
下面是一些示例代码来重现该问题。
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()
问题在于:
setColorFor(255, g: 0, b: 0, a: 0, x: x, y: y)
不是有效的 .premultipliedLast
颜色值。预乘意味着颜色已经乘以 alpha。如果将 255 乘以 0,则得到 0,因此 是此处正确的红色值 — 正如第 10-12 行中的结果所示。
如果您只是以正常方式使用 UIGraphicsImageRenderer 而不是原始位图上下文来构建图像,您可能会少很多混淆。但是当然,如果您的用例排除了这一点,那么一定要使用位图——但是这样您就有更多的空间不正确地使用它。