spine-xna no longer premultiplies alpha on load. Do it when packing the atlas.

This is just simpler all around. The renderer has a setting to know if premultiplied is being used. #121
This commit is contained in:
NathanSweet 2013-09-25 10:10:11 +02:00
parent 1f3dceed40
commit 6f2cc72aaf
5 changed files with 109 additions and 182 deletions

View File

@ -1,166 +1,131 @@
spineboy.png spineboy.png
format: RGBA8888 format: RGBA8888
filter: Linear,Linear filter: Nearest,Nearest
repeat: none repeat: none
head eyes
rotate: false rotate: false
xy: 1, 122 xy: 447, 44
size: 34, 27
orig: 34, 27
offset: 0, 0
index: -1
eyes-closed
rotate: true
xy: 483, 37
size: 34, 27
orig: 34, 27
offset: 0, 0
index: -1
head
rotate: true
xy: 2, 5
size: 121, 132 size: 121, 132
orig: 121, 132 orig: 121, 132
offset: 0, 0 offset: 0, 0
index: -1 index: -1
torso
rotate: false
xy: 1, 28
size: 68, 92
orig: 68, 92
offset: 0, 0
index: -1
left-pant-bottom
rotate: false
xy: 1, 4
size: 44, 22
orig: 44, 22
offset: 0, 0
index: -1
right-pant-bottom
rotate: false
xy: 47, 8
size: 46, 18
orig: 46, 18
offset: 0, 0
index: -1
right-upper-leg
rotate: false
xy: 71, 50
size: 44, 70
orig: 44, 70
offset: 0, 0
index: -1
pelvis
rotate: false
xy: 95, 1
size: 63, 47
orig: 63, 47
offset: 0, 0
index: -1
left-upper-leg
rotate: false
xy: 117, 53
size: 33, 67
orig: 33, 67
offset: 0, 0
index: -1
right-foot
rotate: false
xy: 160, 224
size: 67, 30
orig: 67, 30
offset: 0, 0
index: -1
left-shoulder
rotate: false
xy: 124, 201
size: 34, 53
orig: 34, 53
offset: 0, 0
index: -1
left-ankle
rotate: false
xy: 229, 222
size: 25, 32
orig: 25, 32
offset: 0, 0
index: -1
left-foot
rotate: false
xy: 160, 192
size: 65, 30
orig: 65, 30
offset: 0, 0
index: -1
neck
rotate: false
xy: 124, 171
size: 34, 28
orig: 34, 28
offset: 0, 0
index: -1
right-arm
rotate: false
xy: 124, 124
size: 21, 45
orig: 21, 45
offset: 0, 0
index: -1
right-ankle
rotate: false
xy: 227, 190
size: 25, 30
orig: 25, 30
offset: 0, 0
index: -1
left-hand
rotate: false
xy: 147, 131
size: 35, 38
orig: 35, 38
offset: 0, 0
index: -1
left-arm left-arm
rotate: false rotate: true
xy: 184, 161 xy: 382, 40
size: 35, 29 size: 35, 29
orig: 35, 29 orig: 35, 29
offset: 0, 0 offset: 0, 0
index: -1 index: -1
eyes-closed left-foot
rotate: false rotate: false
xy: 221, 161 xy: 205, 2
size: 34, 27 size: 65, 30
orig: 34, 27 orig: 65, 30
offset: 0, 0 offset: 0, 0
index: -1 index: -1
right-lower-leg left-hand
rotate: false rotate: false
xy: 152, 65 xy: 272, 7
size: 51, 64 size: 35, 38
orig: 51, 64 orig: 35, 38
offset: 0, 0
index: -1
right-foot-idle
rotate: false
xy: 184, 131
size: 53, 28
orig: 53, 28
offset: 0, 0 offset: 0, 0
index: -1 index: -1
left-lower-leg left-lower-leg
rotate: false rotate: true
xy: 205, 65 xy: 331, 77
size: 49, 64 size: 49, 64
orig: 49, 64 orig: 49, 64
offset: 0, 0 offset: 0, 0
index: -1 index: -1
right-shoulder left-shoulder
rotate: false rotate: false
xy: 160, 12 xy: 462, 73
size: 52, 51 size: 34, 53
orig: 52, 51 orig: 34, 53
offset: 0, 0 offset: 0, 0
index: -1 index: -1
eyes left-upper-leg
rotate: true
xy: 259, 47
size: 33, 67
orig: 33, 67
offset: 0, 0
index: -1
neck
rotate: false rotate: false
xy: 214, 36 xy: 382, 10
size: 34, 27 size: 34, 28
orig: 34, 27 orig: 34, 28
offset: 0, 0
index: -1
pelvis
rotate: false
xy: 397, 79
size: 63, 47
orig: 63, 47
offset: 0, 0
index: -1
right-arm
rotate: true
xy: 206, 39
size: 21, 45
orig: 21, 45
offset: 0, 0
index: -1
right-foot
rotate: false
xy: 136, 2
size: 67, 30
orig: 67, 30
offset: 0, 0 offset: 0, 0
index: -1 index: -1
right-hand right-hand
rotate: false rotate: false
xy: 214, 2 xy: 413, 45
size: 32, 32 size: 32, 32
orig: 32, 32 orig: 32, 32
offset: 0, 0 offset: 0, 0
index: -1 index: -1
right-lower-leg
rotate: false
xy: 206, 62
size: 51, 64
orig: 51, 64
offset: 0, 0
index: -1
right-shoulder
rotate: false
xy: 328, 24
size: 52, 51
orig: 52, 51
offset: 0, 0
index: -1
right-upper-leg
rotate: true
xy: 259, 82
size: 44, 70
orig: 44, 70
offset: 0, 0
index: -1
torso
rotate: false
xy: 136, 34
size: 68, 92
orig: 68, 92
offset: 0, 0
index: -1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -69,6 +69,7 @@ namespace Spine {
protected override void LoadContent () { protected override void LoadContent () {
skeletonRenderer = new SkeletonRenderer(GraphicsDevice); skeletonRenderer = new SkeletonRenderer(GraphicsDevice);
skeletonRenderer.PremultipliedAlpha = true;
String name = "spineboy"; // "goblins"; String name = "spineboy"; // "goblins";

View File

@ -43,6 +43,10 @@ namespace Spine {
BasicEffect effect; BasicEffect effect;
RasterizerState rasterizerState; RasterizerState rasterizerState;
float[] vertices = new float[8]; float[] vertices = new float[8];
BlendState defaultBlendState;
private bool premultipliedAlpha;
public bool PremultipliedAlpha { get { return premultipliedAlpha; } set { premultipliedAlpha = value; } }
public SkeletonRenderer (GraphicsDevice device) { public SkeletonRenderer (GraphicsDevice device) {
this.device = device; this.device = device;
@ -62,8 +66,10 @@ namespace Spine {
} }
public void Begin () { public void Begin () {
defaultBlendState = premultipliedAlpha ? BlendState.AlphaBlend : BlendState.NonPremultiplied;
device.RasterizerState = rasterizerState; device.RasterizerState = rasterizerState;
device.BlendState = BlendState.AlphaBlend; // spine-xna textures are premultiplied on load, see Util.cs. device.BlendState = defaultBlendState;
effect.Projection = Matrix.CreateOrthographicOffCenter(0, device.Viewport.Width, device.Viewport.Height, 0, 1, 0); effect.Projection = Matrix.CreateOrthographicOffCenter(0, device.Viewport.Width, device.Viewport.Height, 0, 1, 0);
} }
@ -83,7 +89,7 @@ namespace Spine {
Slot slot = drawOrder[i]; Slot slot = drawOrder[i];
RegionAttachment regionAttachment = slot.Attachment as RegionAttachment; RegionAttachment regionAttachment = slot.Attachment as RegionAttachment;
if (regionAttachment != null) { if (regionAttachment != null) {
BlendState blend = slot.Data.AdditiveBlending ? BlendState.Additive : BlendState.AlphaBlend; BlendState blend = slot.Data.AdditiveBlending ? BlendState.Additive : defaultBlendState;
if (device.BlendState != blend) { if (device.BlendState != blend) {
End(); End();
device.BlendState = blend; device.BlendState = blend;
@ -93,8 +99,12 @@ namespace Spine {
AtlasRegion region = (AtlasRegion)regionAttachment.RendererObject; AtlasRegion region = (AtlasRegion)regionAttachment.RendererObject;
item.Texture = (Texture2D)region.page.rendererObject; item.Texture = (Texture2D)region.page.rendererObject;
Color color;
float a = skeletonA * slot.A; float a = skeletonA * slot.A;
Color color = new Color(skeletonR * slot.R * a, skeletonG * slot.G * a, skeletonB * slot.B * a, a); if (premultipliedAlpha)
color = new Color(skeletonR * slot.R * a, skeletonG * slot.G * a, skeletonB * slot.B * a, a);
else
color = new Color(skeletonR * slot.R, skeletonG * slot.G, skeletonB * slot.B, a);
item.vertexTL.Color = color; item.vertexTL.Color = color;
item.vertexBL.Color = color; item.vertexBL.Color = color;
item.vertexBR.Color = color; item.vertexBR.Color = color;

View File

@ -71,56 +71,7 @@ namespace Spine {
#endif #endif
static public Texture2D LoadTexture (GraphicsDevice device, Stream input) { static public Texture2D LoadTexture (GraphicsDevice device, Stream input) {
Texture2D file = Texture2D.FromStream(device, input); return Texture2D.FromStream(device, input);
// Setup a render target to hold our final texture which will have premulitplied alpha values
RenderTarget2D result = new RenderTarget2D(device, file.Width, file.Height);
device.SetRenderTarget(result);
device.Clear(Color.Black);
// Multiply each color by the source alpha, and write in just the color values into the final texture
BlendState blendColor = new BlendState();
blendColor.ColorWriteChannels = ColorWriteChannels.Red | ColorWriteChannels.Green | ColorWriteChannels.Blue;
blendColor.AlphaDestinationBlend = Blend.Zero;
blendColor.ColorDestinationBlend = Blend.Zero;
blendColor.AlphaSourceBlend = Blend.SourceAlpha;
blendColor.ColorSourceBlend = Blend.SourceAlpha;
SpriteBatch spriteBatch = new SpriteBatch(device);
spriteBatch.Begin(SpriteSortMode.Immediate, blendColor);
spriteBatch.Draw(file, file.Bounds, Color.White);
spriteBatch.End();
// Now copy over the alpha values from the PNG source texture to the final one, without multiplying them
BlendState blendAlpha = new BlendState();
blendAlpha.ColorWriteChannels = ColorWriteChannels.Alpha;
blendAlpha.AlphaDestinationBlend = Blend.Zero;
blendAlpha.ColorDestinationBlend = Blend.Zero;
blendAlpha.AlphaSourceBlend = Blend.One;
blendAlpha.ColorSourceBlend = Blend.One;
spriteBatch.Begin(SpriteSortMode.Immediate, blendAlpha);
spriteBatch.Draw(file, file.Bounds, Color.White);
spriteBatch.End();
// Release the GPU back to drawing to the screen.
device.SetRenderTarget(null);
spriteBatch.Dispose();
file.Dispose();
#if IOS
return result as Texture2D;
#else
// RenderTarget2D are volatile and will be lost on screen resolution changes.
// So instead of using this directly, we create a non-voliate Texture2D.
// This is computationally slower, but should be safe as long as it is done on load.
Texture2D resultTexture = new Texture2D(device, file.Width, file.Height);
Color[] resultContent = new Color[Convert.ToInt32(file.Width * file.Height)];
result.GetData(resultContent);
resultTexture.SetData(resultContent);
result.Dispose(); // Dispose of the RenderTarget2D immediately.
return resultTexture;
#endif
} }
} }
} }