Adding solid color and gradient support.
diff --git a/include/math/mat2d.hpp b/include/math/mat2d.hpp
index 81920eb..20b5bd3 100644
--- a/include/math/mat2d.hpp
+++ b/include/math/mat2d.hpp
@@ -32,6 +32,8 @@
 			result[5] = 0.0f;
 		}
 
+		static const Mat2D& identity();
+
 		static void fromRotation(Mat2D& result, float rad);
 		static void scale(Mat2D& result, const Mat2D& mat, const Vec2D& vec);
 		static void multiply(Mat2D& result, const Mat2D& a, const Mat2D& b);
diff --git a/renderer/library/include/opengl/opengl_render_paint.hpp b/renderer/library/include/opengl/opengl_render_paint.hpp
index 0540de4..1d017e4 100644
--- a/renderer/library/include/opengl/opengl_render_paint.hpp
+++ b/renderer/library/include/opengl/opengl_render_paint.hpp
@@ -2,13 +2,38 @@
 #define _RIVE_OPENGL_RENDER_PAINT_HPP_
 
 #include "renderer.hpp"
+#include <vector>
 
 namespace rive
 {
+	class OpenGLRenderer;
+	class OpenGLRenderPaint;
+	class OpenGLRenderPath;
+
+	class OpenGLGradient
+	{
+		friend class OpenGLRenderPaint;
+
+	private:
+		float m_Position[4];
+		int m_Type = 0;
+		std::vector<float> m_Colors;
+		std::vector<float> m_Stops;
+		bool m_IsVisible = false;
+
+	public:
+		OpenGLGradient(int type);
+		void position(float sx, float sy, float ex, float ey);
+		void addStop(unsigned int color, float stop);
+		void bind(OpenGLRenderer* renderer);
+	};
+
 	class OpenGLRenderPaint : public RenderPaint
 	{
 	private:
 		RenderPaintStyle m_PaintStyle;
+		float m_Color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+		OpenGLGradient* m_Gradient = nullptr;
 
 	public:
 		void style(RenderPaintStyle style) override;
@@ -24,6 +49,11 @@
 		void addStop(unsigned int color, float stop) override;
 		void completeGradient() override;
 		~OpenGLRenderPaint();
+
+		bool doesDraw() const;
+		void draw(OpenGLRenderer* renderer,
+		          const Mat2D& transform,
+		          OpenGLRenderPath* path);
 	};
 } // namespace rive
 #endif
\ No newline at end of file
diff --git a/renderer/library/include/opengl/opengl_render_path.hpp b/renderer/library/include/opengl/opengl_render_path.hpp
index 050a214..afd968f 100644
--- a/renderer/library/include/opengl/opengl_render_path.hpp
+++ b/renderer/library/include/opengl/opengl_render_path.hpp
@@ -3,6 +3,7 @@
 
 #include "contour_render_path.hpp"
 #include "opengl.h"
+#include "math/mat2d.hpp"
 
 namespace rive
 {
@@ -20,7 +21,9 @@
 		FillRule fillRule() const { return m_FillRule; }
 
 		void stencil(OpenGLRenderer* renderer, const Mat2D& transform);
-		void cover(OpenGLRenderer* renderer, const Mat2D& transform);
+		void cover(OpenGLRenderer* renderer,
+		           const Mat2D& transform,
+		           const Mat2D& localTransform = Mat2D::identity());
 	};
 } // namespace rive
 #endif
\ No newline at end of file
diff --git a/renderer/library/include/opengl/opengl_renderer.hpp b/renderer/library/include/opengl/opengl_renderer.hpp
index 4f389bd..149c0fc 100644
--- a/renderer/library/include/opengl/opengl_renderer.hpp
+++ b/renderer/library/include/opengl/opengl_renderer.hpp
@@ -17,6 +17,13 @@
 		GLuint m_IndexBuffer = 0;
 		GLint m_ProjectionUniformIndex = -1;
 		GLint m_TransformUniformIndex = -1;
