I am using a SKMutableTexture to render destructable ground in a game. For all intents and purposes, this works great, except for when I want to render a transparent pixel into a position in the MutableTexture where there is already a non-transparent pixel.
I have created a struct for RGBAColor like so:
struct RGBAColor {
var r: UInt8
var g: UInt8
var b: UInt8
var a: UInt8
static var clear = RGBAColor(r: 0, g: 0, b: 0, a: 0)
}
I then modify the SKMutableTexture like so:
self.texture.modifyPixelData({ (voidptr, len) in
let bytes = voidptr!.assumingMemoryBound(to: RGBAColor.self)
for (index, color) in self.modifiedPixels {
bytes[index] = color
self.pixels[index] = color
}
})
.. where "modifiedPixels" is a dictionary like [Int : RGBAColor] (in order not to update all pixels within the texture - only the modified ones.
It works as I intended when the color has an alpha (a) > 0 .. but not when it is 0 (it is like the pixel is rendered on top of the existing texture .. and not like you are modifying the actual pixel data).
Am I doing something wrong? Or is this the intended behaviour?
From the SKMutableTexture
documentation,
The color components you provide should have already been multiplied by the alpha value.
Normally, if you wanted a color that is red with an alpha of 0.5, you would use RGBA components
(red:1.0, green:0.0, blue:0.0, alpha:0.5)
The equivalent for a premultiplied-alpha color is
(red:0.5, green:0.0, blue:0.0, alpha:0.5)
where the RGB components have been multiplied by the alpha value.
For your specific case, you can add an initializer to the Color
structure to perform the multiplication. Here's one way implement that...
First, define an extension to scale a UInt8
by a Float
value by
extension UInt8 {
func scaled(by value:Float) -> UInt8 {
var scale = UInt(round(Float(self) * value))
scale = Swift.min(scale, UInt(UInt8.max))
return UInt8(scale)
}
}
and then add an initializer that automatically multiples the components by the alpha value
struct Color {
var red:UInt8
var green:UInt8
var blue:UInt8
var alpha:UInt8
init(red:UInt8,green:UInt8,blue:UInt8,alpha:UInt8) {
let alphaScale = Float(alpha) / Float(UInt8.max)
self.red = red.scaled(by: alphaScale)
self.blue = blue.scaled(by: alphaScale)
self.green = green.scaled(by: alphaScale)
self.alpha = alpha
}
}
You can then create a color that is red with alpha = 0.5 by
let color = Color(red:255, green:0, blue:0, alpha:127)
and a completely transparent color with
let color = Color(red:255, green:0, blue:0, alpha:0)