SKTexture.cgImage() smaller than SKTexture

Since upgrading my iPad mini to iOS 12.2 and xcode to 10.2, I've had the following problem...


SKTexture.cgImage() returns an image the size of the visible image rather than the original image that the SKTexture was created with. I need to get a CGImage the size of the texture, and not just the visible pixels.


A very simple example... I have a 32x32 png image that the bottom 16 pixels are clear (alpha of 0) and all of the top 16 rows are a solid color (it's a rectangle filling the top 16 rows). I have imported it into an .xcassets file in xcode. I have a TileSet (.sks file) where I've included the png as a texture of a tile definition. If I look at the properties of the png in xcode, it says it is 32x32px. If I look at the properties of the tile definition it says it is 32x32px. Everything good so far.


I need to examine the pixels of the CGImage of the resulting texture during runtime. I do this by loading the SKTexture from the SKTileSet made in xcode designers, and doing SKTexture.cgImage(). At the time this happens, the SKTexture object reports a size of 32x32 as expected, but the CGImage that comes back is 32x16. It would appear that something has decided that only the top 16 pixels are actually being used (i.e. visible) and therefore the CGImage should be 32x16, and not 32x32 like the SKTexture, or even the original png used to create the texture.


This is a problem for two reasons...


1. I want the image that starts off as 32x32 to always be 32x32, for consistency. I'm showing these textures in an SKTileMapNode and the ones which it thinks are 32x16 get stretched undesirably in the tile map node cells. e.g. my 32x16 rectangle gets stretched double across the height to fill the 32x32 squares in the tile map node.


2. I can't even find out where amongst the 32x32 texture the 32x16 area should be showing. It would be nice if something could at least tell me "yeah it's coming back as 32x16 but that's because we're offsetting it by 0,0".


So the question is either...


A. How do I get the CGImage from the SKTexture without it automatically cropping it? i.e. a 32x32 SKTexture always returning a 32x32 CGImage.


OR


B. How do I stop it automatically cropping images when applied to SKTextures?


Many thanks for any pointers or help. This is driving me crazy as it has broken my entire game in many ways!

Replies

It would seem that updating my devices to iOS 12.2 broke this. SKTexture.cgImage() used to return an image the same size as the texture, but now it only returns the visible size of the image. So infuriating!!!


Can anybody please tell me how I now get the full size image?

I've come across this issue too and after investigation identified the following:

1. The issue only occurs for images stored in a Sprite Atlas/TextureAtlas.

2. The issue only occurs when running on Metal hardware. It doesn't occur when running on the Simulator or older hardware that doesn't support Metal (actually not sure about the older hardware, as my older hardware is on iOS < 12.2).

3. The issue only occurs when creating an SKTexture using the filename initialiser SKTexture(imageNamed: String) or SKTextureAtlas.textureNamed(_ name: String). So depending on the context you may be able to work around the problem by using the SKTexture(image: UIImage) initialiser instead, something like:

if let textureImage = UIImage(named: "textureName") {
     let texture = SKTexture(image: textureImage)
     // Do something with the texture
}

Except that this uses a lot of memory and isn't a solution for me.


This is a pretty big problem if you're creating an SKPhysicsBody using a texture (which is what led me here). The physics body is being created using the texture that's been cropped in size to visible pixels, but texture.size() gives you the uncropped dimensions, so if you pass that size to the physics body initialiser then the body gets scaled up to fit. This creates a physics body that doesn't match the texture used to generate it.

Thanks for the reply, I was beginning to think I was alone lol


I've used something similar to your suggestion as a workaround. The annoying part of my particular usage problem is that I am using the TileSet sks editor in Xcode to create a long list of SKTileGroup SKTileGroupRule / SKTileDefinition / SKTextures, which are then being used to design my game levels upon a SKTileMapNode, again all in Xcode. So the SKTexture objects are all created for me. I have no choice as to how the SKTexture objects get created, unless I actually construct my game levels using code and none of WYSIWYG features that are available. If I had to do that, tbh, I'd abandon the whole project.


Even getting the image name from the SKTexture was problematic, but I ended up using this hacky way for now...


public extension SKTexture
{
  func GetImageName() -> String
  {
       return description.components(separatedBy: "'")[1];
  }
}


Then for each SKTileDefinition in my TileSet I had to do this...


  var TextureList : [SKTexture] = [SKTexture]();
  TextureList.append(contentsOf: TileDef.textures);

  TileDef.textures.removeAll();

  for Texture : SKTexture in TextureList
  {
    let Image : CGImage = UIImage(named: Texture.GetImageName())!.cgImage!;

    let NewTexture : SKTexture = SKTexture(cgImage: Image);

    TileDef.textures.append(NewTexture);
  }


...which basically removes all the textures from the SKTileDefinition called TileDef, and recreates them by loading a UIImage using the image name retrieved by my GetImageName() extension above. Feels really hacky and wrong because I know the images have already been loaded for me, but can't figure out any alternatives.


I've also tried updating my device (an iPad mini) to iOS 12.3 beta in case it's just an iOS 12.2 thing, but unfortunately the normal way of doing this is still broken in that.


I do hope they fix it soon, because I've little confidence in my hacky solution always working as it depends on parsing thet description.

Yeah, no satisfactory workaround. Watch your peak memory usage when using UIImage to initialise a lot of textures too, it seems to circumvent the benefits of using a texture atlas. Same issue is described in this Open Radar from 2015 https://openradar.appspot.com/22496050

Maybe a change in the Metal conversion of Spritekit has reverted a problem?

Better submit a bug report.

texture.cgImage() isn't working properly in iOS 13.4. It returns a corrupted image.