+		GLint m_FillTypeUniformIndex = -1;
+		GLint m_StopCountUniformIndex = -1;
+		GLint m_StopColorsUniformIndex = -1;
+		GLint m_ColorUniformIndex = -1;
+		GLint m_StopsUniformIndex = -1;
+		GLint m_GradientPositionUniformIndex = -1;
+		GLint m_ShapeTransformUniformIndex = -1;
 		GLuint m_VertexArray = 0;
 
 		/// Indices for the max sized contour, prepended with 2 triangles for
@@ -42,6 +49,24 @@
 		void updateIndexBuffer(std::size_t contourLength);
 
 		GLint transformUniformIndex() const { return m_TransformUniformIndex; }
+
+		GLint fillTypeUniformIndex() const { return m_FillTypeUniformIndex; }
+		GLint stopCountUniformIndex() const { return m_StopCountUniformIndex; }
+		GLint stopColorsUniformIndex() const
+		{
+			return m_StopColorsUniformIndex;
+		}
+		GLint colorUniformIndex() const { return m_ColorUniformIndex; }
+		GLint stopsUniformIndex() const { return m_StopsUniformIndex; }
+		GLint shapeTransformUniformIndex() const
+		{
+			return m_ShapeTransformUniformIndex;
+		}
+		GLint gradientPositionUniformIndex() const
+		{
+			return m_GradientPositionUniformIndex;
+		}
+
 		GLuint program() const { return m_Program; }
 		virtual const char* shaderHeader() const { return nullptr; };
 	};
diff --git a/renderer/library/src/opengl/opengl_render_paint.cpp b/renderer/library/src/opengl/opengl_render_paint.cpp
index 9a21daa..f4d3260 100644
--- a/renderer/library/src/opengl/opengl_render_paint.cpp
+++ b/renderer/library/src/opengl/opengl_render_paint.cpp
@@ -1,9 +1,24 @@
 #include "opengl/opengl_render_paint.hpp"
+#include "shapes/paint/color.hpp"
+#include "opengl/opengl_renderer.hpp"
+#include "opengl/opengl_render_path.hpp"
+
 using namespace rive;
 
+void fillColorBuffer(float* buffer, unsigned int value)
+{
+	buffer[0] = colorRed(value) / 255.0f;
+	buffer[1] = colorGreen(value) / 255.0f;
+	buffer[2] = colorBlue(value) / 255.0f;
+	buffer[3] = colorAlpha(value) / 255.0f;
+}
+
 void OpenGLRenderPaint::style(RenderPaintStyle style) { m_PaintStyle = style; }
 
-void OpenGLRenderPaint::color(unsigned int value) {}
+void OpenGLRenderPaint::color(unsigned int value)
+{
+	fillColorBuffer(m_Color, value);
+}
 
 void OpenGLRenderPaint::thickness(float value) {}
 
@@ -15,14 +30,88 @@
 
 void OpenGLRenderPaint::linearGradient(float sx, float sy, float ex, float ey)
 {
+	if (m_Gradient == nullptr)
+	{
+		m_Gradient = new OpenGLGradient(1);
+	}
+	m_Gradient->position(sx, sy, ex, ey);
 }
 
 void OpenGLRenderPaint::radialGradient(float sx, float sy, float ex, float ey)
 {
+	if (m_Gradient == nullptr)
+	{
+		m_Gradient = new OpenGLGradient(2);
+	}
+	m_Gradient->position(sx, sy, ex, ey);
 }
 
-void OpenGLRenderPaint::addStop(unsigned int color, float stop) {}
+void OpenGLRenderPaint::addStop(unsigned int color, float stop)
+{
+	m_Gradient->addStop(color, stop);
+}
 
 void OpenGLRenderPaint::completeGradient() {}
 
