Browse Source

[cpp] More 4.0 porting, SkeletonBinary.

badlogic 4 years ago
parent
commit
dbd708c866

+ 34 - 17
spine-cpp/spine-cpp/include/spine/SkeletonBinary.h

@@ -45,26 +45,38 @@ namespace spine {
 	class Attachment;
 	class VertexAttachment;
 	class Animation;
+	class Timeline;
 	class CurveTimeline;
+    class CurveTimeline1;
+    class CurveTimeline2;
 
 	class SP_API SkeletonBinary : public SpineObject {
 	public:
-		static const int BONE_ROTATE;
-		static const int BONE_TRANSLATE;
-		static const int BONE_SCALE;
-		static const int BONE_SHEAR;
-
-		static const int SLOT_ATTACHMENT;
-		static const int SLOT_COLOR;
-		static const int SLOT_TWO_COLOR;
-
-		static const int PATH_POSITION;
-		static const int PATH_SPACING;
-		static const int PATH_MIX;
-
-		static const int CURVE_LINEAR;
-		static const int CURVE_STEPPED;
-		static const int CURVE_BEZIER;
+        static const int BONE_ROTATE = 0;
+        static const int BONE_TRANSLATE = 1;
+        static const int BONE_TRANSLATEX = 2;
+        static const int BONE_TRANSLATEY = 3;
+        static const int BONE_SCALE = 4;
+        static const int BONE_SCALEX = 5;
+        static const int BONE_SCALEY = 6;
+        static const int BONE_SHEAR = 7;
+        static const int BONE_SHEARX = 8;
+        static const int BONE_SHEARY = 9;
+
+        static const int SLOT_ATTACHMENT = 0;
+        static const int SLOT_RGBA = 1;
+        static const int SLOT_RGB = 2;
+        static const int SLOT_RGBA2 = 3;
+        static const int SLOT_RGB2 = 4;
+        static const int SLOT_ALPHA = 5;
+
+        static const int PATH_POSITION = 0;
+        static const int PATH_SPACING = 1;
+        static const int PATH_MIX = 2;
+
+        static const int CURVE_LINEAR = 0;
+        static const int CURVE_STEPPED = 1;
+        static const int CURVE_BEZIER = 2;
 
 		explicit SkeletonBinary(Atlas* atlasArray);
 
@@ -124,7 +136,12 @@ namespace spine {
 
 		Animation* readAnimation(const String& name, DataInput* input, SkeletonData *skeletonData);
 
-		void readCurve(DataInput* input, int frameIndex, CurveTimeline* timeline);
+		void setBezier(DataInput* input, CurveTimeline* timeline, int bezier, int frame, int value, float time1, float time2,
+                       float value1, float value2, float scale);
+
+		Timeline *readTimeline (DataInput *input, CurveTimeline1 *timeline, float scale);
+
+        Timeline *readTimeline2 (DataInput *input, CurveTimeline2 *timeline, float scale);
 	};
 }
 

+ 0 - 80
spine-cpp/spine-cpp/include/spine/TwoColorTimeline.h

@@ -1,80 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
- * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifndef Spine_TwoColorTimeline_h
-#define Spine_TwoColorTimeline_h
-
-#include <spine/CurveTimeline.h>
-
-namespace spine {
-
-	class SP_API TwoColorTimeline : public CurveTimeline {
-		friend class SkeletonBinary;
-		friend class SkeletonJson;
-
-		RTTI_DECL
-
-	public:
-		static const int ENTRIES;
-
-		explicit TwoColorTimeline(int frameCount);
-
-		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
-
-		virtual int getPropertyId();
-
-		/// Sets the time and value of the specified keyframe.
-		void setFrame(int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2);
-
-		int getSlotIndex();
-		void setSlotIndex(int inValue);
-
-	private:
-		static const int PREV_TIME;
-		static const int PREV_R;
-		static const int PREV_G;
-		static const int PREV_B;
-		static const int PREV_A;
-		static const int PREV_R2;
-		static const int PREV_G2;
-		static const int PREV_B2;
-		static const int R;
-		static const int G;
-		static const int B;
-		static const int A;
-		static const int R2;
-		static const int G2;
-		static const int B2;
-
-		Vector<float> _frames; // time, r, g, b, a, r2, g2, b2, ...
-		int _slotIndex;
-	};
-}
-
-#endif /* Spine_TwoColorTimeline_h */

+ 454 - 228
spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp

@@ -59,7 +59,6 @@
 #include <spine/EventData.h>
 #include <spine/AttachmentTimeline.h>
 #include <spine/ColorTimeline.h>
-#include <spine/TwoColorTimeline.h>
 #include <spine/RotateTimeline.h>
 #include <spine/TranslateTimeline.h>
 #include <spine/ScaleTimeline.h>
@@ -76,23 +75,6 @@
 
 using namespace spine;
 
-const int SkeletonBinary::BONE_ROTATE = 0;
-const int SkeletonBinary::BONE_TRANSLATE = 1;
-const int SkeletonBinary::BONE_SCALE = 2;
-const int SkeletonBinary::BONE_SHEAR = 3;
-
-const int SkeletonBinary::SLOT_ATTACHMENT = 0;
-const int SkeletonBinary::SLOT_COLOR = 1;
-const int SkeletonBinary::SLOT_TWO_COLOR = 2;
-
-const int SkeletonBinary::PATH_POSITION = 0;
-const int SkeletonBinary::PATH_SPACING = 1;
-const int SkeletonBinary::PATH_MIX = 2;
-
-const int SkeletonBinary::CURVE_LINEAR = 0;
-const int SkeletonBinary::CURVE_STEPPED = 1;
-const int SkeletonBinary::CURVE_BEZIER = 2;
-
 SkeletonBinary::SkeletonBinary(Atlas *atlasArray) : _attachmentLoader(
 		new(__FILE__, __LINE__) AtlasAttachmentLoader(atlasArray)), _error(), _scale(1), _ownsLoader(true) {
 
@@ -123,17 +105,18 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 
 	skeletonData = new(__FILE__, __LINE__) SkeletonData();
 
-	char *skeletonData_hash = readString(input);
-	skeletonData->_hash.own(skeletonData_hash);
+	char buffer[8] = { 0 };
+	int lowHash = readInt(input);
+	int hightHash = readInt(input);
+	String hashString;
+	sprintf(buffer, "%x", hightHash);
+	hashString.append(buffer);
+    sprintf(buffer, "%x", lowHash);
+    hashString.append(buffer);
+	skeletonData->_hash = hashString;
 
-	char *skeletonData_version = readString(input);
-	skeletonData->_version.own(skeletonData_version);
-    if ("3.8.75" == skeletonData->_version) {
-        delete input;
-        delete skeletonData;
-        setError("Unsupported skeleton data, please export with a newer version of Spine.", "");
-        return NULL;
-    }
+	char *skeletonDataVersion = readString(input);
+	skeletonData->_version.own(skeletonDataVersion);
 
 	skeletonData->_x = readFloat(input);
 	skeletonData->_y = readFloat(input);
@@ -239,10 +222,12 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 		data->_offsetScaleX = readFloat(input);
 		data->_offsetScaleY = readFloat(input);
 		data->_offsetShearY = readFloat(input);
-		data->_rotateMix = readFloat(input);
-		data->_translateMix = readFloat(input);
-		data->_scaleMix = readFloat(input);
-		data->_shearMix = readFloat(input);
+        data->_mixRotate = readFloat(input);
+        data->_mixX = readFloat(input);
+        data->_mixY = readFloat(input);
+        data->_mixScaleX = readFloat(input);
+        data->_mixScaleY = readFloat(input);
+        data->_mixShearY = readFloat(input);
 		skeletonData->_transformConstraints[i] = data;
 	}
 
@@ -268,8 +253,9 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 		data->_spacing = readFloat(input);
 		if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed)
 			data->_spacing *= _scale;
