4

I'm trying to retrieve UIImage from SKTexture where SKTexture comes from an SKTextureAtlas. Which make is harder due to the fact .cgImage() isn't working on Atlases.

Anyway, I've came up with solution to use CoreGraphic, however, that seems not to work either. Maybe you are able to help me what did I do wrong in here?

func image(with view: SKView) -> UIImage {
    let format = UIGraphicsImageRendererFormat.default()
    format.scale = UIScreen.main.scale
    format.opaque = false
    let renderer = UIGraphicsImageRenderer(size: view.bounds.size, format: format)
    let image = renderer.image { ctx in
        view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
    }
    return image
}

func image(texture: SKTexture) -> UIImage {
    let view = SKView(frame:CGRect(x: 0, y: 0, width: texture.size().width, height: texture.size().height))
    let scene = SKScene(size: texture.size())
    let sprite  = SKSpriteNode(texture: texture)
    sprite.position = CGPoint(x: view.frame.midX, y: view.frame.midY)
    scene.addChild(sprite)
    view.presentScene(scene)
    return image(with: view)
}

let uiImage = image(texture: SKTexture(imageNamed: "HeavyCrossbow.png"))[!

[enter image description here]

This solution isn't working either SKTextureAtlas nor SKTextureAtlas Any idea what did I missed, how to make it happen?

Cheers, Szymon

Shial
  • 1,386
  • 19
  • 31
  • Why isnt cgimage working on atlases? – Knight0fDragon Mar 09 '20 at 03:14
  • @Knight0fDragon SKTexture from a SKTextureAtlas does not support mipmapping. https://stackoverflow.com/a/22066288/2308303 – Shial Mar 09 '20 at 03:17
  • That is a 6 year old answer that may not even apply now – Knight0fDragon Mar 09 '20 at 03:18
  • Besides, mip mapping and cgImage are two different things. I use cgImage to temp fix the skphysics bug introduced recently, so last I knew, it was working. – Knight0fDragon Mar 09 '20 at 03:20
  • 1
    As of 13.3.1 cgImage for textures in atlases is broken, at least on some devices. https://github.com/bg2b/bugtest Rendering to a new texture is the only work-around I've found that seems robust. – bg2b Mar 09 '20 at 11:43

1 Answers1

3

Ok, After weekend I finally got it working. Important part is

guard let render = view.texture(from: sprite) else { return UIImage() }
return UIImage(cgImage: render.cgImage())

which I got thanks to this answer

Full working code:

func image(texture: SKTexture) -> UIImage {
    let view = SKView(frame:CGRect(x: 0, y: 0, width: texture.size().width, height: texture.size().height))
    let scene = SKScene(size: texture.size())
    let sprite  = SKSpriteNode(texture: texture)
    sprite.position = CGPoint(x: view.frame.midX, y: view.frame.midY)
    scene.addChild(sprite)
    view.presentScene(scene)

    guard let render = view.texture(from: sprite) else { return UIImage() }
    return UIImage(cgImage: render.cgImage())
}

The reason I was looking to make it work is that I'm writing game with SpriteKit however UI is done with SwiftUI.

Shial
  • 1,386
  • 19
  • 31
  • If you are doing this, then why bother with an atlas in the first place...... you are going lose all the benefits that an atlas gives. – Knight0fDragon Mar 09 '20 at 03:23
  • @Knight0fDragon atlas is used for game, I just don't won't to create separate objects for UI. I'm working on level editor now where you can switch brushes etc from SwiftUI. This UI I based on SKScene content and SKTileGroups – Shial Mar 09 '20 at 03:53
  • @Knight0fDragon take a look here: https://github.com/shial4/SpriteKit-SwiftUI-Template I've created template for SpriteKit + SwiftUI. That's how I'm working with my project :) – Shial Mar 09 '20 at 03:55