-OpenGLRenderPaint::~OpenGLRenderPaint() {}
\ No newline at end of file
+OpenGLRenderPaint::~OpenGLRenderPaint() { delete m_Gradient; }
+
+bool OpenGLRenderPaint::doesDraw() const
+{
+	return true;
+	return m_Color[3] > 0.0f &&
+	       (m_Gradient == nullptr || m_Gradient->m_IsVisible);
+}
+
+void OpenGLRenderPaint::draw(OpenGLRenderer* renderer,
+                             const Mat2D& transform,
+                             OpenGLRenderPath* path)
+{
+	uint32_t type = 0;
+	if (m_Gradient != nullptr)
+	{
+		type = m_Gradient->m_Type;
+		m_Gradient->bind(renderer);
+	}
+
+	glUniform1i(renderer->fillTypeUniformIndex(), type);
+	glUniform4fv(renderer->colorUniformIndex(), 1, m_Color);
+
+	path->cover(renderer, transform);
+}
+
+OpenGLGradient::OpenGLGradient(int type) : m_Type(type) {}
+
+void OpenGLGradient::position(float sx, float sy, float ex, float ey)
+{
+	m_Colors.clear();
+	m_Stops.clear();
+	m_IsVisible = false;
+	m_Position[0] = sx;
+	m_Position[1] = sy;
+	m_Position[2] = ex;
+	m_Position[3] = ey;
+}
+
+void OpenGLGradient::bind(OpenGLRenderer* renderer)
+{
+	auto numberOfStops = m_Stops.size();
+
+	glUniform1i(renderer->stopCountUniformIndex(), numberOfStops);
+	glUniform4fv(
+	    renderer->stopColorsUniformIndex(), numberOfStops, &m_Colors[0]);
+	glUniform1fv(renderer->stopsUniformIndex(), numberOfStops, &m_Stops[0]);
+	glUniform4fv(renderer->gradientPositionUniformIndex(), 1, &m_Position[0]);
+}
+
+void OpenGLGradient::addStop(unsigned int color, float stop)
+{
+	auto index = m_Colors.size();
+	m_Colors.resize(index + 4);
+
+	fillColorBuffer(&m_Colors[index], color);
+	if (m_Colors[index + 3] > 0.0f)
+	{
+		m_IsVisible = true;
+	}
+	m_Stops.push_back(stop);
+}
\ No newline at end of file
diff --git a/renderer/library/src/opengl/opengl_render_path.cpp b/renderer/library/src/opengl/opengl_render_path.cpp
index 611cb40..1c13c01 100644
--- a/renderer/library/src/opengl/opengl_render_path.cpp
+++ b/renderer/library/src/opengl/opengl_render_path.cpp
@@ -15,10 +15,10 @@
 	{
 		for (auto& subPath : m_SubPaths)
 		{
-			Mat2D subPathTransform;
-			Mat2D::multiply(subPathTransform, transform, subPath.transform());
+			Mat2D pathTransform;
+			Mat2D::multiply(pathTransform, transform, subPath.transform());
 			reinterpret_cast<OpenGLRenderPath*>(subPath.path())
-			    ->stencil(renderer, subPathTransform);
+			    ->stencil(renderer, pathTransform);
 		}
 		return;
 	}
@@ -108,16 +108,19 @@
 	// glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, &indices[0]);
 }
 
-void OpenGLRenderPath::cover(OpenGLRenderer* renderer, const Mat2D& transform)
+void OpenGLRenderPath::cover(OpenGLRenderer* renderer,
+                             const Mat2D& transform,
+                             const Mat2D& localTransform)
 {
 	if (isContainer())
 	{
 		for (auto& subPath : m_SubPaths)
 		{
-			Mat2D subPathTransform;
-			Mat2D::multiply(subPathTransform, transform, subPath.transform());
+			const Mat2D& subPathTransform = subPath.transform();
+			Mat2D pathTransform;
+			Mat2D::multiply(pathTransform, transform, subPathTransform);
 			reinterpret_cast<OpenGLRenderPath*>(subPath.path())
-			    ->stencil(renderer, subPathTransform);
+			    ->cover(renderer, pathTransform, subPathTransform);
 		}
 		return;
 	}
@@ -130,26 +133,47 @@
 		return;
 	}
 