-		data->_rotateMix = readFloat(input);
-		data->_translateMix = readFloat(input);
+		data->_mixRotate = readFloat(input);
+		data->_mixX = readFloat(input);
+        data->_mixY = readFloat(input);
 		skeletonData->_pathConstraints[i] = data;
 	}
 
@@ -700,69 +686,276 @@ void SkeletonBinary::readShortArray(DataInput *input, Vector<unsigned short> &ar
 	}
 }
 
+void SkeletonBinary::setBezier(DataInput* input, CurveTimeline* timeline, int bezier, int frame, int value, float time1, float time2,
+                               float value1, float value2, float scale) {
+    float cx1 = readFloat(input);
+    float cy1 = readFloat(input);
+    float cx2 = readFloat(input);
+    float cy2 = readFloat(input);
+    timeline->setBezier(bezier, frame, value, time1, value1, cx1, cy1 * scale, cx2, cy2 * scale, time2, value2);
+}
+
+Timeline *SkeletonBinary::readTimeline (DataInput *input, CurveTimeline1 *timeline, float scale) {
+    float time = readFloat(input);
+    float value = readFloat(input) * scale;
+    for (int frame = 0, bezier = 0, frameLast = timeline->getFrameCount() - 1;; frame++) {
+        timeline->setFrame(frame, time, value);
+        if (frame == frameLast) break;
+        float time2 = readFloat(input);
+        float value2 = readFloat(input) * scale;
+        switch (readSByte(input)) {
+            case CURVE_STEPPED:
+                timeline->setStepped(frame);
+                break;
+            case CURVE_BEZIER:
+                setBezier(input, timeline, bezier++, frame, 0, time, time2, value, value2, 1);
+        }
+        time = time2;
+        value = value2;
+    }
+    return timeline;
+}
+
+Timeline *SkeletonBinary::readTimeline2 (DataInput *input, CurveTimeline2 *timeline, float scale) {
+    float time = readFloat(input);
+    float value1 = readFloat(input) * scale;
+    float value2 = readFloat(input) * scale;
+    for (int frame = 0, bezier = 0, frameLast = timeline->getFrameCount() - 1;; frame++) {
+        timeline->setFrame(frame, time, value1, value2);
+        if (frame == frameLast) break;
+        float time2 = readFloat(input);
+        float nvalue1 = readFloat(input) * scale;
+        float nvalue2 = readFloat(input) * scale;
+        switch (readSByte(input)) {
+            case CURVE_STEPPED:
+                timeline->setStepped(frame);
+                break;
+            case CURVE_BEZIER:
+                setBezier(input, timeline, bezier++, frame, 0, time, time2, value1, nvalue1, scale);
+                setBezier(input, timeline, bezier++, frame, 1, time, time2, value2, nvalue2, scale);
+        }
+        time = time2;
+        value1 = nvalue1;
+        value2 = nvalue2;
+    }
+    return timeline;
+}
+
 Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, SkeletonData *skeletonData) {
 	Vector<Timeline *> timelines;
 	float scale = _scale;
-	float duration = 0;
-
+    int numTimelines = readVarint(input, true);
 	// Slot timelines.
-	for (int i = 0, n = readVarint(input, true); i < n; ++i) {
+	for (int i = 0, n = numTimelines; i < n; ++i) {
 		int slotIndex = readVarint(input, true);
 		for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) {
 			unsigned char timelineType = readByte(input);
 			int frameCount = readVarint(input, true);
+			int frameLast = frameCount - 1;
 			switch (timelineType) {
 				case SLOT_ATTACHMENT: {
-					AttachmentTimeline *timeline = new(__FILE__, __LINE__) AttachmentTimeline(frameCount);
-					timeline->_slotIndex = slotIndex;
-					for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
+					AttachmentTimeline *timeline = new(__FILE__, __LINE__) AttachmentTimeline(frameCount, slotIndex);
+					for (int frame = 0; frame < frameCount; ++frame) {
 						float time = readFloat(input);
 						String attachmentName(readStringRef(input, skeletonData));
-						timeline->setFrame(frameIndex, time, attachmentName);
-					}
-					timelines.add(timeline);
-					duration = MathUtil::max(duration, timeline->_frames[frameCount - 1]);
-					break;
-				}
-				case SLOT_COLOR: {
-					ColorTimeline *timeline = new(__FILE__, __LINE__) ColorTimeline(frameCount);
-					timeline->_slotIndex = slotIndex;
-					for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
-						float time = readFloat(input);
-						int color = readInt(input);
-						float r = ((color & 0xff000000) >> 24) / 255.0f;
-						float g = ((color & 0x00ff0000) >> 16) / 255.0f;
-						float b = ((color & 0x0000ff00) >> 8) / 255.0f;
-						float a = ((color & 0x000000ff)) / 255.0f;
-						timeline->setFrame(frameIndex, time, r, g, b, a);
-						if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
+						timeline->setFrame(frame, time, attachmentName);
 					}
 					timelines.add(timeline);
-					duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * ColorTimeline::ENTRIES]);
 					break;
 				}
-				case SLOT_TWO_COLOR: {
-					TwoColorTimeline *timeline = new(__FILE__, __LINE__) TwoColorTimeline(frameCount);
-					timeline->_slotIndex = slotIndex;
-					for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
-						float time = readFloat(input);
-						int color = readInt(input);
-						float r = ((color & 0xff000000) >> 24) / 255.0f;
-						float g = ((color & 0x00ff0000) >> 16) / 255.0f;
-						float b = ((color & 0x0000ff00) >> 8) / 255.0f;
-						float a = ((color & 0x000000ff)) / 255.0f;
-						int color2 = readInt(input); // 0x00rrggbb
-						float r2 = ((color2 & 0x00ff0000) >> 16) / 255.0f;
-						float g2 = ((color2 & 0x0000ff00) >> 8) / 255.0f;
-						float b2 = ((color2 & 0x000000ff)) / 255.0f;
-
-						timeline->setFrame(frameIndex, time, r, g, b, a, r2, g2, b2);
-						if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
-					}
-					timelines.add(timeline);
-					duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * TwoColorTimeline::ENTRIES]);
-					break;
+				case SLOT_RGBA: {
+				    int bezierCount = readVarint(input, true);
+					RGBATimeline *timeline = new(__FILE__, __LINE__) RGBATimeline(frameCount, bezierCount, slotIndex);
+
+					float time = readFloat(input);
+                    float r = readByte(input) / 255.0;
+                    float g = readByte(input) / 255.0;
+                    float b = readByte(input) / 255.0;
+                    float a = readByte(input) / 255.0;
+
+                    for (int frame = 0, bezier = 0;; frame++) {
+                        timeline->setFrame(frame, time, r, g, b, a);
+                        if (frame == frameLast) break;
+
+                        float time2 = readFloat(input);
+                        float r2 = readByte(input) / 255.0;
+                        float g2 = readByte(input) / 255.0;
+                        float b2 = readByte(input) / 255.0;
+                        float a2 = readByte(input) / 255.0;
+
+                        switch (readSByte(input)) {
+                            case CURVE_STEPPED:
+                                timeline->setStepped(frame);
+                                break;
+                            case CURVE_BEZIER:
+                                setBezier(input, timeline, bezier++, frame, 0, time, time2, r, r2, 1);
+                                setBezier(input, timeline, bezier++, frame, 1, time, time2, g, g2, 1);
+                                setBezier(input, timeline, bezier++, frame, 2, time, time2, b, b2, 1);
+                                setBezier(input, timeline, bezier++, frame, 3, time, time2, a, a2, 1);
+                        }
+                        time = time2;
+                        r = r2;
+                        g = g2;
+                        b = b2;
+                        a = a2;
+                    }
+                    timelines.add(timeline);
+                    break;
 				}
