Explorar o código

[c] 4.0 porting, more timeline work.

badlogic %!s(int64=4) %!d(string=hai) anos
pai
achega
44b390cc0f

+ 46 - 31
spine-c/spine-c/include/spine/Animation.h

@@ -34,6 +34,7 @@
 #include <spine/Event.h>
 #include <spine/Attachment.h>
 #include <spine/Array.h>
+#include <stdint.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -41,7 +42,7 @@ extern "C" {
 
 typedef struct spTimeline spTimeline;
 struct spSkeleton;
-typedef unsigned long long spPropertyId;
+typedef uint64_t spPropertyId;
 
 _SP_ARRAY_DECLARE_TYPE(spPropertyIdArray, spPropertyId)
 _SP_ARRAY_DECLARE_TYPE(spTimelineArray, spTimeline*)
@@ -100,10 +101,10 @@ typedef enum {
     SP_PROPERTY_PATHCONSTRAINT_MIX = 1 << 18
 } spProperty;
 
-#define SP_MAX_PROPERTY_IDS 2
+#define SP_MAX_PROPERTY_IDS 3
 
 typedef struct _spTimelineVtable {
-    void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+    void (*apply) (const spTimeline* self, struct spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
                    int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction);
     void (*dispose) (spTimeline* self);
 } _spTimelineVtable;
