ALL BUSINESS
COMIDA
DIRECTORIES
EDUCATIONAL
ENTERTAINMENT
FASHION TIPS
FINER THINGS
FREE CREATOR TOOLS
HEALTH
MARKETPLACE
MEMBER's ONLY
MONEY MATTER$
MOTIVATIONAL
NEWS & WEATHER
TECHNOLOGIA
TELEVISION NETWORKS
USA VOTES 2024
VIDEOS
INVESTOR RELATIONS
IN DEVELOPMENT
Posted by - Latinos MediaSyndication -
on - September 28, 2023 -
Filed in - Technology -
-
367 Views - 0 Comments - 0 Likes - 0 Reviews
I'm trying to convert a metal texture to png data ( or any lossless compressed format) to save it to disk.
After converting the data back to a texture, I want to get back the same texture, unaltered.
I've got this working with opaque textures, but with transparent pixels something goes wrong.
This is the code I'm using to compress and decompress the texture.
public struct TextureCompresser { let context:CIContext let loader:MTKTextureLoader public init(device:MTLDevice) { loader = MTKTextureLoader(device: device) context = CIContext(options: [ CIContextOption.outputPremultiplied: false, ]) } public func compress(texture:MTLTexture) -> Data? { let options:[CIImageOption:Any] = [CIImageOption.colorSpace:CGColorSpace(name: CGColorSpace.linearSRGB)!] guard let ciImage = CIImage(mtlTexture: texture, options:options) else { return nil } return context.pngRepresentation(of: ciImage, format: .BGRA8, colorSpace: CGColorSpace(name: CGColorSpace.sRGB)!) } public func decompress(png:Data) throws -> MTLTexture { let usage = MTLTextureUsage(rawValue: MTLTextureUsage.renderTarget.rawValue | MTLTextureUsage.shaderRead.rawValue | MTLTextureUsage.shaderWrite.rawValue) let options:[MTKTextureLoader.Option : Any] = [ MTKTextureLoader.Option.textureUsage:usage.rawValue, MTKTextureLoader.Option.origin:MTKTextureLoader.Origin.flippedVertically.rawValue, MTKTextureLoader.Option.SRGB:NSNumber(value: true), MTKTextureLoader.Option.generateMipmaps:NSNumber(value: false), ] return try loader.newTexture(data:png,options:options) } }
I have put this code in a repo here.
Note that I'm using a linear color space for CIImage options. That looks wrong to me, as my textures are sRGB, but that was the only way I could get it to work at all (but only for opaque pixels). My textures are bgra8Unorm_srgb.
Although it does works for the opaque pixels, transparent pixels are altered. For example a test pixel is transformed after a compress/decompress like this:
[24, 72, 233, 78] -> [48, 127, 255, 78]
I've tried every possible combination of options and color spaces that I can think of, but nothing seems to work. Does anyone know what I'm doing wrong?
Update - image magik
I've also looked at the pixels in the resulting png with the ImageMagik utility. For the test pixel (24, 72, 233, 78) I get this:
0,0: (255,127,48,78) #FF7F304E srgba(255,127,48,0.305882)
That matches what I'm getting from loading the texture (except the order because the texture is BRG) so it looks like the issue is with saving rather than loading.
Update - other questions
I've looked at all the similar questions on stack overflow, and none of the address this issue. The closest ones are these two:
Saving and Loading MTLTexture as PNG This one is trying to do the same thing, but the problem is scale. It does not address the problem I have described.
Swift Metal save bgra8Unorm texture to PNG file This one is also trying to save a texture as a png, but it does not mention the issue I have. It uses a slightly different api to make the png, but I've checked and it has the same problem.