+				case SLOT_RGB: {
+                    int bezierCount = readVarint(input, true);
+                    RGBTimeline *timeline = new(__FILE__, __LINE__) RGBTimeline(frameCount, bezierCount, slotIndex);
+
+                    float time = readFloat(input);
+                    float r = readByte(input) / 255.0;
+                    float g = readByte(input) / 255.0;
+                    float b = readByte(input) / 255.0;
+
+                    for (int frame = 0, bezier = 0;; frame++) {
+                        timeline->setFrame(frame, time, r, g, b);
+                        if (frame == frameLast) break;
+
+                        float time2 = readFloat(input);
+                        float r2 = readByte(input) / 255.0;
+                        float g2 = readByte(input) / 255.0;
+                        float b2 = readByte(input) / 255.0;
+
+                        switch (readSByte(input)) {
+                            case CURVE_STEPPED:
+                                timeline->setStepped(frame);
+                                break;
+                            case CURVE_BEZIER:
+                                setBezier(input, timeline, bezier++, frame, 0, time, time2, r, r2, 1);
+                                setBezier(input, timeline, bezier++, frame, 1, time, time2, g, g2, 1);
+                                setBezier(input, timeline, bezier++, frame, 2, time, time2, b, b2, 1);
+                        }
+                        time = time2;
+                        r = r2;
+                        g = g2;
+                        b = b2;
+                    }
+                    timelines.add(timeline);
+                    break;
+                }
+                case SLOT_RGBA2: {
+                    int bezierCount = readVarint(input, true);
+                    RGBA2Timeline *timeline = new(__FILE__, __LINE__) RGBA2Timeline(frameCount, bezierCount, slotIndex);
+
+                    float time = readFloat(input);
+                    float r = readByte(input) / 255.0;
+                    float g = readByte(input) / 255.0;
+                    float b = readByte(input) / 255.0;
+                    float a = readByte(input) / 255.0;
+                    float r2 = readByte(input) / 255.0;
+                    float g2 = readByte(input) / 255.0;
+                    float b2 = readByte(input) / 255.0;
+
+                    for (int frame = 0, bezier = 0;; frame++) {
+                        timeline->setFrame(frame, time, r, g, b, a, r2, g2, b2);
+                        if (frame == frameLast) break;
+                        float time2 = readFloat(input);
+                        float nr = readByte(input) / 255.0;
+                        float ng = readByte(input) / 255.0;
+                        float nb = readByte(input) / 255.0;
+                        float na = readByte(input) / 255.0;
+                        float nr2 = readByte(input) / 255.0;
+                        float ng2 = readByte(input) / 255.0;
+                        float nb2 = readByte(input) / 255.0;
+
+                        switch (readSByte(input)) {
+                            case CURVE_STEPPED:
+                                timeline->setStepped(frame);
+                                break;
+                            case CURVE_BEZIER:
+                                setBezier(input, timeline, bezier++, frame, 0, time, time2, r, nr, 1);
+                                setBezier(input, timeline, bezier++, frame, 1, time, time2, g, ng, 1);
+                                setBezier(input, timeline, bezier++, frame, 2, time, time2, b, nb, 1);
+                                setBezier(input, timeline, bezier++, frame, 3, time, time2, a, na, 1);
+                                setBezier(input, timeline, bezier++, frame, 4, time, time2, r2, nr2, 1);
+                                setBezier(input, timeline, bezier++, frame, 5, time, time2, g2, ng2, 1);
+                                setBezier(input, timeline, bezier++, frame, 6, time, time2, b2, nb2, 1);
+                        }
+                        time = time2;
+                        r = nr;
+                        g = ng;
+                        b = nb;
+                        a = na;
+                        r2 = nr2;
+                        g2 = ng2;
+                        b2 = nb2;
+                    }
+                    timelines.add(timeline);
+                    break;
+                }
+                case SLOT_RGB2: {
+                    int bezierCount = readVarint(input, true);
+                    RGB2Timeline *timeline = new(__FILE__, __LINE__) RGB2Timeline(frameCount, bezierCount, slotIndex);
+
+                    float time = readFloat(input);
+                    float r = readByte(input) / 255.0;
+                    float g = readByte(input) / 255.0;
+                    float b = readByte(input) / 255.0;
+                    float r2 = readByte(input) / 255.0;
+                    float g2 = readByte(input) / 255.0;
+                    float b2 = readByte(input) / 255.0;
+
+                    for (int frame = 0, bezier = 0;; frame++) {
+                        timeline->setFrame(frame, time, r, g, b, r2, g2, b2);
+                        if (frame == frameLast) break;
+                        float time2 = readFloat(input);
+                        float nr = readByte(input) / 255.0;
+                        float ng = readByte(input) / 255.0;
+                        float nb = readByte(input) / 255.0;
+                        float nr2 = readByte(input) / 255.0;
+                        float ng2 = readByte(input) / 255.0;
+                        float nb2 = readByte(input) / 255.0;
+
+                        switch (readSByte(input)) {
+                            case CURVE_STEPPED:
+                                timeline->setStepped(frame);
+                                break;
+                            case CURVE_BEZIER:
+                                setBezier(input, timeline, bezier++, frame, 0, time, time2, r, nr, 1);
+                                setBezier(input, timeline, bezier++, frame, 1, time, time2, g, ng, 1);
+                                setBezier(input, timeline, bezier++, frame, 2, time, time2, b, nb, 1);
+                                setBezier(input, timeline, bezier++, frame, 3, time, time2, r2, nr2, 1);
+                                setBezier(input, timeline, bezier++, frame, 4, time, time2, g2, ng2, 1);
+                                setBezier(input, timeline, bezier++, frame, 5, time, time2, b2, nb2, 1);
+                        }
+                        time = time2;
+                        r = nr;
+                        g = ng;
+                        b = nb;
+                        r2 = nr2;
+                        g2 = ng2;
+                        b2 = nb2;
+                    }
+                    timelines.add(timeline);
+                    break;
+                }
+                case SLOT_ALPHA: {
+                    int bezierCount = readVarint(input, true);
+                    AlphaTimeline *timeline = new(__FILE__, __LINE__) AlphaTimeline(frameCount, bezierCount, slotIndex);
+                    float time = readFloat(input);
+                    float a = readByte(input) / 255.0;
+                    for (int frame = 0, bezier = 0;; frame++) {
+                        timeline->setFrame(frame, time, a);
+                        if (frame == frameLast) break;
+                        float time2 = readFloat(input);
+                        float a2 = readByte(input) / 255;
+                        switch (readSByte(input)) {
+                            case CURVE_STEPPED:
+                                timeline->setStepped(frame);
+                                break;
+                            case CURVE_BEZIER:
+                                setBezier(input, timeline, bezier++, frame, 0, time, time2, a, a2, 1);
+                        }
+                        time = time2;
+                        a = a2;
+                    }
+                    timelines.add(timeline);
+                    break;
+                }
 				default: {
 					ContainerUtil::cleanUpVectorOfPointers(timelines);
 					setError("Invalid timeline type for a slot: ", skeletonData->_slots[slotIndex]->_name.buffer());
@@ -778,144 +971,186 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
 		for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) {
 			unsigned char timelineType = readByte(input);
 			int frameCount = readVarint(input, true);
+            int bezierCount = readVarint(input, true);
+			Timeline *timeline = NULL;
 			switch (timelineType) {
-				case BONE_ROTATE: {
-					RotateTimeline *timeline = new(__FILE__, __LINE__) RotateTimeline(frameCount);
-					timeline->_boneIndex = boneIndex;
-					for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
-						float time = readFloat(input);
-						float degrees = readFloat(input);
-						timeline->setFrame(frameIndex, time, degrees);
-						if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
-					}
-					timelines.add(timeline);
-					duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * RotateTimeline::ENTRIES]);
-					break;
-				}
-				case BONE_TRANSLATE:
-				case BONE_SCALE:
-				case BONE_SHEAR: {
-					TranslateTimeline *timeline;
-					float timelineScale = 1;
-					if (timelineType == BONE_SCALE) {
-						timeline = new(__FILE__, __LINE__) ScaleTimeline(frameCount);
-					} else if (timelineType == BONE_SHEAR) {
-						timeline = new(__FILE__, __LINE__) ShearTimeline(frameCount);
-					} else {
-						timeline = new(__FILE__, __LINE__) TranslateTimeline(frameCount);
-						timelineScale = scale;
-					}
-					timeline->_boneIndex = boneIndex;
-					for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
-						float time = readFloat(input);
-						float x = readFloat(input) * timelineScale;
-						float y = readFloat(input) * timelineScale;
-						timeline->setFrame(frameIndex, time, x, y);
-						if (frameIndex < frameCount - 1) {
-							readCurve(input, frameIndex, timeline);
-						}
-					}
-					timelines.add(timeline);
-					duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * TranslateTimeline::ENTRIES]);
-					break;
-				}
+			    case BONE_ROTATE:
+			        timeline = readTimeline(input, new(__FILE__, __LINE__) RotateTimeline(frameCount, bezierCount, boneIndex), 1);
+			        break;
+			    case BONE_TRANSLATE:
+                    timeline = readTimeline2(input, new(__FILE__, __LINE__) TranslateTimeline(frameCount, bezierCount, boneIndex), scale);
+                    break;
+			    case BONE_TRANSLATEX:
+                    timeline = readTimeline(input, new(__FILE__, __LINE__) TranslateXTimeline(frameCount, bezierCount, boneIndex), scale);
+                    break;
+			    case BONE_TRANSLATEY:
+                    timeline = readTimeline(input, new(__FILE__, __LINE__) TranslateYTimeline(frameCount, bezierCount, boneIndex), scale);
+                    break;
+			    case BONE_SCALE:
+                    timeline = readTimeline2(input, new(__FILE__, __LINE__) ScaleTimeline(frameCount, bezierCount, boneIndex), 1);
+                    break;
+			    case BONE_SCALEX:
+                    timeline = readTimeline(input, new(__FILE__, __LINE__) ScaleXTimeline(frameCount, bezierCount, boneIndex), 1);
+                    break;
+			    case BONE_SCALEY:
+                    timeline = readTimeline(input, new(__FILE__, __LINE__) ScaleYTimeline(frameCount, bezierCount, boneIndex), 1);
+                    break;
+			    case BONE_SHEAR:
+                    timeline = readTimeline2(input, new(__FILE__, __LINE__) ShearTimeline(frameCount, bezierCount, boneIndex), 1);
+                    break;
+			    case BONE_SHEARX:
+                    timeline = readTimeline(input, new(__FILE__, __LINE__) ShearXTimeline(frameCount, bezierCount, boneIndex), 1);
+                    break;
+			    case BONE_SHEARY:
+                    timeline = readTimeline(input, new(__FILE__, __LINE__) ShearYTimeline(frameCount, bezierCount, boneIndex), 1);
+                    break;
 				default: {
 					ContainerUtil::cleanUpVectorOfPointers(timelines);
 					setError("Invalid timeline type for a bone: ", skeletonData->_bones[boneIndex]->_name.buffer());
 					return NULL;
 				}
 			}