@@ -281,36 +282,66 @@ SP_API void spRGBTimeline_setFrame (spRGBATimeline* self, int frameIndex, float
 
 /**/
 
-static const int TWOCOLOR_ENTRIES = 8;
+typedef struct spAlphaTimeline {
+    spCurveTimeline1 super;
+    int slotIndex;
+} spAlphaTimeline;
 
-typedef struct spTwoColorTimeline {
-	spCurveTimeline super;
-	int const framesCount;
-	float* const frames; /* time, r, g, b, a, ... */
-	int slotIndex;
-} spTwoColorTimeline;
+SP_API spAlphaTimeline* spAlphaTimeline_create (int frameCount, int bezierCount, int slotIndex);
 
-SP_API spTwoColorTimeline* spTwoColorTimeline_create (int framesCount);
+SP_API void spAlphaTimeline_setFrame (spAlphaTimeline* self, int frame, float time, float x);
 
-SP_API void spTwoColorTimeline_setFrame (spTwoColorTimeline* self, int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2);
+/**/
+
+typedef struct spRGBA2Timeline {
+    spCurveTimeline super;
+    int slotIndex;
+} spRGBA2Timeline;
+
+SP_API spRGBA2Timeline* spRGBA2Timeline_create (int framesCount, int bezierCount, int slotIndex);
+
+SP_API void spRGBA2Timeline_setFrame (spRGBA2Timeline* self, int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2);
+
+/**/
+
+typedef struct spRGB2Timeline {
+    spCurveTimeline super;
+    int slotIndex;
+} spRGB2Timeline;
+
+SP_API spRGB2Timeline* spRGB2Timeline_create (int framesCount, int bezierCount, int slotIndex);
+
+SP_API void spRGB2Timeline_setFrame (spRGB2Timeline* self, int frameIndex, float time, float r, float g, float b, float r2, float g2, float b2);
 
 /**/
 
 typedef struct spAttachmentTimeline {
 	spTimeline super;
-	int const framesCount;
-	float* const frames; /* time, ... */
 	int slotIndex;
 	const char** const attachmentNames;
 } spAttachmentTimeline;
 
-SP_API spAttachmentTimeline* spAttachmentTimeline_create (int framesCount);
+SP_API spAttachmentTimeline* spAttachmentTimeline_create (int framesCount, int SlotIndex);
 
 /* @param attachmentName May be 0. */
 SP_API void spAttachmentTimeline_setFrame (spAttachmentTimeline* self, int frameIndex, float time, const char* attachmentName);
 
 /**/
 
+typedef struct spDeformTimeline {
+    spCurveTimeline super;
+    int const frameVerticesCount;
+    const float** const frameVertices;
+    int slotIndex;
+    spAttachment* attachment;
+} spDeformTimeline;
+
+SP_API spDeformTimeline* spDeformTimeline_create (int framesCount, int frameVerticesCount);
+
+SP_API void spDeformTimeline_setFrame (spDeformTimeline* self, int frameIndex, float time, float* vertices);
+
+/**/
+
 typedef struct spEventTimeline {
 	spTimeline super;
 	int const framesCount;
@@ -338,22 +369,6 @@ SP_API void spDrawOrderTimeline_setFrame (spDrawOrderTimeline* self, int frameIn
 
 /**/
 
-typedef struct spDeformTimeline {
-	spCurveTimeline super;
-	int const framesCount;
-	float* const frames; /* time, ... */
-	int const frameVerticesCount;
-	const float** const frameVertices;
-	int slotIndex;
-	spAttachment* attachment;
-} spDeformTimeline;
-
-SP_API spDeformTimeline* spDeformTimeline_create (int framesCount, int frameVerticesCount);
-
-SP_API void spDeformTimeline_setFrame (spDeformTimeline* self, int frameIndex, float time, float* vertices);
-
-/**/
-
 static const int IKCONSTRAINT_ENTRIES = 6;
 
 typedef struct spIkConstraintTimeline {

+ 3 - 0
spine-c/spine-c/include/spine/Color.h

@@ -44,8 +44,11 @@ typedef struct spColor {
 SP_API spColor* spColor_create();
 SP_API void spColor_dispose(spColor* self);
 SP_API void spColor_setFromFloats(spColor* color, float r, float g, float b, float a);
+SP_API void spColor_setFromFloats3(spColor* self, float r, float g, float b);
 SP_API void spColor_setFromColor(spColor* color, spColor* otherColor);
+SP_API void spColor_setFromColor3(spColor* self, spColor* otherColor);
 SP_API void spColor_addFloats(spColor* color, float r, float g, float b, float a);
+SP_API void spColor_addFloats3(spColor* color, float r, float g, float b);
 SP_API void spColor_addColor(spColor* color, spColor* otherColor);
 SP_API void spColor_clamp(spColor* color);
 

+ 341 - 168
spine-c/spine-c/src/spine/Animation.c

@@ -406,12 +406,10 @@ void _spTranslateXTimeline_apply (const spTimeline* timeline, spSkeleton* skelet
                                   spEvent** firedEvents, int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
 ) {
     spBone *bone;
-    float x, t;
-    int i, curveType;
+    float x;
 
     spTranslateXTimeline* self = SUB_CAST(spTranslateXTimeline, timeline);
     float *frames = self->super.super.frames->items;
-    float *curves = self->super.curves->items;
 
     bone = skeleton->bones[self->boneIndex];
     if (!bone->active) return;
@@ -466,13 +464,10 @@ void _spTranslateYTimeline_apply (const spTimeline* timeline, spSkeleton* skelet
                                   spEvent** firedEvents, int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
 ) {
     spBone *bone;
-    int frame;
-    float y, t;
-    int i, curveType;
+    float y;
 
-    spTranslateXTimeline* self = SUB_CAST(spTranslateXTimeline, timeline);
+    spTranslateYTimeline* self = SUB_CAST(spTranslateYTimeline, timeline);
     float *frames = self->super.super.frames->items;
-    float *curves = self->super.curves->items;
 
     bone = skeleton->bones[self->boneIndex];
     if (!bone->active) return;
@@ -505,6 +500,7 @@ void _spTranslateYTimeline_apply (const spTimeline* timeline, spSkeleton* skelet
     UNUSED(lastTime);
     UNUSED(firedEvents);
     UNUSED(eventsCount);
+    UNUSED(direction);
 }
 
 spTranslateYTimeline* spTranslateYTimeline_create (int frameCount, int bezierCount, int boneIndex) {
@@ -654,13 +650,10 @@ void _spScaleXTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton,
                               spEvent** firedEvents, int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
 ) {
     spBone *bone;
-    int frame;
-    float x, t;
-    int i, curveType;
+    float x;
 
-    spTranslateXTimeline* self = SUB_CAST(spTranslateXTimeline, timeline);
+    spScaleXTimeline* self = SUB_CAST(spScaleXTimeline, timeline);
     float *frames = self->super.super.frames->items;
-    float *curves = self->super.curves->items;
 
     bone = skeleton->bones[self->boneIndex];
     if (!bone->active) return;
@@ -743,13 +736,10 @@ void _spScaleYTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton,
                                   spEvent** firedEvents, int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
 ) {
     spBone *bone;
-    int frame;
-    float y, t;
-    int i, curveType;
+    float y;
 
-    spTranslateXTimeline* self = SUB_CAST(spTranslateXTimeline, timeline);
+    spScaleYTimeline* self = SUB_CAST(spScaleYTimeline, timeline);
     float *frames = self->super.super.frames->items;
-    float *curves = self->super.curves->items;
 
     bone = skeleton->bones[self->boneIndex];
     if (!bone->active) return;
@@ -832,7 +822,6 @@ void _spShearTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, f
 	int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
 ) {
 	spBone *bone;
-	int frame;
 	float x, y, t;
 	int i, curveType;
 
@@ -920,13 +909,10 @@ void _spShearXTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton,
                               spEvent** firedEvents, int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
 ) {
     spBone *bone;
-    int frame;
     float x;
-    int curveType;
 
-    spTranslateXTimeline* self = SUB_CAST(spTranslateXTimeline, timeline);
+    spShearXTimeline* self = SUB_CAST(spShearXTimeline, timeline);
     float *frames = self->super.super.frames->items;
-    float *curves = self->super.curves->items;
 
     bone = skeleton->bones[self->boneIndex];
     if (!bone->active) return;
@@ -958,6 +944,7 @@ void _spShearXTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton,
     UNUSED(lastTime);
     UNUSED(firedEvents);
     UNUSED(eventsCount);
+    UNUSED(direction);
 }
 
 spShearXTimeline* spShearXTimeline_create (int frameCount, int bezierCount, int boneIndex) {
@@ -979,13 +966,10 @@ void _spShearYTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton,
                               spEvent** firedEvents, int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
 ) {
     spBone *bone;
-    int frame;
-    float y, t;
-    int i, curveType;
+    float y;
 
-    spTranslateXTimeline* self = SUB_CAST(spTranslateXTimeline, timeline);
+    spShearYTimeline* self = SUB_CAST(spShearYTimeline, timeline);
     float *frames = self->super.super.frames->items;
-    float *curves = self->super.curves->items;
 
     bone = skeleton->bones[self->boneIndex];
     if (!bone->active) return;
@@ -1018,6 +1002,7 @@ void _spShearYTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton,
     UNUSED(lastTime);
     UNUSED(firedEvents);
     UNUSED(eventsCount);
+    UNUSED(direction);
 }
 
 spShearYTimeline* spShearYTimeline_create (int frameCount, int bezierCount, int boneIndex) {
@@ -1041,7 +1026,7 @@ void _spRGBATimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, fl
 	int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
 ) {
 	spSlot *slot;
-	int frame, i, curveType;
+	int i, curveType;
 	float r, g, b, a, t;
 	spColor* color;
 	spColor* setup;
@@ -1143,11 +1128,11 @@ void _spRGBTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, flo
                             int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
 ) {
     spSlot *slot;
-    int frame, i, curveType;
+    int i, curveType;
     float r, g, b, t;
     spColor* color;
     spColor* setup;
-    spRGBATimeline* self = (spRGBATimeline*)timeline;
+    spRGBTimeline* self = (spRGBTimeline*)timeline;
     float *frames = self->super.super.frames->items;
     float *curves = self->super.curves->items;
 
@@ -1238,117 +1223,319 @@ void spRGBTimeline_setFrame (spRGBATimeline* self, int frameIndex, float time, f
 
 /**/
 
-static const int TWOCOLOR_PREV_TIME = -8, TWOCOLOR_PREV_R = -7, TWOCOLOR_PREV_G = -6, TWOCOLOR_PREV_B = -5, TWOCOLOR_PREV_A = -4;
-static const int TWOCOLOR_PREV_R2 = -3, TWOCOLOR_PREV_G2 = -2, TWOCOLOR_PREV_B2 = -1;
-static const int TWOCOLOR_R = 1, TWOCOLOR_G = 2, TWOCOLOR_B = 3, TWOCOLOR_A = 4, TWOCOLOR_R2 = 5, TWOCOLOR_G2 = 6, TWOCOLOR_B2 = 7;
+void _spAlphaTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
+                              spEvent** firedEvents, int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
+) {
+    spSlot *slot;
+    float a;
+    spColor* color;
+    spColor* setup;
+    spAlphaTimeline* self = (spAlphaTimeline*)timeline;
+    float *frames = self->super.super.frames->items;
 
-void _spTwoColorTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
-	int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
+    slot = skeleton->slots[self->slotIndex];
+    if (!slot->bone->active) return;
+
+    if (time < frames[0]) { /* Time is before first frame-> */
+        color = &slot->color; setup = &slot->data->color;
+        switch (blend) {
+            case SP_MIX_BLEND_SETUP:
+                color->a = setup->a;
+                return;
+            case SP_MIX_BLEND_FIRST:
+                color->a += (setup->a - color->a) * alpha;
+            default: {
+            }
+        }
+        return;
+    }
+
+    a = spCurveTimeline1_getCurveValue(SUPER(self), time);
+    if (alpha == 1)
+        slot->color.a = a;
+    else {
+        if (blend == SP_MIX_BLEND_SETUP) slot->color.a = slot->data->color.a;
+        slot->color.a += (a - slot->color.a) * alpha;
+    }
+    
+    UNUSED(lastTime);
+    UNUSED(firedEvents);
+    UNUSED(eventsCount);
+    UNUSED(direction);
+}
+
+spAlphaTimeline* spAlphaTimeline_create (int frameCount, int bezierCount, int slotIndex) {
+    spAlphaTimeline* timeline = NEW(spAlphaTimeline);
+    spPropertyId ids[1];
+    ids[0] = ((spPropertyId)SP_PROPERTY_ALPHA << 32) | slotIndex;
+    _spCurveTimeline_init(SUPER(timeline), frameCount, CURVE1_ENTRIES, bezierCount, ids, 1, _spCurveTimeline_dispose, _spAlphaTimeline_apply);
+    timeline->slotIndex = slotIndex;
+    return timeline;
+}
+
+void spAlphaTimeline_setFrame (spAlphaTimeline* self, int frame, float time, float alpha) {
+    spCurveTimeline1_setFrame(SUPER(self), frame, time, alpha);
+}
+
+/**/
+
+static const int RGBA2_ENTRIES = 8, COLOR_R2 = 5, COLOR_G2 = 6, COLOR_B2 = 7;
+
+void _spRGBA2Timeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+                            int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
 ) {
-	spSlot *slot;
-	int frame;
-	float percent, frameTime;
-	float r, g, b, a, r2, g2, b2;
-	spColor* light;
-	spColor* dark;
-	spColor* setupLight;
-	spColor* setupDark;
-	spColorTimeline* self = (spColorTimeline*)timeline;
-	slot = skeleton->slots[self->slotIndex];
-	if (!slot->bone->active) return;
+    spSlot *slot;
+    int i, curveType;
+    float r, g, b, a, r2, g2, b2, t;
+    spColor* light, *setupLight;
+    spColor* dark, *setupDark;
+    spRGBA2Timeline* self = (spRGBA2Timeline*)timeline;
+    float *frames = self->super.super.frames->items;
+    float *curves = self->super.curves->items;
 
-	if (time < self->frames[0]) {
-		switch (blend) {
-		case SP_MIX_BLEND_SETUP:
-			spColor_setFromColor(&slot->color, &slot->data->color);
-			spColor_setFromColor(slot->darkColor, slot->data->darkColor);
-			return;
-		case SP_MIX_BLEND_FIRST:
-			light = &slot->color;
-			dark = slot->darkColor;
-			setupLight = &slot->data->color;
-			setupDark = slot->data->darkColor;
-			spColor_addFloats(light, (setupLight->r - light->r) * alpha, (setupLight->g - light->g) * alpha, (setupLight->b - light->b) * alpha,
-				(setupLight->a - light->a) * alpha);
-			spColor_addFloats(dark, (setupDark->r - dark->r) * alpha, (setupDark->g - dark->g) * alpha, (setupDark->b - dark->b) * alpha, 0);
-		case SP_MIX_BLEND_REPLACE:
-		case SP_MIX_BLEND_ADD:
-			; /* to appease compiler */
-		}
-		return;
-	}
+    slot = skeleton->slots[self->slotIndex];
+    if (!slot->bone->active) return;
 
-	if (time >= self->frames[self->framesCount - TWOCOLOR_ENTRIES]) { /* Time is after last frame */
-		int i = self->framesCount;
-		r = self->frames[i + TWOCOLOR_PREV_R];
-		g = self->frames[i + TWOCOLOR_PREV_G];
-		b = self->frames[i + TWOCOLOR_PREV_B];
-		a = self->frames[i + TWOCOLOR_PREV_A];
-		r2 = self->frames[i + TWOCOLOR_PREV_R2];
-		g2 = self->frames[i + TWOCOLOR_PREV_G2];
-		b2 = self->frames[i + TWOCOLOR_PREV_B2];
-	} else {
-		/* Interpolate between the previous frame and the current frame. */
-		frame = binarySearch(self->frames, self->framesCount, time, TWOCOLOR_ENTRIES);
+    if (time < frames[0]) {
+        light = &slot->color; dark = slot->darkColor; setupLight = &slot->data->color; setupDark = slot->data->darkColor;
+        switch (blend) {
+            case SP_MIX_BLEND_SETUP:
+                spColor_setFromColor(light, setupLight);
+                spColor_setFromFloats3(dark, setupDark->r, setupDark->g, setupDark->b);
+                return;
+            case SP_MIX_BLEND_FIRST:
+                spColor_addFloats(light, (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;
+            default: {
+            }
+        }
+        return;
+    }
+
+    r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0;
+    i = search(frames, time, RGBA2_ENTRIES);
+    curveType = (int) curves[i / RGBA2_ENTRIES];
+    switch (curveType) {
+        case CURVE_LINEAR: {
+            float before = frames[i];
+            r = frames[i + COLOR_R];
+            g = frames[i + COLOR_G];
+            b = frames[i + COLOR_B];
+            a = frames[i + COLOR_A];
+            r2 = frames[i + COLOR_R2];
+            g2 = frames[i + COLOR_G2];
+            b2 = frames[i + COLOR_B2];
+            t = (time - before) / (frames[i + RGBA2_ENTRIES] - before);
+            r += (frames[i + RGBA2_ENTRIES + COLOR_R] - r) * t;
+            g += (frames[i + RGBA2_ENTRIES + COLOR_G] - g) * t;
+            b += (frames[i + RGBA2_ENTRIES + COLOR_B] - b) * t;
+            a += (frames[i + RGBA2_ENTRIES + COLOR_A] - a) * t;
+            r2 += (frames[i + RGBA2_ENTRIES + COLOR_R2] - r2) * t;
+            g2 += (frames[i + RGBA2_ENTRIES + COLOR_G2] - g2) * t;
+            b2 += (frames[i + RGBA2_ENTRIES + COLOR_B2] - b2) * t;
+            break;
+        }
+        case CURVE_STEPPED: {
+            r = frames[i + COLOR_R];
+            g = frames[i + COLOR_G];
+            b = frames[i + COLOR_B];
+            a = frames[i + COLOR_A];
+            r2 = frames[i + COLOR_R2];
+            g2 = frames[i + COLOR_G2];
+            b2 = frames[i + COLOR_B2];
+            break;
+        }
+        default: {
+            r = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR_R, curveType - CURVE_BEZIER);
+            g = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR_G,
+                               curveType + BEZIER_SIZE - CURVE_BEZIER);
+            b = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR_B,
+                               curveType + BEZIER_SIZE * 2 - CURVE_BEZIER);
+            a = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR_A,
+                               curveType + BEZIER_SIZE * 3 - CURVE_BEZIER);
+            r2 = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR_R2,
+                                curveType + BEZIER_SIZE * 4 - CURVE_BEZIER);
+            g2 = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR_G2,
+                                curveType + BEZIER_SIZE * 5 - CURVE_BEZIER);
+            b2 = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR_B2,
+                                curveType + BEZIER_SIZE * 6 - CURVE_BEZIER);
+        }
+    }
+    
+    light = &slot->color, dark = slot->darkColor;
+    if (alpha == 1) {
+        spColor_setFromFloats(light, r, g, b, a);
+        spColor_setFromFloats3(dark, r2, g2, b2);
+    } else {
+        if (blend == SP_MIX_BLEND_SETUP) {
+            spColor_setFromColor(light, &slot->data->color);
+            spColor_setFromColor(dark, slot->data->darkColor);
+        }
+        spColor_addFloats(light, (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;
+    }
 
-		r = self->frames[frame + TWOCOLOR_PREV_R];
-		g = self->frames[frame + TWOCOLOR_PREV_G];
-		b = self->frames[frame + TWOCOLOR_PREV_B];
-		a = self->frames[frame + TWOCOLOR_PREV_A];
-		r2 = self->frames[frame + TWOCOLOR_PREV_R2];
-		g2 = self->frames[frame + TWOCOLOR_PREV_G2];
-		b2 = self->frames[frame + TWOCOLOR_PREV_B2];
+    UNUSED(lastTime);
+    UNUSED(firedEvents);
+    UNUSED(eventsCount);
+    UNUSED(direction);
+}
 
-		frameTime = self->frames[frame];
-		percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / TWOCOLOR_ENTRIES - 1,
-			1 - (time - frameTime) / (self->frames[frame + TWOCOLOR_PREV_TIME] - frameTime));
-
-		r += (self->frames[frame + TWOCOLOR_R] - r) * percent;
-		g += (self->frames[frame + TWOCOLOR_G] - g) * percent;
-		b += (self->frames[frame + TWOCOLOR_B] - b) * percent;
-		a += (self->frames[frame + TWOCOLOR_A] - a) * percent;
-		r2 += (self->frames[frame + TWOCOLOR_R2] - r2) * percent;
-		g2 += (self->frames[frame + TWOCOLOR_G2] - g2) * percent;
-		b2 += (self->frames[frame + TWOCOLOR_B2] - b2) * percent;
-	}
-	if (alpha == 1) {
-		spColor_setFromFloats(&slot->color, r, g, b, a);
-		spColor_setFromFloats(slot->darkColor, r2, g2, b2, 1);
-	} else {
-		light = &slot->color;
-		dark = slot->darkColor;
-		if (blend == SP_MIX_BLEND_SETUP) {
-			spColor_setFromColor(light, &slot->data->color);
-			spColor_setFromColor(dark, slot->data->darkColor);
-		}
-		spColor_addFloats(light, (r - light->r) * alpha, (g - light->g) * alpha, (b - light->b) * alpha, (a - light->a) * alpha);
-		spColor_addFloats(dark, (r2 - dark->r) * alpha, (g2 - dark->g) * alpha, (b2 - dark->b) * alpha, 0);
-	}
+spRGBA2Timeline* spRGBA2Timeline_create (int framesCount, int bezierCount, int slotIndex) {
+    spRGBA2Timeline* timeline = NEW(spRGBA2Timeline);
+    spPropertyId ids[3];
+    ids[0] = ((spPropertyId)SP_PROPERTY_RGB << 32) | slotIndex;
+    ids[1] = ((spPropertyId)SP_PROPERTY_ALPHA << 32) | slotIndex;
+    ids[2] = ((spPropertyId)SP_PROPERTY_RGB2 << 32) | slotIndex;
+    _spCurveTimeline_init(SUPER(timeline), framesCount, RGBA2_ENTRIES, bezierCount, ids, 3, _spCurveTimeline_dispose, _spRGBA2Timeline_apply);
+    timeline->slotIndex = slotIndex;
+    return timeline;
+}
 
-	UNUSED(lastTime);
-	UNUSED(firedEvents);
-	UNUSED(eventsCount);
-	UNUSED(direction);
+void spRGBA2Timeline_setFrame (spRGBA2Timeline* self, int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2) {
+    float *frames = self->super.super.frames->items;
+    frameIndex *= RGBA_ENTRIES;
+    frames[frameIndex] = time;
+    frames[frameIndex + COLOR_R] = r;
+    frames[frameIndex + COLOR_G] = g;
+    frames[frameIndex + COLOR_B] = b;
+    frames[frameIndex + COLOR_A] = a;
+    frames[frameIndex + COLOR_R2] = r2;
+    frames[frameIndex + COLOR_G2] = g2;
+    frames[frameIndex + COLOR_B2] = b2;
 }
 
-int _spTwoColorTimeline_getPropertyId (const spTimeline* timeline) {
-	return (SP_TIMELINE_TWOCOLOR << 24) + SUB_CAST(spTwoColorTimeline, timeline)->slotIndex;
+/**/
+
+static const int RGB2_ENTRIES = 7, COLOR2_R2 = 5, COLOR2_G2 = 6, COLOR2_B2 = 7;
+
+
+void _spRGB2Timeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+                             int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction
+) {
+    spSlot *slot;
+    int i, curveType;
+    float r, g, b, r2, g2, b2, t;
+    spColor* light, *setupLight;
+    spColor* dark, *setupDark;
+    spRGB2Timeline* self = (spRGB2Timeline*)timeline;
+    float *frames = self->super.super.frames->items;
+    float *curves = self->super.curves->items;
+
+    slot = skeleton->slots[self->slotIndex];
+    if (!slot->bone->active) return;
+
+    if (time < frames[0]) {
+        light = &slot->color; dark = slot->darkColor; setupLight = &slot->data->color; setupDark = slot->data->darkColor;
+        switch (blend) {
+            case SP_MIX_BLEND_SETUP:
+                spColor_setFromColor3(light, setupLight);
+                spColor_setFromColor3(dark, setupDark);
+                return;
+            case SP_MIX_BLEND_FIRST:
+                spColor_addFloats3(light, (setupLight->r - light->r) * alpha, (setupLight->g - light->g) * alpha,
+                                  (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;
+            default: {
+            }
+        }
+        return;
+    }
+
+    r = 0, g = 0, b = 0, r2 = 0, g2 = 0, b2 = 0;
+    i = search(frames, time, RGB2_ENTRIES);
+    curveType = (int) curves[i / RGB2_ENTRIES];
+    switch (curveType) {
+        case CURVE_LINEAR: {
+            float before = frames[i];
+            r = frames[i + COLOR_R];
+            g = frames[i + COLOR_G];
+            b = frames[i + COLOR_B];
+            r2 = frames[i + COLOR2_R2];
+            g2 = frames[i + COLOR2_G2];
+            b2 = frames[i + COLOR2_B2];
+            t = (time - before) / (frames[i + RGB2_ENTRIES] - before);
+            r += (frames[i + RGBA2_ENTRIES + COLOR_R] - r) * t;
+            g += (frames[i + RGBA2_ENTRIES + COLOR_G] - g) * t;
+            b += (frames[i + RGBA2_ENTRIES + COLOR_B] - b) * t;
+            r2 += (frames[i + RGBA2_ENTRIES + COLOR2_R2] - r2) * t;
+            g2 += (frames[i + RGBA2_ENTRIES + COLOR2_G2] - g2) * t;
+            b2 += (frames[i + RGBA2_ENTRIES + COLOR2_B2] - b2) * t;
+            break;
+        }
+        case CURVE_STEPPED: {
+            r = frames[i + COLOR_R];
+            g = frames[i + COLOR_G];
+            b = frames[i + COLOR_B];
+            r2 = frames[i + COLOR2_R2];
+            g2 = frames[i + COLOR2_G2];
+            b2 = frames[i + COLOR2_B2];
+            break;
+        }
+        default: {
+            r = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR_R, curveType - CURVE_BEZIER);
+            g = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR_G,
+                                                curveType + BEZIER_SIZE - CURVE_BEZIER);
+            b = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR_B,
+                                                curveType + BEZIER_SIZE * 2 - CURVE_BEZIER);
+            r2 = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR2_R2,
+                                                 curveType + BEZIER_SIZE * 3 - CURVE_BEZIER);
+            g2 = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR2_G2,
+                                                 curveType + BEZIER_SIZE * 4 - CURVE_BEZIER);
+            b2 = _spCurveTimeline_getBezierValue(SUPER(self), time, i, COLOR2_B2,
+                                                 curveType + BEZIER_SIZE * 5 - CURVE_BEZIER);
+        }
+    }
+
+    light = &slot->color, dark = slot->darkColor;
+    if (alpha == 1) {
+        spColor_setFromFloats3(light, r, g, b);
+        spColor_setFromFloats3(dark, r2, g2, b2);
+    } else {
+        if (blend == SP_MIX_BLEND_SETUP) {
+            spColor_setFromColor3(light, &slot->data->color);
+            spColor_setFromColor3(dark, slot->data->darkColor);
+        }
+        spColor_addFloats3(light, (r - light->r) * alpha, (g - light->g) * alpha, (b - light->b) * alpha);
+        dark->r += (r2 - dark->r) * alpha;
+        dark->g += (g2 - dark->g) * alpha;
+        dark->b += (b2 - dark->b) * alpha;
+    }
+
+    UNUSED(lastTime);
+    UNUSED(firedEvents);
+    UNUSED(eventsCount);
+    UNUSED(direction);
 }
 
