Render/Snapshot Сцена SpriteKit для NSImage

Кто-нибудь знает, как "сделать снимок" полным SKView или SKScene в NSImage?

Мы смогли использовать API textureFromNode для создания SKTexture из node и всех его дочерних элементов. Но пока мы не можем найти способ извлечь данные изображения, скажем, NSImage. Мы создаем смешанное приложение Cocoa/SpriteKit, в котором нам нужно извлечь маленькие миниатюры сцен.

Опять же, это похоже на iOS (для получения UIImage) с помощью drawViewHierarchyInRect:

UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, scale);
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Но как это сделать на Cocoa с NSImage? Справка.

Ответы

Ответ 1

Пока нет прямого доступа к данным изображения из SKTexture. Вы можете представить node на неэкранированной сцене и захватить иерархию представлений. Я использую этот код в iOS для справки:

class func imageFromNode(node : SKNode, inScene scene: SKScene) -> UIImage? {
    if let texture = scene.view?.textureFromNode(node) {
        let view = SKView(frame: CGRect(origin: CGPointZero, size: texture.size()))
        view.allowsTransparency = true
        view.backgroundColor = SKColor.clearColor()

        let scene = SKScene(size: view.bounds.size)
        scene.view?.allowsTransparency = true
        scene.backgroundColor = SKColor.clearColor()

        let sprite  = SKSpriteNode(texture: texture)
        sprite.position = CGPoint(x: CGRectGetMidX(view.frame), y: CGRectGetMidY(view.frame))
        scene.addChild(sprite)
        view.presentScene(scene)
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, 0.0)
        view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
    return nil
}

Ответ 2

Как и OS X 10.11 (и iOS 9 и tvOS, но мы будем игнорировать их, так как вы спрашиваете о NSImage), SKTexture может экспортировать свое содержимое в CGImageRef. Таким образом, подход textureFromNode, который вы упоминаете, теперь является первым шагом в рабочем решении:

SKTexture *texture = [view textureFromNode: view.scene];
NSImage *image = [[NSImage alloc] initWithCGImage: texture.CGImage
                                             size: view.bounds];

Обратите внимание на параметр size на initWithCGImage:, вы можете передать CGSizeZero там, чтобы NSImage наследовал размер пикселя CGImage, но поскольку вы можете иметь дело с Retina Display, вы, вероятно, захотите размер точки, который вы можете получить из размеров представления.

Ответ 3

Вот моя попытка (по OSX), чтобы выкопать лежащее в основе изображение...

Сначала я подклассифицировал SKView и в своем методе drawRect лениво схватил a NSBitmapImageRep

if (![self cache])
    [self setCache:[self bitmapImageRepForCachingDisplayInRect:[self visibleRect]]];

Затем я настроил NSTimer, который периодически пытался скопировать содержимое этого кеша в пользовательское представление эскизов:

MySKView *sv = .....
NSBitmapImageRep *cache = [sv cache];
if (!cache)
    return;

NSRect srcFrame = NSMakeRect(0,0,[sv frame].size.width,[sv frame].size.height);
NSRect thumbFrame = [[self thumbView] frame];
NSRect thumbRect = NSMakeRect(0,0,thumbFrame.size.width,thumbFrame.size.height);
[sv cacheDisplayInRect:srcFrame toBitmapImageRep:cache];
[[self thumbView] lockFocus];
[cache drawInRect:thumbRect
         fromRect:srcFrame
         operation:NSCompositeCopy
         fraction:1.0
   respectFlipped:YES
            hints:nil];
[[self thumbView] unlockFocus];

Нет кубиков. Все, что я получаю в моем представлении thumbnail, является черным.

Вероятно, по той же причине, что это все чертежи, основанные на OpenGL/GPU.

Это привело бы меня к тому, чтобы каким-то образом найти базовый контекст рисования OpenGL, используемый SKView, и сделать glReadPixels в этом контексте.

Ответ 4

SKView является подклассом UIView, поэтому вы можете рисовать слой вида в контексте CoreGraphics, а затем получать изображение из контекста.

Посмотрите на использование view.layer.drawInContext.