+			timelines.add(timeline);
 		}
 	}
 
 	// IK timelines.
 	for (int i = 0, n = readVarint(input, true); i < n; ++i) {
-		int index = readVarint(input, true);
-		int frameCount = readVarint(input, true);
-		IkConstraintTimeline *timeline = new(__FILE__, __LINE__) IkConstraintTimeline(frameCount);
-		timeline->_ikConstraintIndex = index;
-		for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
-			float time = readFloat(input);
-			float mix = readFloat(input);
-			float softness = readFloat(input) * _scale;
-			signed char bendDirection = readSByte(input);
-			bool compress = readBoolean(input);
-			bool stretch = readBoolean(input);
-			timeline->setFrame(frameIndex, time, mix, softness, bendDirection, compress, stretch);
-			if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
-		}
+        int index = readVarint(input, true);
+        int frameCount = readVarint(input, true);
+        int frameLast = frameCount - 1;
+        int bezierCount = readVarint(input, true);
+        IkConstraintTimeline *timeline = new (__FILE__, __LINE__) IkConstraintTimeline(frameCount, bezierCount, index);
+        float time = readFloat(input);
+        float mix = readFloat(input);
+        float softness = readFloat(input) * scale;
+        for (int frame = 0, bezier = 0;; frame++) {
+            int bendDirection = readSByte(input);
+            bool compress = readBoolean(input);
+            bool stretch = readBoolean(input);
+            timeline->setFrame(frame, time, mix, softness, bendDirection, compress, stretch);
+            if (frame == frameLast) break;
+            float time2 = readFloat(input);
+            float mix2 = readFloat(input);
+            float softness2 = readFloat(input) * scale;
+            switch (readSByte(input)) {
+                case CURVE_STEPPED:
+                    timeline->setStepped(frame);
+                    break;
+                case CURVE_BEZIER:
+                    setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1);
+                    setBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2, scale);
+            }
+            time = time2;
+            mix = mix2;
+            softness = softness2;
+        }
 		timelines.add(timeline);
