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
.