Hi,
I'm trying to transition my macOS application to use Metal. It uses CIFilter's extensively. I'm trying to create a Metal texture for use in a MTKView subclass. The source data is an array of float values, not an RGB image. I add color later with a CIFilter colormap. The existing code (that works) creates a CIImage like this:
NSData *convertedData = <float data>
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((CFDataRef)convertedData);
CGBitmapInfo bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrder32Host | kCGBitmapFloatComponents;
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
CGImageRef cgImage = CGImageCreate(width, // size_t width
height, // size_t height
32, // size_t bitsPerComponent (float=32)
32, // size_t bitsPerPixel == bitsPerComponent for float
bytesPerRow, // size_t bytesPerRow -> width in pixels * sizeof(float)
colorSpace, // CGColorSpaceRef
bitmapInfo, // CGBitmapInfo
dataProvider, // CGDataProviderRef
NULL, // const CGFloat decode[] - NULL = do not want to allow
// remapping of the image’s color values
NO, // shouldInterpolate?
kCGRenderingIntentDefault); // CGColorRenderingIntent
CIImage *ciImage = [CIImage imageWithCGImage:cgImage options:@{kCIImageColorSpace: [NSNull null]}];
I tried to convert this into a Metal texture:
MTKTextureLoader *textureLoader = [[MTKTextureLoader alloc] initWithDevice:self.device];
NSError *error = nil;
NSNumber *textureUsageOptions = @(MTLTextureUsageUnknown); // TODO: revisit this when things work
texture = [textureLoader newTextureWithCGImage:ciImage.CGImage // CGImageRef
options:@{
MTKTextureLoaderOptionTextureUsage:textureUsageOptions,
MTKTextureLoaderOptionSRGB:@(0) // image data is linear
}
error:&error];
I get this error:
Error Domain=MTKTextureLoaderErrorDomain Code=0 "Image decoding failed"
UserInfo={NSLocalizedDescription=Image decoding failed, MTKTextureLoaderErrorKey=Image decoding failed}
Is this the right approach? I don't tend to see many examples using float components to create images.
Thanks!
In case anyone needs the answer, the solution is to create a texture descriptor directly from the data:
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR32Float
width:width
height:height
mipmapped:NO];
Create a new texture using that descriptor ([device newTextureWithDescriptor]), then use the "replaceRegion:mipmapLevel..." method to copy the data directly into the texture buffer.