You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C99中实现SCScreenshotManager_captureImage适配macOS 15?

基于ScreenCaptureKit的C99截图实现方案

针对macOS 15中CGDisplayCreateImageForRect废弃的问题,以下是用C99封装ScreenCaptureKit截图逻辑的可行方案,可直接供Go通过C绑定调用:

核心思路

ScreenCaptureKit仅提供Swift/ObjC API,因此先通过Swift实现核心截图逻辑,再通过@_cdecl暴露C兼容接口,最后编译为静态库供C/Go调用。

1. Swift核心实现(封装ScreenCaptureKit)

创建Swift文件SCScreenshotWrapper.swift:

import ScreenCaptureKit
import CoreGraphics
import Dispatch

@_cdecl("SCScreenshotManager_captureImage")
func captureImage(displayID: UInt32, x: Int, y: Int, width: Int, height: Int,
                  outImageData: UnsafeMutablePointer<UnsafeMutableRawPointer?>?,
                  outWidth: UnsafeMutablePointer<Int>?,
                  outHeight: UnsafeMutablePointer<Int>?,
                  outBytesPerRow: UnsafeMutablePointer<Int>?) -> Int32 {
    // 检查ScreenCaptureKit支持性
    guard SCShareableContent.isSupported else {
        return -1
    }
    
    let captureRect = CGRect(x: x, y: y, width: width, height: height)
    let targetDisplay = SCShareableContent.Display(displayID: CGDirectDisplayID(displayID))
    
    // 配置截图参数(保持与旧API输出格式一致:RGBA8888)
    let config = SCScreenshotConfiguration(display: targetDisplay, bounds: captureRect)
    config.pixelFormat = .RGBA8888
    config.showsCursor = false // 按需开启
    
    var rawData: UnsafeMutableRawPointer?
    var imgWidth = 0
    var imgHeight = 0
    var rowBytes = 0
    
    // 用信号量实现同步捕获(适配C同步接口)
    let semaphore = DispatchSemaphore(value: 0)
    SCScreenshotManager.captureImage(with: config) { result in
        defer { semaphore.signal() }
        switch result {
        case .success(let cgImage):
            // 将CGImage转换为可导出的原始像素数据
            let context = CGContext(data: nil,
                                    width: cgImage.width,
                                    height: cgImage.height,
                                    bitsPerComponent: 8,
                                    bytesPerRow: cgImage.width * 4,
                                    space: CGColorSpaceCreateDeviceRGB(),
                                    bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
            context.draw(cgImage, in: CGRect(origin: .zero, size: cgImage.size))
            
            rawData = context.data
            imgWidth = cgImage.width
            imgHeight = cgImage.height
            rowBytes = context.bytesPerRow
        case .failure:
            break
        }
    }
    semaphore.wait()
    
    // 输出结果到C指针
    outImageData?.pointee = rawData
    outWidth?.pointee = imgWidth
    outHeight?.pointee = imgHeight
    outBytesPerRow?.pointee = rowBytes
    
    return rawData != nil ? 0 : -2
}

@_cdecl("SCScreenshotManager_freeImageData")
func freeImageData(data: UnsafeMutableRawPointer?) {
    data?.deallocate()
}

2. C99头文件定义

创建头文件sc_screenshot_wrapper.h,供Go绑定使用:

#ifndef SC_SCREENSHOT_WRAPPER_H
#define SC_SCREENSHOT_WRAPPER_H

#include <stdint.h>
#include <stddef.h>

// 截图函数:返回0成功,-1不支持ScreenCaptureKit,-2捕获失败
// outImageData: 输出原始RGBA像素数据(需调用free函数释放)
int32_t SCScreenshotManager_captureImage(uint32_t displayID, int x, int y, int width, int height,
                                         void **outImageData, int *outWidth, int *outHeight, int *outBytesPerRow);

// 释放截图数据(避免内存泄漏)
void SCScreenshotManager_freeImageData(void *imageData);

#endif

3. 编译Swift为静态库

用Xcode创建静态库项目,将上述Swift文件加入项目,编译生成多架构(arm64/x86_64)的静态库:

xcodebuild -project SCScreenshotWrapper.xcodeproj -scheme SCScreenshotWrapper -configuration Release -arch arm64 -arch x86_64 build

4. Go语言绑定调用

在Go代码中通过CGO调用该C接口:

/*
#cgo LDFLAGS: -L./path/to/library -lSCScreenshotWrapper -framework ScreenCaptureKit -framework CoreGraphics -framework Foundation
#include "sc_screenshot_wrapper.h"
*/
import "C"
import (
    "fmt"
    "image"
    "image/rgba"
    "unsafe"
)

func CaptureDisplayRect(displayID uint32, x, y, width, height int) (*image.RGBA, error) {
    var cImgData *C.void
    var cWidth, cHeight, cBytesPerRow C.int

    res := C.SCScreenshotManager_captureImage(
        C.uint32_t(displayID),
        C.int(x), C.int(y), C.int(width), C.int(height),
        &cImgData, &cWidth, &cHeight, &cBytesPerRow,
    )

    if res != 0 {
        return nil, fmt.Errorf("capture failed with code %d", res)
    }
    defer C.SCScreenshotManager_freeImageData(cImgData)

    widthInt := int(cWidth)
    heightInt := int(cHeight)
    bytesPerRowInt := int(cBytesPerRow)

    // 将C原始数据转换为Go RGBA图像
    pixelSlice := unsafe.Slice((*uint8)(unsafe.Pointer(cImgData)), bytesPerRowInt*heightInt)
    img := rgba.NewRGBA(image.Rect(0, 0, widthInt, heightInt))
    copy(img.Pix, pixelSlice)

    return img, nil
}

关键注意事项

  • 权限配置:必须在应用的Info.plist中添加NSScreenCaptureUsageDescription字段,说明截图用途,否则会被系统拒绝授权。
  • 内存安全:必须调用SCScreenshotManager_freeImageData释放截图数据,否则会导致内存泄漏。
  • 格式兼容:采用RGBA8888像素格式,与原CGDisplayCreateImageForRect的输出格式一致,可直接适配原有库的图像处理逻辑。

内容的提问来源于stack exchange,提问作者Roman Pushkin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 22:54:51