-	auto triangleCount = vertexCount - 5;
+	{
+		float m4[16] = {transform[0],
+		                transform[1],
+		                0.0,
+		                0.0,
+		                transform[2],
+		                transform[3],
+		                0.0,
+		                0.0,
+		                0.0,
+		                0.0,
+		                1.0,
+		                0.0,
+		                transform[4],
+		                transform[5],
+		                0.0,
+		                1.0};
 
-	float m4[16] = {transform[0],
-	                transform[1],
-	                0.0,
-	                0.0,
-	                transform[2],
-	                transform[3],
-	                0.0,
-	                0.0,
-	                0.0,
-	                0.0,
-	                1.0,
-	                0.0,
-	                transform[4],
-	                transform[5],
-	                0.0,
-	                1.0};
+		glUniformMatrix4fv(renderer->transformUniformIndex(), 1, GL_FALSE, m4);
+	}
+	{
+		float m4[16] = {localTransform[0],
+		                localTransform[1],
+		                0.0,
+		                0.0,
+		                localTransform[2],
+		                localTransform[3],
+		                0.0,
+		                0.0,
+		                0.0,
+		                0.0,
+		                1.0,
+		                0.0,
+		                localTransform[4],
+		                localTransform[5],
+		                0.0,
+		                1.0};
 
-	glUniformMatrix4fv(renderer->transformUniformIndex(), 1, GL_FALSE, m4);
+		glUniformMatrix4fv(
+		    renderer->shapeTransformUniformIndex(), 1, GL_FALSE, m4);
+	}
 
 	glEnableVertexAttribArray(0);
 	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * 4, (void*)0);
diff --git a/renderer/library/src/opengl/opengl_renderer.cpp b/renderer/library/src/opengl/opengl_renderer.cpp
index fa0f741..30f0e08 100644
--- a/renderer/library/src/opengl/opengl_renderer.cpp
+++ b/renderer/library/src/opengl/opengl_renderer.cpp
@@ -78,6 +78,16 @@
 	m_ProjectionUniformIndex = glGetUniformLocation(m_Program, "projection");
 	m_TransformUniformIndex = glGetUniformLocation(m_Program, "transform");
 
+	m_FillTypeUniformIndex = glGetUniformLocation(m_Program, "fillType");
+	m_StopCountUniformIndex = glGetUniformLocation(m_Program, "count");
+	m_StopColorsUniformIndex = glGetUniformLocation(m_Program, "colors");
+	m_StopsUniformIndex = glGetUniformLocation(m_Program, "stops");
+	m_ColorUniformIndex = glGetUniformLocation(m_Program, "color");
+	m_GradientPositionUniformIndex =
+	    glGetUniformLocation(m_Program, "position");
+	m_ShapeTransformUniformIndex =
+	    glGetUniformLocation(m_Program, "localTransform");
+
 	float projection[16] = {0.0f};
 	orthographicProjection(projection, 0.0f, 800, 800, 0.0f, 0.0f, 1.0f);
 	modelViewProjection(projection);
@@ -88,7 +98,7 @@
 void OpenGLRenderer::drawPath(RenderPath* path, RenderPaint* paint)
 {
 	auto glPaint = static_cast<OpenGLRenderPaint*>(paint);
-	if (glPaint->style() == RenderPaintStyle::stroke)
+	if (glPaint->style() == RenderPaintStyle::stroke || !glPaint->doesDraw())
 	{
 		return;
 	}
@@ -108,7 +118,8 @@
 	glStencilFunc(GL_NOTEQUAL, 0, 0xFF);
 	glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
 
-	glPath->cover(this, transform());
+	glPaint->draw(this, transform(), glPath);
+	// glPath->cover(this, transform());
 }
 
 void OpenGLRenderer::clipPath(RenderPath* path) {}