-		duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * IkConstraintTimeline::ENTRIES]);
 	}
 
 	// Transform constraint timelines.
 	for (int i = 0, n = readVarint(input, true); i < n; ++i) {
-		int index = readVarint(input, true);
-		int frameCount = readVarint(input, true);
-		TransformConstraintTimeline *timeline = new(__FILE__, __LINE__) TransformConstraintTimeline(frameCount);
-		timeline->_transformConstraintIndex = index;
-		for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
-			float time = readFloat(input);
-			float rotateMix = readFloat(input);
-			float translateMix = readFloat(input);
-			float scaleMix = readFloat(input);
-			float shearMix = readFloat(input);
-			timeline->setFrame(frameIndex, time, rotateMix, translateMix, scaleMix, shearMix);
-			if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
-		}
+        int index = readVarint(input, true);
+        int frameCount = readVarint(input, true);
+        int frameLast = frameCount - 1;
+        int bezierCount = readVarint(input, true);
+        TransformConstraintTimeline *timeline = new TransformConstraintTimeline(frameCount, bezierCount, index);
+        float time = readFloat(input);
+        float mixRotate = readFloat(input);
+        float mixX = readFloat(input);
+        float mixY = readFloat(input); 
+        float mixScaleX = readFloat(input);
+        float mixScaleY = readFloat(input);
+        float mixShearY = readFloat(input);
+        for (int frame = 0, bezier = 0;; frame++) {
+            timeline->setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
+            if (frame == frameLast) break;
+            float time2 = readFloat(input);
+            float mixRotate2 = readFloat(input);
+            float mixX2 = readFloat(input);
+            float mixY2 = readFloat(input);
+            float mixScaleX2 = readFloat(input);
+            float mixScaleY2 = readFloat(input);
+            float mixShearY2 = readFloat(input);
+            switch (readSByte(input)) {
+                case CURVE_STEPPED:
+                    timeline->setStepped(frame);
+                    break;
+                case CURVE_BEZIER:
+                    setBezier(input, timeline, bezier++, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+                    setBezier(input, timeline, bezier++, frame, 1, time, time2, mixX, mixX2, 1);
+                    setBezier(input, timeline, bezier++, frame, 2, time, time2, mixY, mixY2, 1);
+                    setBezier(input, timeline, bezier++, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
+                    setBezier(input, timeline, bezier++, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
+                    setBezier(input, timeline, bezier++, frame, 5, time, time2, mixShearY, mixShearY2, 1);
+            }
+            time = time2;
+            mixRotate = mixRotate2;
+            mixX = mixX2;
+            mixY = mixY2;
+            mixScaleX = mixScaleX2;
+            mixScaleY = mixScaleY2;
+            mixShearY = mixShearY2;
+        }
 		timelines.add(timeline);
-		duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * TransformConstraintTimeline::ENTRIES]);
 	}
 
 	// Path constraint timelines.
 	for (int i = 0, n = readVarint(input, true); i < n; ++i) {
-		int index = readVarint(input, true);
-		PathConstraintData *data = skeletonData->_pathConstraints[index];
-		for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) {
-			int timelineType = readSByte(input);
-			int frameCount = readVarint(input, true);
-			switch (timelineType) {
-				case PATH_POSITION:
-				case PATH_SPACING: {
-					PathConstraintPositionTimeline *timeline;
-					float timelineScale = 1;
-					if (timelineType == PATH_SPACING) {
-						timeline = new(__FILE__, __LINE__) PathConstraintSpacingTimeline(frameCount);
-
-						if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) timelineScale = scale;
-					} else {
-						timeline = new(__FILE__, __LINE__) PathConstraintPositionTimeline(frameCount);
-
-						if (data->_positionMode == PositionMode_Fixed) timelineScale = scale;
-					}
-					timeline->_pathConstraintIndex = index;
-					for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
-						float time = readFloat(input);
-						float value = readFloat(input) * timelineScale;
-						timeline->setFrame(frameIndex, time, value);
-						if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
-					}
-					timelines.add(timeline);
-					duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * PathConstraintPositionTimeline::ENTRIES]);
-					break;
-				}
-				case PATH_MIX: {
-					PathConstraintMixTimeline *timeline = new(__FILE__, __LINE__) PathConstraintMixTimeline(frameCount);
-
-					timeline->_pathConstraintIndex = index;
-					for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
-						float time = readFloat(input);
-						float rotateMix = readFloat(input);
-						float translateMix = readFloat(input);
-						timeline->setFrame(frameIndex, time, rotateMix, translateMix);
-						if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
-					}
-					timelines.add(timeline);
-					duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * PathConstraintMixTimeline::ENTRIES]);
-					break;
-				}
-			}
-		}
+        int index = readVarint(input, true);
+        PathConstraintData *data = skeletonData->_pathConstraints[index];
+        for (int ii = 0, nn = readVarint(input, true); ii < nn; ii++) {
+            int type = readSByte(input);
+            int frameCount = readVarint(input, true);
+            int bezierCount = readVarint(input, true);
+            switch (type) {
+                case PATH_POSITION: {
+                    timelines
+                            .add(readTimeline(input, new PathConstraintPositionTimeline(frameCount, bezierCount, index),
+                                              data->_positionMode == PositionMode_Fixed ? scale : 1));
+                    break;
+                }
+                case PATH_SPACING: {
+                    timelines
+                            .add(readTimeline(input,
+                                                              new PathConstraintSpacingTimeline(frameCount,
+                                                                                                bezierCount,
+                                                                                                index),
+                                                              data->_spacingMode == SpacingMode_Length ||
+                                                              data->_spacingMode == SpacingMode_Fixed ? scale : 1));
+                    break;
+                }
+                case PATH_MIX:
+                    PathConstraintMixTimeline *timeline = new PathConstraintMixTimeline(frameCount, bezierCount, index);
+                    float time = readFloat(input);
+                    float mixRotate = readFloat(input);
+                    float mixX = readFloat(input);
+                    float mixY = readFloat(input);
+                    for (int frame = 0, bezier = 0, frameLast = timeline->getFrameCount() - 1;; frame++) {
+                        timeline->setFrame(frame, time, mixRotate, mixX, mixY);
+                        if (frame == frameLast) break;
+                        float time2 = readFloat(input);
+                        float mixRotate2 = readFloat(input);
+                        float mixX2 = readFloat(input);
+                        float mixY2 = readFloat(input);
+                        switch (readSByte(input)) {
+                            case CURVE_STEPPED:
+                                timeline->setStepped(frame);
+                                break;
+                            case CURVE_BEZIER:
+                                setBezier(input, timeline, bezier++, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+                                setBezier(input, timeline, bezier++, frame, 1, time, time2, mixX, mixX2, 1);
+                                setBezier(input, timeline, bezier++, frame, 2, time, time2, mixY, mixY2, 1);
+
+                        }
+                        time = time2;
+                        mixRotate = mixRotate2;
+                        mixX = mixX2;
+                        mixY = mixY2;
+                    }
+                    timelines.add(timeline);
+            }
+        }
 	}
 
 	// Deform timelines.
