Есть ли способ записать вывод металла как растровое изображение или другой формат, подходящий для печати? - PullRequest
1 голос
/ 28 марта 2020

Я конвертирую приложение из OpenGL в Metal, используя MTKit. Оригинальное приложение позволяло пользователю распечатывать сгенерированный OpenGL экран, который я просматривал (я также делал это с Core Graphics). Я хотел бы сделать то же самое в металле. Я думал, что будет легко найти решение или хотя бы подход к проблеме, но я застрял. Кто-нибудь решил это, или у кого-нибудь есть какие-либо предположения о том, что я должен искать?

Я опытный программист Ma c (предпочел бы решение Objective- C, но я немного работы со всем остальным) и я опытный новичок в Metal.

  • MacOS 10.15.3, все MacBook.
  • Swift 5.
  • Xcode 11.2.1 .

1 Ответ

1 голос
/ 28 марта 2020

Я предполагаю, что вы хотите сохранить изображение из MTKView . Но эта функция должна работать для любых текстур. Также не забудьте установить: framebufferOnly = false; Свойство .

bool takeScreenshot = true;
/// Called whenever the view needs to render a frame
- (void)drawInMTKView:(nonnull MTKView *)view
{
    // Create a new command buffer for each render pass to the current drawable
    id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
    commandBuffer.label = @"MyCommand";

    // Obtain a renderPassDescriptor generated from the view's drawable textures
    MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
    id<MTLTexture> currentSwapChainTexture = view.currentDrawable.texture;



    // Your render code...



    [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb)
     {
        if(takeScreenshot)
        {
            SaveTexture(currentSwapChainTexture);
            takeScreenshot = false;
        }

    }];

    // Finalize rendering here & push the command buffer to the GPU
    [commandBuffer commit];
    // CPU <-> GPU Synchronization
    if(takeScreenshot)[commandBuffer waitUntilCompleted];

Сохранить текстуру Функция:

void SaveTexture(id<MTLTexture> texture)
{
    int width = (int) texture.width;
    int height = (int) texture.height;
    int bytePerPixel = 4;
    int bytesPerRow = width * bytePerPixel;
    int bytesCount = width * height * bytePerPixel;
    int bitsPerComponent = 8;
    int bitsPerPixel = 32;

    void *imageBytes = malloc(bytesCount);
    void *destBytes = malloc(bytesCount);

    MTLRegion mtlregion = MTLRegionMake2D(0, 0, width, height);
    [texture getBytes:imageBytes bytesPerRow:bytesPerRow fromRegion:mtlregion mipmapLevel:0];

    vImage_Buffer src;
    src.data = imageBytes;
    src.width = width;
    src.height = height;
    src.rowBytes = bytesPerRow;


    vImage_Buffer dest;
    dest.data = destBytes;
    dest.width = width;
    dest.height = height;
    dest.rowBytes = bytesPerRow;

    // BGRA -> RGBA (Swap)
    const uint8_t map[4] = {2, 1, 0, 3};
    vImagePermuteChannels_ARGB8888(&src, &dest, map, kvImageNoFlags);


    CGColorSpaceRef cgColorSpaceRef = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3); //kCGColorSpaceSRGB - For sRGB
    CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
    CGContextRef context = CGBitmapContextCreate(destBytes, width, height, bitsPerComponent, bytesPerRow, cgColorSpaceRef, bitmapInfo);
    CGImageRef cgImage = CGBitmapContextCreateImage(context);

    // Your NSImage
    NSImage * image = [[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize];

    // Save to Photos
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^
     {
        [PHAssetCreationRequest creationRequestForAssetFromImage: image];
    }

                                      completionHandler:^(BOOL success, NSError * error)
     {
        if(success) printf("Success \n");
    }];



    free(imageBytes);
    free(destBytes);
    CGColorSpaceRelease(cgColorSpaceRef);
    CGContextRelease(context);
    CGImageRelease(cgImage);
    texture = nil;
}
...