118 lines
2.7 KiB
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)
|
|
}
|