From 604a09b3fb49c76cea777a4817744f338a5d8d25 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Tue, 10 Mar 2026 21:23:45 -0400 Subject: [PATCH] [libgdx] Fixed RGB2 and RGBA2 timeline flicker. Also the `alpha != 1 && blend == setup` was incorrect. related EsotericSoftware/spine-editor#931 --- .../com/esotericsoftware/spine/Animation.java | 307 +++++++++--------- 1 file changed, 153 insertions(+), 154 deletions(-) diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index cebd1e1ad..ab7a9b198 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -1072,21 +1072,18 @@ public class Animation { if (time < frames[0]) { Color setup = slot.data.setup.color; switch (blend) { - case setup -> { + case setup: color.r = setup.r; color.g = setup.g; color.b = setup.b; + // Fall through. + default: return; - } - case first -> { + case first: r = color.r + (setup.r - color.r) * alpha; g = color.g + (setup.g - color.g) * alpha; b = color.b + (setup.b - color.b) * alpha; } - default -> { - return; - } - } } else { int i = search(frames, time, ENTRIES), curveType = (int)curves[i >> 2]; switch (curveType) { @@ -1156,14 +1153,13 @@ public class Animation { if (time < frames[0]) { Color setup = slot.data.setup.color; switch (blend) { - case setup -> { + case setup: color.a = setup.a; + // Fall through. + default: return; - } - case first -> a = color.a + (setup.a - color.a) * alpha; - default -> { - return; - } + case first: + a = color.a + (setup.a - color.a) * alpha; } } else { a = getCurveValue(time); @@ -1211,89 +1207,91 @@ public class Animation { } protected void apply (Slot slot, SlotPose pose, float time, float alpha, MixBlend blend) { - float[] frames = this.frames; Color light = pose.color, dark = pose.darkColor; + float r2, g2, b2; + float[] frames = this.frames; if (time < frames[0]) { SlotPose setup = slot.data.setup; Color setupLight = setup.color, setupDark = setup.darkColor; switch (blend) { - case setup -> { + case setup: light.set(setupLight); dark.r = setupDark.r; dark.g = setupDark.g; dark.b = setupDark.b; - } - case first -> { + // Fall through. + default: + return; + case first: light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha, (setupLight.a - light.a) * alpha); - dark.r += (setupDark.r - dark.r) * alpha; - dark.g += (setupDark.g - dark.g) * alpha; - dark.b += (setupDark.b - dark.b) * alpha; + r2 = dark.r + (setupDark.r - dark.r) * alpha; + g2 = dark.g + (setupDark.g - dark.g) * alpha; + b2 = dark.b + (setupDark.b - dark.b) * alpha; } - } - return; - } - - float r, g, b, a, r2, g2, b2; - int i = search(frames, time, ENTRIES), curveType = (int)curves[i >> 3]; - switch (curveType) { - case LINEAR -> { - float before = frames[i]; - r = frames[i + R]; - g = frames[i + G]; - b = frames[i + B]; - a = frames[i + A]; - r2 = frames[i + R2]; - g2 = frames[i + G2]; - b2 = frames[i + B2]; - float t = (time - before) / (frames[i + ENTRIES] - before); - r += (frames[i + ENTRIES + R] - r) * t; - g += (frames[i + ENTRIES + G] - g) * t; - b += (frames[i + ENTRIES + B] - b) * t; - a += (frames[i + ENTRIES + A] - a) * t; - r2 += (frames[i + ENTRIES + R2] - r2) * t; - g2 += (frames[i + ENTRIES + G2] - g2) * t; - b2 += (frames[i + ENTRIES + B2] - b2) * t; - } - case STEPPED -> { - r = frames[i + R]; - g = frames[i + G]; - b = frames[i + B]; - a = frames[i + A]; - r2 = frames[i + R2]; - g2 = frames[i + G2]; - b2 = frames[i + B2]; - } - default -> { - r = getBezierValue(time, i, R, curveType - BEZIER); - g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER); - b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER); - a = getBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER); - r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 4 - BEZIER); - g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 5 - BEZIER); - b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 6 - BEZIER); - } - } - - if (alpha == 1) { - light.set(r, g, b, a); - dark.r = r2; - dark.g = g2; - dark.b = b2; } else { - if (blend == setup) { - SlotPose setup = slot.data.setup; - light.set(setup.color); - Color setupDark = setup.darkColor; - dark.r = setupDark.r; - dark.g = setupDark.g; - dark.b = setupDark.b; + float r, g, b, a; + int i = search(frames, time, ENTRIES), curveType = (int)curves[i >> 3]; + switch (curveType) { + case LINEAR -> { + float before = frames[i]; + r = frames[i + R]; + g = frames[i + G]; + b = frames[i + B]; + a = frames[i + A]; + r2 = frames[i + R2]; + g2 = frames[i + G2]; + b2 = frames[i + B2]; + float t = (time - before) / (frames[i + ENTRIES] - before); + r += (frames[i + ENTRIES + R] - r) * t; + g += (frames[i + ENTRIES + G] - g) * t; + b += (frames[i + ENTRIES + B] - b) * t; + a += (frames[i + ENTRIES + A] - a) * t; + r2 += (frames[i + ENTRIES + R2] - r2) * t; + g2 += (frames[i + ENTRIES + G2] - g2) * t; + b2 += (frames[i + ENTRIES + B2] - b2) * t; + } + case STEPPED -> { + r = frames[i + R]; + g = frames[i + G]; + b = frames[i + B]; + a = frames[i + A]; + r2 = frames[i + R2]; + g2 = frames[i + G2]; + b2 = frames[i + B2]; + } + default -> { + r = getBezierValue(time, i, R, curveType - BEZIER); + g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER); + b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER); + a = getBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER); + r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 4 - BEZIER); + g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 5 - BEZIER); + b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 6 - BEZIER); + } + } + + if (alpha == 1) + light.set(r, g, b, a); + else if (blend == setup) { + SlotPose setupPose = slot.data.setup; + Color setup = setupPose.color; + light.set(setup.r + (r - setup.r) * alpha, setup.g + (g - setup.g) * alpha, setup.b + (b - setup.b) * alpha, + setup.a + (a - setup.a) * alpha); + setup = setupPose.darkColor; + r2 = setup.r + (r2 - setup.r) * alpha; + g2 = setup.g + (g2 - setup.g) * alpha; + b2 = setup.b + (b2 - setup.b) * alpha; + } else { + light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha); + r2 = dark.r + (r2 - dark.r) * alpha; + g2 = dark.g + (g2 - dark.g) * alpha; + b2 = dark.b + (b2 - dark.b) * alpha; } - light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha); - dark.r += (r2 - dark.r) * alpha; - dark.g += (g2 - dark.g) * alpha; - dark.b += (b2 - dark.b) * alpha; } + dark.r = r2 < 0 ? 0 : (r2 > 1 ? 1 : r2); + dark.g = g2 < 0 ? 0 : (g2 > 1 ? 1 : g2); + dark.b = b2 < 0 ? 0 : (b2 > 1 ? 1 : b2); } } @@ -1327,94 +1325,95 @@ public class Animation { } protected void apply (Slot slot, SlotPose pose, float time, float alpha, MixBlend blend) { - float[] frames = this.frames; Color light = pose.color, dark = pose.darkColor; + float r, g, b, r2, g2, b2; + float[] frames = this.frames; if (time < frames[0]) { SlotPose setup = slot.data.setup; Color setupLight = setup.color, setupDark = setup.darkColor; switch (blend) { - case setup -> { + case setup: light.r = setupLight.r; light.g = setupLight.g; light.b = setupLight.b; dark.r = setupDark.r; dark.g = setupDark.g; dark.b = setupDark.b; + // Fall through. + default: + return; + case first: + r = light.r + (setupLight.r - light.r) * alpha; + g = light.g + (setupLight.g - light.g) * alpha; + b = light.b + (setupLight.b - light.b) * alpha; + r2 = dark.r + (setupDark.r - dark.r) * alpha; + g2 = dark.g + (setupDark.g - dark.g) * alpha; + b2 = dark.b + (setupDark.b - dark.b) * alpha; } - case first -> { - light.r += (setupLight.r - light.r) * alpha; - light.g += (setupLight.g - light.g) * alpha; - light.b += (setupLight.b - light.b) * alpha; - dark.r += (setupDark.r - dark.r) * alpha; - dark.g += (setupDark.g - dark.g) * alpha; - dark.b += (setupDark.b - dark.b) * alpha; - } - } - return; - } - - float r, g, b, r2, g2, b2; - int i = search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES]; - switch (curveType) { - case LINEAR -> { - float before = frames[i]; - r = frames[i + R]; - g = frames[i + G]; - b = frames[i + B]; - r2 = frames[i + R2]; - g2 = frames[i + G2]; - b2 = frames[i + B2]; - float t = (time - before) / (frames[i + ENTRIES] - before); - r += (frames[i + ENTRIES + R] - r) * t; - g += (frames[i + ENTRIES + G] - g) * t; - b += (frames[i + ENTRIES + B] - b) * t; - r2 += (frames[i + ENTRIES + R2] - r2) * t; - g2 += (frames[i + ENTRIES + G2] - g2) * t; - b2 += (frames[i + ENTRIES + B2] - b2) * t; - } - case STEPPED -> { - r = frames[i + R]; - g = frames[i + G]; - b = frames[i + B]; - r2 = frames[i + R2]; - g2 = frames[i + G2]; - b2 = frames[i + B2]; - } - default -> { - r = getBezierValue(time, i, R, curveType - BEZIER); - g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER); - b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER); - r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 3 - BEZIER); - g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 4 - BEZIER); - b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 5 - BEZIER); - } - } - - if (alpha == 1) { - light.r = r; - light.g = g; - light.b = b; - dark.r = r2; - dark.g = g2; - dark.b = b2; } else { - if (blend == setup) { - SlotPose setup = slot.data.setup; - Color setupLight = setup.color, setupDark = setup.darkColor; - light.r = setupLight.r; - light.g = setupLight.g; - light.b = setupLight.b; - dark.r = setupDark.r; - dark.g = setupDark.g; - dark.b = setupDark.b; + int i = search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES]; + switch (curveType) { + case LINEAR -> { + float before = frames[i]; + r = frames[i + R]; + g = frames[i + G]; + b = frames[i + B]; + r2 = frames[i + R2]; + g2 = frames[i + G2]; + b2 = frames[i + B2]; + float t = (time - before) / (frames[i + ENTRIES] - before); + r += (frames[i + ENTRIES + R] - r) * t; + g += (frames[i + ENTRIES + G] - g) * t; + b += (frames[i + ENTRIES + B] - b) * t; + r2 += (frames[i + ENTRIES + R2] - r2) * t; + g2 += (frames[i + ENTRIES + G2] - g2) * t; + b2 += (frames[i + ENTRIES + B2] - b2) * t; + } + case STEPPED -> { + r = frames[i + R]; + g = frames[i + G]; + b = frames[i + B]; + r2 = frames[i + R2]; + g2 = frames[i + G2]; + b2 = frames[i + B2]; + } + default -> { + r = getBezierValue(time, i, R, curveType - BEZIER); + g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER); + b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER); + r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 3 - BEZIER); + g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 4 - BEZIER); + b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 5 - BEZIER); + } + } + + if (alpha != 1) { + if (blend == setup) { + SlotPose setupPose = slot.data.setup; + Color setup = setupPose.color; + r = setup.r + (r - setup.r) * alpha; + g = setup.g + (g - setup.g) * alpha; + b = setup.b + (b - setup.b) * alpha; + setup = setupPose.darkColor; + r2 = setup.r + (r2 - setup.r) * alpha; + g2 = setup.g + (g2 - setup.g) * alpha; + b2 = setup.b + (b2 - setup.b) * alpha; + } else { + r = light.r + (r - light.r) * alpha; + g = light.g + (g - light.g) * alpha; + b = light.b + (b - light.b) * alpha; + r2 = dark.r + (r2 - dark.r) * alpha; + g2 = dark.g + (g2 - dark.g) * alpha; + b2 = dark.b + (b2 - dark.b) * alpha; + } } - light.r += (r - light.r) * alpha; - light.g += (g - light.g) * alpha; - light.b += (b - light.b) * alpha; - dark.r += (r2 - dark.r) * alpha; - dark.g += (g2 - dark.g) * alpha; - dark.b += (b2 - dark.b) * alpha; } + light.r = r < 0 ? 0 : (r > 1 ? 1 : r); + light.g = g < 0 ? 0 : (g > 1 ? 1 : g); + light.b = b < 0 ? 0 : (b > 1 ? 1 : b); + dark.r = r2 < 0 ? 0 : (r2 > 1 ? 1 : r2); + dark.g = g2 < 0 ? 0 : (g2 > 1 ? 1 : g2); + dark.b = b2 < 0 ? 0 : (b2 > 1 ? 1 : b2); } }