@@ -937,22 +1172,21 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
 
 				bool weighted = attachment->_bones.size() > 0;
 				Vector<float> &vertices = attachment->_vertices;
-				size_t deformLength = weighted ? vertices.size() / 3 * 2 : vertices.size();
-
-				size_t frameCount = (size_t)readVarint(input, true);
+				int deformLength = weighted ? vertices.size() / 3 * 2 : vertices.size();
 
-				DeformTimeline *timeline = new(__FILE__, __LINE__) DeformTimeline(frameCount);
-				timeline->_slotIndex = slotIndex;
-				timeline->_attachment = attachment;
+				int frameCount = readVarint(input, true);
+				int frameLast = frameCount -1;
+				int bezierCount = readVarint(input, true);
+				DeformTimeline *timeline = new(__FILE__, __LINE__) DeformTimeline(frameCount, bezierCount, slotIndex, attachment);
 
-				for (size_t frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
-					float time = readFloat(input);
+				float time = readFloat(input);
+				for (int frame = 0, bezier = 0;; ++frame) {
 					Vector<float> deform;
 					size_t end = (size_t)readVarint(input, true);
 					if (end == 0) {
 						if (weighted) {
 							deform.setSize(deformLength, 0);
-							for (size_t iiii = 0; iiii < deformLength; ++iiii)
+							for (int iiii = 0; iiii < deformLength; ++iiii)
 								deform[iiii] = 0;
 						} else {
 							deform.clearAndAddAll(vertices);
@@ -975,12 +1209,20 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
 						}
 					}
 
-					timeline->setFrame(frameIndex, time, deform);
-					if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
+                    timeline->setFrame(frame, time, deform);
+                    if (frame == frameLast) break;
+                    float time2 = readFloat(input);
+                    switch(readSByte(input)) {
+                        case CURVE_STEPPED:
+                            timeline->setStepped(frame);
+                            break;
+                        case CURVE_BEZIER:
+                            setBezier(input, timeline, bezier++, frame, 0, time, time2, 0, 1, 1);
+                    }
+                    time = time2;
 				}
 
 				timelines.add(timeline);
-				duration = MathUtil::max(duration, timeline->_frames[frameCount - 1]);
 			}
 		}
 	}