-spTwoColorTimeline* spTwoColorTimeline_create (int framesCount) {
-	return (spTwoColorTimeline*)_spBaseTimeline_create(framesCount, SP_TIMELINE_TWOCOLOR, TWOCOLOR_ENTRIES, _spTwoColorTimeline_apply, _spTwoColorTimeline_getPropertyId);
+spRGB2Timeline* spRGB2Timeline_create (int framesCount, int bezierCount, int slotIndex) {
+    spRGB2Timeline* timeline = NEW(spRGB2Timeline);
+    spPropertyId ids[2];
+    ids[0] = ((spPropertyId)SP_PROPERTY_RGB << 32) | slotIndex;
+    ids[1] = ((spPropertyId)SP_PROPERTY_RGB2 << 32) | slotIndex;
+    _spCurveTimeline_init(SUPER(timeline), framesCount, RGB2_ENTRIES, bezierCount, ids, 2, _spCurveTimeline_dispose, _spRGB2Timeline_apply);
+    timeline->slotIndex = slotIndex;
+    return timeline;
 }
 
-void spTwoColorTimeline_setFrame (spTwoColorTimeline* self, int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2) {
-	frameIndex *= TWOCOLOR_ENTRIES;
-	self->frames[frameIndex] = time;
-	self->frames[frameIndex + TWOCOLOR_R] = r;
-	self->frames[frameIndex + TWOCOLOR_G] = g;
-	self->frames[frameIndex + TWOCOLOR_B] = b;
-	self->frames[frameIndex + TWOCOLOR_A] = a;
-	self->frames[frameIndex + TWOCOLOR_R2] = r2;
-	self->frames[frameIndex + TWOCOLOR_G2] = g2;
-	self->frames[frameIndex + TWOCOLOR_B2] = b2;
+void spRGB2Timeline_setFrame (spRGB2Timeline* self, int frameIndex, float time, float r, float g, float b, float r2, float g2, float b2) {
+    float *frames = self->super.super.frames->items;
+    frameIndex *= RGBA_ENTRIES;
+    frames[frameIndex] = time;
+    frames[frameIndex + COLOR_R] = r;
+    frames[frameIndex + COLOR_G] = g;
+    frames[frameIndex + COLOR_B] = b;
+    frames[frameIndex + COLOR2_R2] = r2;
+    frames[frameIndex + COLOR2_G2] = g2;
+    frames[frameIndex + COLOR2_B2] = b2;
 }
 
 /**/
@@ -1362,30 +1549,31 @@ void _spAttachmentTimeline_apply (const spTimeline* timeline, spSkeleton* skelet
 	const char* attachmentName;
 	spAttachmentTimeline* self = (spAttachmentTimeline*)timeline;
 	int frameIndex;
+	float* frames = self->super.frames->items;
 	spSlot* slot = skeleton->slots[self->slotIndex];
 	if (!slot->bone->active) return;
 
 	if (direction == SP_MIX_DIRECTION_OUT) {
-	    if (blend == SP_MIX_BLEND_SETUP)
-	        _spSetAttachment(self, skeleton, slot, slot->data->attachmentName);
+	    if (blend == SP_MIX_BLEND_SETUP) {
+            _spSetAttachment(self, skeleton, slot, slot->data->attachmentName);
+        }
 		return;
 	}
 
-	if (time < self->frames[0]) {
+	if (time < frames[0]) {
 		if (blend == SP_MIX_BLEND_SETUP || blend == SP_MIX_BLEND_FIRST) {
 			_spSetAttachment(self, skeleton, slot, slot->data->attachmentName);
 		}
 		return;
 	}
 
-	if (time >= self->frames[self->framesCount - 1])
-		frameIndex = self->framesCount - 1;
-	else
-		frameIndex = binarySearch1(self->frames, self->framesCount, time) - 1;
+    if (time < frames[0]) {
+        if (blend == SP_MIX_BLEND_SETUP || blend == SP_MIX_BLEND_FIRST) _spSetAttachment(self, skeleton, slot, slot->data->attachmentName);
+        return;
+    }
 
-	attachmentName = self->attachmentNames[frameIndex];
-	spSlot_setAttachment(skeleton->slots[self->slotIndex],
-		attachmentName ? spSkeleton_getAttachmentForSlotIndex(skeleton, self->slotIndex, attachmentName) : 0);
+	attachmentName = self->attachmentNames[search(frames, self->super.frames->size, time)];
+    _spSetAttachment(self, skeleton, slot, attachmentName);
 
 	UNUSED(lastTime);
 	UNUSED(firedEvents);
@@ -1393,36 +1581,25 @@ void _spAttachmentTimeline_apply (const spTimeline* timeline, spSkeleton* skelet
 	UNUSED(alpha);
 }
 
-int _spAttachmentTimeline_getPropertyId (const spTimeline* timeline) {
-	return (SP_TIMELINE_ATTACHMENT << 24) + SUB_CAST(spAttachmentTimeline, timeline)->slotIndex;
-}
-
 void _spAttachmentTimeline_dispose (spTimeline* timeline) {
-	spAttachmentTimeline* self = SUB_CAST(spAttachmentTimeline, timeline);
-	int i;
-
-	_spTimeline_deinit(timeline);
-
-	for (i = 0; i < self->framesCount; ++i)
-		FREE(self->attachmentNames[i]);
-	FREE(self->attachmentNames);
-	FREE(self->frames);
-	FREE(self);
+    spAttachmentTimeline* self = SUB_CAST(spAttachmentTimeline, timeline);
+    int i;
+    for (i = 0; i < self->super.frames->size; ++i)
+        FREE(self->attachmentNames[i]);
+    FREE(self->attachmentNames);
 }
 
-spAttachmentTimeline* spAttachmentTimeline_create (int framesCount) {
+spAttachmentTimeline* spAttachmentTimeline_create (int framesCount, int slotIndex) {
 	spAttachmentTimeline* self = NEW(spAttachmentTimeline);
-	_spTimeline_init(SUPER(self), SP_TIMELINE_ATTACHMENT, _spAttachmentTimeline_dispose, _spAttachmentTimeline_apply, _spAttachmentTimeline_getPropertyId);
-
-	CONST_CAST(int, self->framesCount) = framesCount;
-	CONST_CAST(float*, self->frames) = CALLOC(float, framesCount);
-	CONST_CAST(char**, self->attachmentNames) = CALLOC(char*, framesCount);
-
+    spPropertyId ids[1];
+    ids[0] = ((spPropertyId)SP_PROPERTY_ATTACHMENT << 32) | slotIndex;
+	_spTimeline_init(SUPER(self), framesCount, 1, ids, 1, _spAttachmentTimeline_dispose, _spAttachmentTimeline_apply);
+    CONST_CAST(char**, self->attachmentNames) = CALLOC(char*, framesCount);
 	return self;
 }
 
 void spAttachmentTimeline_setFrame (spAttachmentTimeline* self, int frameIndex, float time, const char* attachmentName) {
-	self->frames[frameIndex] = time;
+	self->super.frames->items[frameIndex] = time;
 
 	FREE(self->attachmentNames[frameIndex]);
 	if (attachmentName)
@@ -1463,8 +1640,8 @@ void _spDeformTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton,
 			return;
 	}
 
-	frames = self->frames;
-	framesCount = self->framesCount;
+	frames = self->super.super.frames->items;
+	framesCount =  self->super.super.frames->size;
 	vertexCount = self->frameVerticesCount;
 	if (slot->deformCount < vertexCount) {
 		if (slot->deformCapacity < vertexCount) {
@@ -1570,7 +1747,7 @@ void _spDeformTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton,
 	}
 
 	/* Interpolate between the previous frame and the current frame. */
-	frame = binarySearch(frames, framesCount, time, 1);
+	frame = search2(frames, framesCount, time, 1);
 	prevVertices = frameVertices[frame - 1];
 	nextVertices = frameVertices[frame];
 	frameTime = frames[frame];
@@ -1645,10 +1822,6 @@ void _spDeformTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton,
 	UNUSED(direction);
 }
 
-int _spDeformTimeline_getPropertyId (const spTimeline* timeline) {
-	return (SP_TIMELINE_DEFORM << 27) + SUB_CAST(spVertexAttachment, SUB_CAST(spDeformTimeline, timeline)->attachment)->id + SUB_CAST(spDeformTimeline, timeline)->slotIndex;
-}
-
 void _spDeformTimeline_dispose (spTimeline* timeline) {
 	spDeformTimeline* self = SUB_CAST(spDeformTimeline, timeline);
 	int i;

+ 20 - 0
spine-c/spine-c/src/spine/Color.c

@@ -46,6 +46,13 @@ void spColor_setFromFloats(spColor* self, float r, float g, float b, float a) {
 	spColor_clamp(self);
 }
 
+void spColor_setFromFloats3(spColor* self, float r, float g, float b) {
+    self->r = r;
+    self->g = g;
+    self->b = b;
+    spColor_clamp(self);
+}
+
 void spColor_setFromColor(spColor* self, spColor* otherColor) {
 	self->r = otherColor->r;
 	self->g = otherColor->g;
@@ -53,6 +60,12 @@ void spColor_setFromColor(spColor* self, spColor* otherColor) {
 	self->a = otherColor->a;
 }
 
+void spColor_setFromColor3(spColor* self, spColor* otherColor) {
+    self->r = otherColor->r;
+    self->g = otherColor->g;
+    self->b = otherColor->b;
+}
+
 void spColor_addColor(spColor* self, spColor* otherColor) {
 	self->r += otherColor->r;
 	self->g += otherColor->g;
@@ -69,6 +82,13 @@ void spColor_addFloats(spColor* self, float r, float g, float b, float a) {
 	spColor_clamp(self);
 }
 
+void spColor_addFloats3(spColor* self, float r, float g, float b) {
+    self->r += r;
+    self->g += g;
+    self->b += b;
+    spColor_clamp(self);
+}
+
 void spColor_clamp(spColor* self) {
 	if (self->r < 0) self->r = 0;
 	else if (self->r > 1) self->r = 1;

+ 1 - 4
spine-c/spine-c/src/spine/Skeleton.c

@@ -214,8 +214,6 @@ static void _sortReset(spBone** bones, int bonesCount) {
 }
 
 static void _sortIkConstraint (_spSkeleton* const internal, spIkConstraint* constraint) {
-	int /*bool*/ contains = 0;
-	int i;
 	spBone* target = constraint->target;
 	spBone** constrained;
 	spBone* parent;
@@ -281,7 +279,6 @@ static void _sortTransformConstraint(_spSkeleton* const internal, spTransformCon
 	int i, boneCount;
 	spBone** constrained;
 	spBone* child;
-	int /*boolean*/ contains = 0;
 
 	constraint->active = constraint->target->active && (!constraint->data->skinRequired || (internal->super.skin != 0 && spTransformConstraintDataArray_contains(internal->super.skin->transformConstraints, constraint->data)));
 	if (!constraint->active) return;
@@ -408,7 +405,7 @@ void spSkeleton_updateWorldTransform (const spSkeleton* self) {
 
 void spSkeleton_updateWorldTransformWith (const spSkeleton* self, const spBone *parent) {
 /* Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection. */
-    int i, n;
+    int i;
     float rotationY, la, lb, lc, ld;
     _spUpdate *updateCache;
     _spSkeleton* internal = SUB_CAST(_spSkeleton, self);