pcb-to-stencil/svg_render_darwin.go

118 lines
2.7 KiB
Go

package main
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework AppKit -framework CoreGraphics
#import <AppKit/AppKit.h>
#import <stdlib.h>
// Renders SVG data to raw RGBA pixels using macOS native NSImage.
// Returns NULL on failure. Caller must free() the returned pixels.
unsigned char* nativeRenderSVG(const void* svgBytes, int svgLen, int targetW, int targetH) {
@autoreleasepool {
NSData *data = [NSData dataWithBytesNoCopy:(void*)svgBytes length:svgLen freeWhenDone:NO];
NSImage *svgImage = [[NSImage alloc] initWithData:data];
if (!svgImage) return NULL;
int w = targetW;
int h = targetH;
int rowBytes = w * 4;
int totalBytes = rowBytes * h;
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:w
pixelsHigh:h
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
bytesPerRow:rowBytes
bitsPerPixel:32];
[NSGraphicsContext saveGraphicsState];
NSGraphicsContext *ctx = [NSGraphicsContext graphicsContextWithBitmapImageRep:rep];
[NSGraphicsContext setCurrentContext:ctx];
// Start with fully transparent background
[[NSColor clearColor] set];
NSRectFill(NSMakeRect(0, 0, w, h));
// Draw SVG, preserving alpha
[svgImage drawInRect:NSMakeRect(0, 0, w, h)
fromRect:NSZeroRect
operation:NSCompositingOperationSourceOver
fraction:1.0];
[NSGraphicsContext restoreGraphicsState];
unsigned char* result = (unsigned char*)malloc(totalBytes);
if (result) {
memcpy(result, [rep bitmapData], totalBytes);
}
return result;
}
}
*/
import "C"
import (
"image"
"image/color"
"unsafe"
)
// renderSVGNative uses macOS NSImage to render SVG data to an image.Image
// with full transparency support.
func renderSVGNative(svgData []byte, width, height int) image.Image {
if len(svgData) == 0 {
return nil
}
pixels := C.nativeRenderSVG(
unsafe.Pointer(&svgData[0]),
C.int(len(svgData)),
C.int(width),
C.int(height),
)
if pixels == nil {
return nil
}
defer C.free(unsafe.Pointer(pixels))
rawLen := width * height * 4
raw := unsafe.Slice((*byte)(unsafe.Pointer(pixels)), rawLen)
img := image.NewNRGBA(image.Rect(0, 0, width, height))
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
i := (y*width + x) * 4
r, g, b, a := raw[i], raw[i+1], raw[i+2], raw[i+3]
// NSImage gives premultiplied alpha — convert to straight
if a > 0 && a < 255 {
scale := 255.0 / float64(a)
r = clampByte(float64(r) * scale)
g = clampByte(float64(g) * scale)
b = clampByte(float64(b) * scale)
}
img.SetNRGBA(x, y, color.NRGBA{R: r, G: g, B: b, A: a})
}
}
return img
}
func clampByte(v float64) uint8 {
if v > 255 {
return 255
}
if v < 0 {
return 0
}
return uint8(v)
}