@@ -1024,7 +1266,6 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
 			timeline->setFrame(i, time, drawOrder);
 		}
 		timelines.add(timeline);
-		duration = MathUtil::max(duration, timeline->_frames[drawOrderCount - 1]);
 	}
 
 	// Event timeline.
@@ -1050,27 +1291,12 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
 			}
 			timeline->setFrame(i, event);
 		}
-
 		timelines.add(timeline);
-		duration = MathUtil::max(duration, timeline->_frames[eventCount - 1]);
 	}
 
+    float duration = 0;
+    for (int i = 0, n = timelines.size(); i < n; i++) {
+        duration = MathUtil::max(duration, (timelines[i])->getDuration());
+    }
 	return new(__FILE__, __LINE__) Animation(String(name), timelines, duration);
 }
-
-void SkeletonBinary::readCurve(DataInput *input, int frameIndex, CurveTimeline *timeline) {
-	switch (readByte(input)) {
-	case CURVE_STEPPED: {
-		timeline->setStepped(frameIndex);
-		break;
-	}
-	case CURVE_BEZIER: {
-		float cx1 = readFloat(input);
-		float cy1 = readFloat(input);
-		float cx2 = readFloat(input);
-		float cy2 = readFloat(input);
-		timeline->setCurve(frameIndex, cx1, cy1, cx2, cy2);
-		break;
-	}
-	}
-}

+ 0 - 1
spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp

@@ -58,7 +58,6 @@
 #include <spine/EventData.h>
 #include <spine/AttachmentTimeline.h>
 #include <spine/ColorTimeline.h>
-#include <spine/TwoColorTimeline.h>
 #include <spine/RotateTimeline.h>
 #include <spine/TranslateTimeline.h>
 #include <spine/ScaleTimeline.h>

+ 0 - 182
spine-cpp/spine-cpp/src/spine/TwoColorTimeline.cpp

