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

如何在macOS中编程实现指定区域屏幕截图(适配Unity插件)

macOS 指定区域桌面截图的实用实现方案(适配Unity原生插件)

针对你需要根据displayId、x、y、width、height返回截图字节数据的需求,以下是几种可行的简便实现方式:

1. 继续使用已弃用的CGWindowListCreateImage(快速兼容)

虽然官方标记为弃用,但该API目前在macOS 14及以下版本仍能正常运行,不会影响功能,适合快速实现需求。你可以封装一个函数,直接通过该API截取指定区域,再转换为Unity可用的字节数据:

#import <CoreGraphics/CoreGraphics.h>
#import <ImageIO/ImageIO.h>

NSData* captureDisplayArea(CGDirectDisplayID displayId, CGFloat x, CGFloat y, CGFloat width, CGFloat height) {
    // 获取显示器的像素缩放因子(适配Retina屏幕)
    CGFloat scale = CGDisplayScreenSize(displayId).width / CGDisplayBounds(displayId).size.width;
    CGRect captureRect = CGRectMake(x * scale, y * scale, width * scale, height * scale);
    
    // 创建截图
    CGImageRef image = CGWindowListCreateImage(
        captureRect,
        kCGWindowListOptionOnScreenOnly,
        kCGNullWindowID,
        kCGWindowImageDefault
    );
    
    if (!image) return nil;
    
    // 将CGImage转换为NSData(这里用PNG格式,也可换成RGBA raw数据适配Unity)
    NSMutableData* data = [NSMutableData data];
    CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)data, kUTTypePNG, 1, NULL);
    CGImageDestinationAddImage(dest, image, NULL);
    CGImageDestinationFinalize(dest);
    
    CFRelease(dest);
    CGImageRelease(image);
    
    return data;
}

如果需要直接返回Unity可用的RGBA字节流,可以跳过ImageIO转换,直接从CGImage中提取像素数据。

2. 基于CGDisplayCreateImage的裁剪方案

用CGDisplayCreateImage先捕获整个显示器画面,再裁剪指定区域,这种方式同样属于旧API,但逻辑更直接:

NSData* captureCroppedDisplayArea(CGDirectDisplayID displayId, CGFloat x, CGFloat y, CGFloat width, CGFloat height) {
    CGFloat scale = CGDisplayScreenSize(displayId).width / CGDisplayBounds(displayId).size.width;
    CGRect captureRect = CGRectMake(x * scale, y * scale, width * scale, height * scale);
    
    CGImageRef fullDisplayImage = CGDisplayCreateImage(displayId);
    if (!fullDisplayImage) return nil;
    
    // 裁剪指定区域
    CGImageRef croppedImage = CGImageCreateWithImageInRect(fullDisplayImage, captureRect);
    CGImageRelease(fullDisplayImage);
    
    if (!croppedImage) return nil;
    
    // 转换为字节数据(示例为PNG,可替换为raw像素)
    NSMutableData* data = [NSMutableData data];
    CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)data, kUTTypePNG, 1, NULL);
    CGImageDestinationAddImage(dest, croppedImage, NULL);
    CGImageDestinationFinalize(dest);
    
    CFRelease(dest);
    CGImageRelease(croppedImage);
    
    return data;
}

3. 适配ScreenCaptureKit的单次截图方案

如果不想依赖弃用API,可以利用ScreenCaptureKit实现单次截图:启动流并捕获第一帧后立即停止,模拟单次截图的效果:

#import <ScreenCaptureKit/ScreenCaptureKit.h>

typedef void(^CaptureCompletion)(NSData*);

void captureAreaWithScreenCaptureKit(CGDirectDisplayID displayId, CGFloat x, CGFloat y, CGFloat width, CGFloat height, CaptureCompletion completion) {
    // 获取目标显示器
    SCShareableContent* content = [SCShareableContent shareableContentWithError:nil];
    SCDisplay* targetDisplay = nil;
    for (SCDisplay* display in content.displays) {
        if (display.displayID == displayId) {
            targetDisplay = display;
            break;
        }
    }
    if (!targetDisplay) {
        completion(nil);
        return;
    }
    
    // 配置流参数,指定裁剪区域
    SCStreamConfiguration* config = [[SCStreamConfiguration alloc] init];
    CGRect captureRect = CGRectMake(x, y, width, height);
    config.cropRect = captureRect;
    config.pixelFormat = kCVPixelFormatType_32BGRA; // 适配Unity的RGBA格式
    
    // 创建流
    SCStream* stream = [[SCStream alloc] initWithDisplay:targetDisplay configuration:config delegate:nil];
    
    __block BOOL hasCaptured = NO;
    [stream addStreamOutput:^(SCStreamFrame * _Nonnull frame, CMSampleBufferRef _Nonnull sampleBuffer, NSError * _Nullable error) {
        if (hasCaptured) return;
        hasCaptured = YES;
        
        // 从SampleBuffer中提取像素数据
        CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
        CVPixelBufferLockBaseAddress(imageBuffer, 0);
        void* baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
        size_t bufferSize = CVPixelBufferGetDataSize(imageBuffer);
        
        NSData* pixelData = [NSData dataWithBytes:baseAddress length:bufferSize];
        CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
        
        // 停止流
        [stream stopCaptureWithCompletionHandler:^(NSError * _Nullable error) {
            completion(pixelData);
        }];
    }];
    
    // 启动捕获
    [stream startCaptureWithCompletionHandler:^(NSError * _Nullable error) {
        if (error) completion(nil);
    }];
}

这种方式需要申请屏幕录制权限,适合长期维护的插件。

内容的提问来源于stack exchange,提问作者Rahel Gunaratne

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 22:07:50