- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个之前从 png 文件创建的 UIImage:
let strokeUIImage = UIImage(data: pngData)
我想将 strokeImage(具有不透明度)转换为 MTLTexture
以便在 MTKView
中显示,但进行转换似乎执行了不需要的预乘,这会使所有内容变暗半透明边缘。
我的混合设置如下:
pipelineDescriptor.colorAttachments[0].isBlendingEnabled = true
pipelineDescriptor.colorAttachments[0].rgbBlendOperation = .add
pipelineDescriptor.colorAttachments[0].alphaBlendOperation = .add
pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .one
pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .one
pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
我试过两种转换方法:
let stampTexture = try! MTKTextureLoader(device: self.device!).newTexture(cgImage: strokeUIImage.cgImage!, options: nil)
以及更精细的 dataProvider 驱动方法:
let image = strokeUIImage.cgImage!
let imageWidth = image.width
let imageHeight = image.height
let bytesPerPixel:Int! = 4
let rowBytes = imageWidth * bytesPerPixel
let texDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Unorm_srgb,
width: imageWidth,
height: imageHeight,
mipmapped: false)
guard let stampTexture = device!.makeTexture(descriptor: texDescriptor) else { return }
let srcData: CFData! = image.dataProvider?.data
let pixelData = CFDataGetBytePtr(srcData)
let region = MTLRegionMake2D(0, 0, imageWidth, imageHeight)
stampTexture.replace(region: region, mipmapLevel: 0, withBytes: pixelData!, bytesPerRow: Int(rowBytes))
两者都会产生相同的不需要的预乘结果。
我尝试了后者,因为有一些帖子表明旧的 swift3 方法 CGDataProviderCopyData()
从未预乘的图像中提取原始像素数据。可悲的是,等价物:
let srcData: CFData! = image.dataProvider?.data
似乎并不能解决问题。我错过了什么吗?
如有任何指点,我们将不胜感激。
最佳答案
经过多次试验,我找到了解决 CoreGraphics 图像中固有的预乘问题的解决方案。感谢 Warren 关于使用 Accelerate 函数(特别是 vImageUnpremultiplyData_ARGB8888
)的提示,我想,为什么不使用 vImage_CGImageFormat
构建 CGImage,这将允许我使用 bitmapInfo 设置指定如何解释 alpha...结果并不完美,如下图附件所示:
不知何故,在翻译中,alpha 值被稍微提高了,(可能是 rgb,但不是很明显)。顺便说一下,我应该指出 png 像素格式是 sRGB,我使用的 MTKView 设置为 MTLPixelFormat.rgba16Float
(应用要求)
下面是我实现的完整 metalDrawStrokeUIImage 例程。特别值得注意的是这一行:
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue)
这实际上是在不调用 vImageUnpremultiplyData_ARGB8888
的情况下取消关联 alpha(我认为)。查看生成的图像肯定看起来像未预乘的图像...
最后,为了在 MTKView 端取回预乘纹理,我让片段着色器处理预乘:
fragment float4 premult_fragment(VertexOut interpolated [[stage_in]],
texture2d<float> texture [[texture(0)]],
sampler sampler2D [[sampler(0)]]) {
float4 sampled = texture.sample(sampler2D, interpolated.texCoord);
// this fragment shader premultiplies incoming rgb with texture's alpha
return float4(sampled.r * sampled.a,
sampled.g * sampled.a,
sampled.b * sampled.a,
sampled.a );
} // end of premult_fragment
结果非常接近输入源,但图像可能比传入的 png 不透明 5%。同样,png 像素格式是 sRGB,我用来显示的 MTKView 设置为 MTLPixelFormat.rgba16Float
。所以,我确定某处有些东西变得糊涂了。如果有人有任何指示,我将不胜感激。
下面是相关代码的其余部分:
func metalDrawStrokeUIImage (strokeUIImage: UIImage, strokeBbox: CGRect) {
self.metalSetupRenderPipeline(compStyle: compMode.strokeCopy) // needed so stampTexture is not modified by fragmentFunction
let bytesPerPixel = 4
let bitsPerComponent = 8
let width = Int(strokeUIImage.size.width)
let height = Int(strokeUIImage.size.height)
let rowBytes = width * bytesPerPixel
//
let texDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Unorm_srgb,
width: width,
height: height,
mipmapped: false)
guard let stampTexture = device!.makeTexture(descriptor: texDescriptor) else { return }
//let cgImage: CGImage = strokeUIImage.cgImage!
//let sourceColorSpace = cgImage.colorSpace else {
guard
let cgImage = strokeUIImage.cgImage,
let sourceColorSpace = cgImage.colorSpace else {
print("Unable to initialize cgImage or colorSpace.")
return
}
var format = vImage_CGImageFormat(
bitsPerComponent: UInt32(cgImage.bitsPerComponent),
bitsPerPixel: UInt32(cgImage.bitsPerPixel),
colorSpace: Unmanaged.passRetained(sourceColorSpace),
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue),
version: 0, decode: nil,
renderingIntent: CGColorRenderingIntent.defaultIntent)
var sourceBuffer = vImage_Buffer()
defer {
free(sourceBuffer.data)
}
var error = vImageBuffer_InitWithCGImage(&sourceBuffer, &format, nil, cgImage, numericCast(kvImageNoFlags))
guard error == kvImageNoError else {
print ("[MetalBrushStrokeView]: can't vImageBuffer_InitWithCGImage")
return
}
//vImagePremultiplyData_RGBA8888(&sourceBuffer, &sourceBuffer, numericCast(kvImageNoFlags))
// create a CGImage from vImage_Buffer
var destCGImage = vImageCreateCGImageFromBuffer(&sourceBuffer, &format, nil, nil, numericCast(kvImageNoFlags), &error)?.takeRetainedValue()
guard error == kvImageNoError else {
print ("[MetalBrushStrokeView]: can't vImageCreateCGImageFromBuffer")
return
}
let dstData: CFData = (destCGImage!.dataProvider!.data)!
let pixelData = CFDataGetBytePtr(dstData)
destCGImage = nil
let region = MTLRegionMake2D(0, 0, Int(width), Int(height))
stampTexture.replace(region: region, mipmapLevel: 0, withBytes: pixelData!, bytesPerRow: Int(rowBytes))
let stampColor = UIColor.white
let stampCorners = self.stampSetVerticesFromBbox(bbox: strokeBbox)
self.stampAppendToVertexBuffer(stampLayer: stampLayerMode.stampLayerFG, stampCorners: stampCorners, stampColor: stampColor)
self.metalRenderStampSingle(stampTexture: stampTexture)
self.initializeStampArray() // clears out the stamp array so we always draw 1 stamp at a time
} // end of func metalDrawStrokeUIImage (strokeUIImage: UIImage, strokeBbox: CGRect)
关于swift - 无需预乘即可将 CGImage 转换为 MTLTexture,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56164586/
我正在使用 Metal 来显示相机框架。我将输出的样本缓冲区转换为 id使用 CVMetalTextureCacheCreateTextureFromImage ,效果很好……除了框架顺时针旋转了 9
我关注了this example 从 Apple 缩小非常大的图像我是 downloading from a remote location .我已经用 Swift 重写了代码。它显然有效,但是当我调
我可以创建一个新的 MTLTexture 尺寸 w2/h2 现有 MTLTexture 区域 x1/y1/w1/h1 ? PS:我考虑过使用 MTLTexture.buffer?.makeTextur
我正在尝试获取 MTLTexture 的像素这边走: let pixelCount = compareTexture.width * compareTexture.height let region
我有一个 MTLTexture,它是它出现在 MTLView 中的大小。但是,我正在将纹理写入 AVAssetWriterInputPixelBufferAdaptor 以录制需要纹理的视频标准视频尺
MTLTexture 之间的主要区别是什么?对比 CGImageRef ?我们什么时候需要使用MTLTexture而不是 CGImageRef (反之亦然)? 我有一个应用程序(比如视频游戏),它可以
我使用以下代码创建了一个 MTLTexture 对象(为清楚起见,仅列出了部分代码)。 int tmpbuf[4096] id device = MTLCreateSystemDefaultDevic
我试图通过计算每个像素的所有 R G B 和 A 分量的总和来确定 MTLTexture(bgra8Unorm 格式)是否为空白。 此函数旨在通过在将纹理复制到指针后在内存中添加相邻的 float 来
我知道如何创建 MTLBuffer 和/或 MTLTexture,但是当不再需要这些资源时如何释放 GPU 内存? 最佳答案 MTLBuffer 和 MTLTexture 是 Objective-C
写入IOSurface时需要使用哪些API,以及需要采取哪些预防措施在 XPC 进程中,该进程也被用作 MTLTexture 的后备存储。在主应用程序中? 在我的 XPC 服务中,我有以下内容: IO
我有这个方法 - (id) createTextureFromImage:(UIImage*) image device:(id) device { CGImageRef imageRef = i
我将计算着色器的结果存储在 MTLBuffer 中。 MTLBuffer 的每个元素都是一个 UInt16。我试图将结果传递给片段着色器,该着色器通过将 MTLBuffer 的元素解释为 0 到 25
我是 Metal 新手。我想从 CVImageBufferRef 制作 MTLTexture。我正在使用以下代码示例来执行此操作。 guard let unwrappedImageTe
我有一个之前从 png 文件创建的 UIImage: let strokeUIImage = UIImage(data: pngData) 我想将 strokeImage(具有不透明度)转换为 MTL
MTLTexture 是一种内存分配,用于存储 GPU 可访问的格式化图像数据。我想知道有没有什么方法可以在本地保存 MTLTexture 数据? 如果是这样,在本地保存 MTLTexture 数据的
我目前正在使用 Metal 开发实时过滤器。在定义我的 CIImage 之后,我将图像渲染到 MTLTexture。 下面是我的渲染代码。 context 是由 Metal 支持的 CIContext
生成由 CVPixelBuffer 支持的 MTLTexture 的正确方法是什么? 我有以下代码,但它似乎泄漏了: func PixelBufferToMTLTexture(pixelBuffer:
我有内核函数,它必须将从 pixelBuffer(ARFrame.capturedImage) 创建的 Y 和 CbCr 纹理转换为 RGB 纹理,就像在苹果指南中一样 https://develop
尝试在 SCNRender 对象的每次调用渲染上从 MTLTexture 创建 CVPixelBufferRef: CVPixelBufferLockBaseAddress(pixelBuffer
我有一个(动画)UIView-Hierarchy,我想定期将 UIView 内容渲染到 MTLTexture 中以供进一步处理。 我尝试过的是子类化我的父 UIView 和 override publ
我是一名优秀的程序员,十分优秀!