@@ -1,182 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
- * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#ifdef SPINE_UE4
-#include "SpinePluginPrivatePCH.h"
-#endif
-
-#include <spine/TwoColorTimeline.h>
-
-#include <spine/Skeleton.h>
-#include <spine/Event.h>
-
-#include <spine/Animation.h>
-#include <spine/Bone.h>
-#include <spine/Property.h>
-#include <spine/Slot.h>
-#include <spine/SlotData.h>
-
-using namespace spine;
-
-RTTI_IMPL(TwoColorTimeline, CurveTimeline)
-
-const int TwoColorTimeline::ENTRIES = 8;
-const int TwoColorTimeline::PREV_TIME = -8;
-const int TwoColorTimeline::PREV_R = -7;
-const int TwoColorTimeline::PREV_G = -6;
-const int TwoColorTimeline::PREV_B = -5;
-const int TwoColorTimeline::PREV_A = -4;
-const int TwoColorTimeline::PREV_R2 = -3;
-const int TwoColorTimeline::PREV_G2 = -2;
-const int TwoColorTimeline::PREV_B2 = -1;
-const int TwoColorTimeline::R = 1;
-const int TwoColorTimeline::G = 2;
-const int TwoColorTimeline::B = 3;
-const int TwoColorTimeline::A = 4;
-const int TwoColorTimeline::R2 = 5;
-const int TwoColorTimeline::G2 = 6;
-const int TwoColorTimeline::B2 = 7;
-
-TwoColorTimeline::TwoColorTimeline(int frameCount) : CurveTimeline(frameCount), _slotIndex(0) {
-	_frames.ensureCapacity(frameCount * ENTRIES);
-	_frames.setSize(frameCount * ENTRIES, 0);
-}
-
-void TwoColorTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
-	MixBlend blend, MixDirection direction
-) {
-	SP_UNUSED(lastTime);
-	SP_UNUSED(pEvents);
-	SP_UNUSED(direction);
-
-	Slot *slotP = skeleton._slots[_slotIndex];
-	Slot &slot = *slotP;
-	if (!slot._bone.isActive()) return;
-
-	if (time < _frames[0]) {
-		// Time is before first frame.
-		switch (blend) {
-			case MixBlend_Setup:
-				slot.getColor().set(slot.getData().getColor());
-				slot.getDarkColor().set(slot.getData().getDarkColor());
-				return;
-			case MixBlend_First: {
-				Color &color = slot.getColor();
-				color.r += (color.r - slot._data.getColor().r) * alpha;
-				color.g += (color.g - slot._data.getColor().g) * alpha;
-				color.b += (color.b - slot._data.getColor().b) * alpha;
-				color.a += (color.a - slot._data.getColor().a) * alpha;
-
-				Color &darkColor = slot.getDarkColor();
-				darkColor.r += (darkColor.r - slot._data.getDarkColor().r) * alpha;
-				darkColor.g += (darkColor.g - slot._data.getDarkColor().g) * alpha;
-				darkColor.b += (darkColor.b - slot._data.getDarkColor().b) * alpha;
-				return;
-			}
-			default:
-				return;
-		}
-	}
-
-	float r, g, b, a, r2, g2, b2;
-	if (time >= _frames[_frames.size() - ENTRIES]) {
-		// Time is after last frame.
-		size_t i = _frames.size();
-		r = _frames[i + PREV_R];
-		g = _frames[i + PREV_G];
-		b = _frames[i + PREV_B];
-		a = _frames[i + PREV_A];
-		r2 = _frames[i + PREV_R2];
-		g2 = _frames[i + PREV_G2];
-		b2 = _frames[i + PREV_B2];
-	} else {
-		// Interpolate between the previous frame and the current frame.
-		size_t frame = (size_t)Animation::binarySearch(_frames, time, ENTRIES);
-		r = _frames[frame + PREV_R];
-		g = _frames[frame + PREV_G];
-		b = _frames[frame + PREV_B];
-		a = _frames[frame + PREV_A];
-		r2 = _frames[frame + PREV_R2];
-		g2 = _frames[frame + PREV_G2];
-		b2 = _frames[frame + PREV_B2];
-		float frameTime = _frames[frame];
-		float percent = getCurvePercent(frame / ENTRIES - 1,
-										1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
-
-		r += (_frames[frame + R] - r) * percent;
-		g += (_frames[frame + G] - g) * percent;
-		b += (_frames[frame + B] - b) * percent;
-		a += (_frames[frame + A] - a) * percent;
-		r2 += (_frames[frame + R2] - r2) * percent;
-		g2 += (_frames[frame + G2] - g2) * percent;
-		b2 += (_frames[frame + B2] - b2) * percent;
-	}
-
-	if (alpha == 1) {
-		Color &color = slot.getColor();
-		color.set(r, g, b, a);
-
-		Color &darkColor = slot.getDarkColor();
-		darkColor.set(r2, g2, b2, 1);
-	} else {
-		Color &light = slot._color;
-		Color &dark = slot._darkColor;
-		if (blend == MixBlend_Setup) {
-			light.set(slot._data._color);
-			dark.set(slot._data._darkColor);
-		}
-		light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha);
-		dark.add((r2 - dark.r) * alpha, (g2 - dark.g) * alpha, (b2 - dark.b) * alpha, 0);
-	}
-}
-
-int TwoColorTimeline::getPropertyId() {
-	return ((int) TimelineType_TwoColor << 24) + _slotIndex;
-}
-
-void TwoColorTimeline::setFrame(int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2) {
-	frameIndex *= ENTRIES;
-	_frames[frameIndex] = time;
-	_frames[frameIndex + R] = r;
-	_frames[frameIndex + G] = g;
-	_frames[frameIndex + B] = b;
-	_frames[frameIndex + A] = a;
-	_frames[frameIndex + R2] = r2;
-	_frames[frameIndex + G2] = g2;
-	_frames[frameIndex + B2] = b2;
-}
-
-int TwoColorTimeline::getSlotIndex() {
-	return _slotIndex;
-}
-
-void TwoColorTimeline::setSlotIndex(int inValue) {
-	assert(inValue >= 0);
-	_slotIndex = inValue;
-}