How do I get white NSImages with SF Symbols?

When I get imageWithSystemSymbolName, I get black graphics on a transparent background. How can I get white symbols?

When I look at the symbols (like triangle.circle) in SF Symbols, they're all white on transparent. For example, I want to draw triangle.circle.fill on a black screen, so I don't want a black image.

I'm doing something like this to get SF Symbols (as NSData to pass them as a byte array into Unity):

Code Block objc
NSData* GetSymbolImageAsBytes(int width, int height)
{
GCController *gc = [GCController current];
NSString *name = gc.extendedGamepad.buttonY.sfSymbolsName;
NSImage *image = [NSImage
imageWithSystemSymbolName: name
accessibilityDescription: nil
];
// https://stackoverflow.com/a/38442746/79125
NSSize size = NSMakeSize(width, height);
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:size.width
pixelsHigh:size.height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bytesPerRow:0
bitsPerPixel:0];
rep.size = size;
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:rep]];
[image drawInRect:NSMakeRect(0, 0, size.width, size.height) fromRect:NSZeroRect operation:NSCompositingOperationCopy fraction:1.0];
[NSGraphicsContext restoreGraphicsState];
if (rep == nil)
{
return nil;
}
// Passing nil gives a warning but seems to work okay.
return [rep representationUsingType:NSBitmapImageFileTypePNG properties:nil];
}

Answered by Frameworks Engineer in 663443022
Normally any styling (such as tints) are applied at the time the symbol is drawn by the UI element (button, etc) because it's context sensitive to the state of the UI element (pressed, disabled, etc).

To do this yourself, setup a CG transparency layer right before drawing the symbol:

Code Block Objective-C
            NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
            [graphicsContext saveGraphicsState];
            CGContextRef context = [graphicsContext CGContext];
            CGContextBeginTransparencyLayerWithRect(context, dstRect, nil);
            CGContextSetBlendMode(context, kCGBlendModeNormal);
/* draw the image */
            CGContextSetBlendMode(context, kCGBlendModeSourceIn);
CGContextSetFillColor(context, /* desired fill color */);
            CGContextFillRect(context, dstRect);
            CGContextEndTransparencyLayer(context);

Note that by doing this your are stripping the symbol image of its configuration and layout metrics. It won't be able to adapt to different point sizes or weights, and baseline offsets will be lost. This means, for instance, that the minus symbol will be 3 pixels tall at typical point sizes. If you simply draw that based on its bitmap size it will look like an underscore.

To fix this you need to pay attention to the NSImage's alignmentRect property, which is a rectangle whose bottom edge is the baseline and top edge is the capHeight.
Accepted Answer
Normally any styling (such as tints) are applied at the time the symbol is drawn by the UI element (button, etc) because it's context sensitive to the state of the UI element (pressed, disabled, etc).

To do this yourself, setup a CG transparency layer right before drawing the symbol:

Code Block Objective-C
            NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
            [graphicsContext saveGraphicsState];
            CGContextRef context = [graphicsContext CGContext];
            CGContextBeginTransparencyLayerWithRect(context, dstRect, nil);
            CGContextSetBlendMode(context, kCGBlendModeNormal);
/* draw the image */
            CGContextSetBlendMode(context, kCGBlendModeSourceIn);
CGContextSetFillColor(context, /* desired fill color */);
            CGContextFillRect(context, dstRect);
            CGContextEndTransparencyLayer(context);

Note that by doing this your are stripping the symbol image of its configuration and layout metrics. It won't be able to adapt to different point sizes or weights, and baseline offsets will be lost. This means, for instance, that the minus symbol will be 3 pixels tall at typical point sizes. If you simply draw that based on its bitmap size it will look like an underscore.

To fix this you need to pay attention to the NSImage's alignmentRect property, which is a rectangle whose bottom edge is the baseline and top edge is the capHeight.
How do I get white NSImages with SF Symbols?
 
 
Q