diff --git a/renderer/library/src/opengl/opengl_shaders.cpp b/renderer/library/src/opengl/opengl_shaders.cpp
index 8ada16f..9da09c9 100644
--- a/renderer/library/src/opengl/opengl_shaders.cpp
+++ b/renderer/library/src/opengl/opengl_shaders.cpp
@@ -10,7 +10,7 @@
 uniform mat4 transform;
 uniform mat4 localTransform;
 
-void main(void) 
+void main() 
 {
     gl_Position = projection*transform*vec4(position, 0.0, 1.0);
     pos = (localTransform*vec4(position, 0.0, 1.0)).xy;
@@ -25,8 +25,7 @@
 #endif
 
 uniform vec4 color;
-uniform vec2 start;
-uniform vec2 end;
+uniform vec4 position;
 uniform int count;
 uniform vec4 colors[16];
 uniform float stops[16];
@@ -36,52 +35,59 @@
 
 void main()
 {
-    fragColor = vec4(0.0, 0.0, 0.0, 0.35);
+    if (fillType == 0)
+    {
+        // solid
+        fragColor = color;//vec4(color.rgb * color.a, color.a);
+    }
+    else if (fillType == 1)
+    {
+        // linear
 
-    // if (fillType == 0)
-    // {
-    //     // solid
-    //     gl_FragColor = vec4(color.rgb * color.a, color.a);
-    // }
-    // else if (fillType == 1)
-    // {
-    //     // linear
-    //     vec2 toEnd = end - start;
-    //     float lengthSquared = toEnd.x * toEnd.x + toEnd.y * toEnd.y;
-    //     float f = dot(pos - start, toEnd) / lengthSquared;
-    //     gl_FragColor =
-    //         mix(colors[0], colors[1], smoothstep(stops[0], stops[1], f));
-    //     for (int i = 1; i < 15; ++i)
-    //     {
-    //         if (i >= count - 1)
-    //         {
-    //             break;
-    //         }
-    //         gl_FragColor = mix(gl_FragColor,
-    //                         colors[i + 1],
-    //                         smoothstep(stops[i], stops[i + 1], f));
-    //     }
-    //     float alpha = gl_FragColor.w;
-    //     gl_FragColor = vec4(gl_FragColor.xyz * alpha, alpha);
-    // }
-    // else if (fillType == 2)
-    // {
-    //     // radial
-    //     float f = distance(start, pos) / distance(start, end);
-    //     gl_FragColor =
-    //         mix(colors[0], colors[1], smoothstep(stops[0], stops[1], f));
-    //     for (int i = 1; i < 15; ++i)
-    //     {
-    //         if (i >= count - 1)
-    //         {
-    //             break;
-    //         }
-    //         gl_FragColor = mix(gl_FragColor,
-    //                         colors[i + 1],
-    //                         smoothstep(stops[i], stops[i + 1], f));
-    //     }
-    //     float alpha = gl_FragColor.w;
-    //     gl_FragColor = vec4(gl_FragColor.xyz * alpha, alpha);
-    // }
+        vec2 start = position.xy;
+        vec2 end = position.zw;
+
+        
+        vec2 toEnd = end - start;
+        float lengthSquared = toEnd.x * toEnd.x + toEnd.y * toEnd.y;
+        float f = dot(pos - start, toEnd) / lengthSquared;
+        fragColor =
+            mix(colors[0], colors[1], smoothstep(stops[0], stops[1], f));
+        for (int i = 1; i < 15; ++i)
+        {
+            if (i >= count - 1)
+            {
+                break;
+            }
+            fragColor = mix(fragColor,
+                            colors[i + 1],
+                            smoothstep(stops[i], stops[i + 1], f));
+        }
+        // float alpha = fragColor.w;
+        // fragColor = vec4(fragColor.xyz * alpha, alpha);
+    }
+    else if (fillType == 2)
+    {
+        // radial
+
+        vec2 start = position.xy;
+        vec2 end = position.zw;
+        
+        float f = distance(start, pos) / distance(start, end);
+        fragColor =
+            mix(colors[0], colors[1], smoothstep(stops[0], stops[1], f));
+        for (int i = 1; i < 15; ++i)
+        {
+            if (i >= count - 1)
+            {
+                break;
+            }
+            fragColor = mix(fragColor,
+                            colors[i + 1],
+                            smoothstep(stops[i], stops[i + 1], f));
+        }
+        // float alpha = fragColor.w;
+        // fragColor = vec4(fragColor.xyz * alpha, alpha);
+    }
 }
 )""";
\ No newline at end of file
diff --git a/renderer/viewer/assets/car.riv b/renderer/viewer/assets/car.riv
new file mode 100644
index 0000000..81202cb
--- /dev/null
+++ b/renderer/viewer/assets/car.riv
Binary files differ
diff --git a/renderer/viewer/assets/gradient.riv b/renderer/viewer/assets/gradient.riv
new file mode 100644
index 0000000..3e022a0
--- /dev/null
+++ b/renderer/viewer/assets/gradient.riv
Binary files differ
diff --git a/renderer/viewer/assets/juice.riv b/renderer/viewer/assets/juice.riv
new file mode 100644
index 0000000..5df55e4
--- /dev/null
+++ b/renderer/viewer/assets/juice.riv
Binary files differ
diff --git a/renderer/viewer/src/viewer.cpp b/renderer/viewer/src/viewer.cpp
index 79b3112..86dca8d 100644
--- a/renderer/viewer/src/viewer.cpp
+++ b/renderer/viewer/src/viewer.cpp
@@ -105,8 +105,9 @@
 	uint8_t* fileBytes = nullptr;
 	unsigned int fileBytesLength = 0;
 
-	std::string filename = "assets/polygon_party.riv";
+	// std::string filename = "assets/polygon_party.riv";
 	// std::string filename = "assets/triangle.riv";
+	std::string filename = "assets/juice.riv";
 	FILE* fp = fopen(filename.c_str(), "r");
 	fseek(fp, 0, SEEK_END);
 	fileBytesLength = ftell(fp);
diff --git a/src/math/mat2d.cpp b/src/math/mat2d.cpp
index bb6bf64..b8bd10d 100644
--- a/src/math/mat2d.cpp
+++ b/src/math/mat2d.cpp
@@ -7,7 +7,10 @@
 
 Mat2D::Mat2D() : m_Buffer{1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f} {}
 
-Mat2D::Mat2D(const Mat2D& copy) : m_Buffer{copy[0], copy[1], copy[2], copy[3], copy[4], copy[5]} {}
+Mat2D::Mat2D(const Mat2D& copy) :
+    m_Buffer{copy[0], copy[1], copy[2], copy[3], copy[4], copy[5]}
+{
+}
 
 void Mat2D::fromRotation(Mat2D& result, float rad)
 {
@@ -34,8 +37,8 @@
 
 void Mat2D::multiply(Mat2D& result, const Mat2D& a, const Mat2D& b)
 {
-	float a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4],
-	      b5 = b[5];
+	float a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5],
+	      b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5];
 	result[0] = a0 * b0 + a2 * b1;
 	result[1] = a1 * b0 + a3 * b1;
 	result[2] = a0 * b2 + a2 * b3;
@@ -124,4 +127,7 @@
 	result[1] *= sx;
 	result[2] *= sy;
 	result[3] *= sy;
-}
\ No newline at end of file
+}
+
+static Mat2D s_Transform;
+const Mat2D& Mat2D::identity() { return s_Transform; }
\ No newline at end of file