Improving GL renderer.
diff --git a/renderer/library/build/premake5.lua b/renderer/library/build/premake5.lua
index a8d16e4..a28d333 100644
--- a/renderer/library/build/premake5.lua
+++ b/renderer/library/build/premake5.lua
@@ -11,7 +11,7 @@
 
 if os.host() == "macosx" then
     links {"Cocoa.framework", "rive"}
-    defines {"PLATFORM_MACOS", "RIVE_HAS_METAL", "RIVE_HAS_OPENGL"}
+    defines {"RIVE_HAS_METAL", "RIVE_HAS_OPENGL"}
     defines {"GL_SILENCE_DEPRECATION"}
     includedirs {"../../dependencies/DiligentEngine_build/build/include"}
     files {"../src/**.mm"}
diff --git a/renderer/library/include/opengl/opengl.h b/renderer/library/include/opengl/opengl.h
index 629bc12..1f1cd48 100644
--- a/renderer/library/include/opengl/opengl.h
+++ b/renderer/library/include/opengl/opengl.h
@@ -9,8 +9,6 @@
 #include <OpenGLES/ES2/glext.h>
 #else
 #include <OpenGL/gl3.h>
-#include <OpenGL/glu.h>
-#include <OpenGL/glext.h>
 #endif
 #elif defined(__ANDROID__) || defined(ANDROID)
 #include <GLES2/gl2.h>
diff --git a/renderer/library/include/opengl/opengl_render_path.hpp b/renderer/library/include/opengl/opengl_render_path.hpp
index c704fce..050a214 100644
--- a/renderer/library/include/opengl/opengl_render_path.hpp
+++ b/renderer/library/include/opengl/opengl_render_path.hpp
@@ -20,6 +20,7 @@
 		FillRule fillRule() const { return m_FillRule; }
 
 		void stencil(OpenGLRenderer* renderer, const Mat2D& transform);
+		void cover(OpenGLRenderer* renderer, const Mat2D& transform);
 	};
 } // 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 2304d3d..4f389bd 100644
--- a/renderer/library/include/opengl/opengl_renderer.hpp
+++ b/renderer/library/include/opengl/opengl_renderer.hpp
@@ -17,6 +17,10 @@
 		GLuint m_IndexBuffer = 0;
 		GLint m_ProjectionUniformIndex = -1;
 		GLint m_TransformUniformIndex = -1;
+		GLuint m_VertexArray = 0;
+
+		/// Indices for the max sized contour, prepended with 2 triangles for
+		/// bounding boxes.
 		std::vector<unsigned short> m_Indices;
 
 	public:
diff --git a/renderer/library/src/opengl/opengl_render_path.cpp b/renderer/library/src/opengl/opengl_render_path.cpp
index 1286f6b..611cb40 100644
--- a/renderer/library/src/opengl/opengl_render_path.cpp
+++ b/renderer/library/src/opengl/opengl_render_path.cpp
@@ -23,14 +23,16 @@
 		return;
 	}
 
-	glUseProgram(renderer->program());
+	// glUseProgram(renderer->program());
 	std::size_t vertexCount;
 
 	if (isDirty())
 	{
 		computeContour();
 		vertexCount = m_ContourVertices.size();
-		renderer->updateIndexBuffer(vertexCount);
+		// We only want the indices to go from the off contour point (bounds'
+		// last point). First 4 points are bounds.
+		renderer->updateIndexBuffer(vertexCount - 3);
 
 		glBindBuffer(GL_ARRAY_BUFFER, m_ContourBuffer);
 		glBufferData(GL_ARRAY_BUFFER,
@@ -44,12 +46,15 @@
 		vertexCount = m_ContourVertices.size();
 	}
 
-	if (vertexCount < 2)
+	// 4 vertices of bounds and one for the repeated start (repeated on close so
+	// we don't need to modulate indices and share them across all paths with
+	// different contours).
+	if (vertexCount < 5)
 	{
 		return;
 	}
 
-	auto triangleCount = vertexCount - 2;
+	auto triangleCount = vertexCount - 5;
 	// printf("VCOUNT: %i E: %i\n", vertexCount, triangleCount);
 	// printf("X: %f %f\n", transform[0], transform[1]);
 	// printf("Y: %f %f\n", transform[2], transform[3]);
@@ -77,21 +82,80 @@
 	glEnableVertexAttribArray(0);
 	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * 4, (void*)0);
 
-	glDisable(GL_CULL_FACE);
-	glDisable(GL_DEPTH_TEST);
-	glEnable(GL_BLEND);
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+	// glDisable(GL_CULL_FACE);
+	// glDisable(GL_DEPTH_TEST);
+	// glEnable(GL_BLEND);
+	// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
 	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->indexBuffer());
-	glDrawElements(
-	    GL_TRIANGLES, triangleCount * 3, GL_UNSIGNED_SHORT, (void*)(0));
-	GLenum err;
-	while ((err = glGetError()) != GL_NO_ERROR)
-	{
-		// Process/log the error.
-		fprintf(stderr, "ERRR:: %i\n", err);
-	}
+	// Index buffer offset is always after first 6 (2 triangles for bounds).
+
+	// Draw the triangulated contour (triangle fans from the bottom left of the
+	// AABB) into the stencil buffer.
+	glDrawElements(GL_TRIANGLES,
+	               triangleCount * 3,
+	               GL_UNSIGNED_SHORT,
+	               (void*)(6 * sizeof(unsigned short)));
+
+	// GLenum err;
+	// while ((err = glGetError()) != GL_NO_ERROR)
+	// {
+	// 	// Process/log the error.
+	// 	fprintf(stderr, "ERRR:: %i\n", err);
+	// }
 	// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 	// static unsigned short indices[3] = {0, 2, 2};
 	// glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, &indices[0]);
+}
+
+void OpenGLRenderPath::cover(OpenGLRenderer* renderer, const Mat2D& transform)
+{
+	if (isContainer())
+	{
+		for (auto& subPath : m_SubPaths)
+		{
+			Mat2D subPathTransform;
+			Mat2D::multiply(subPathTransform, transform, subPath.transform());
+			reinterpret_cast<OpenGLRenderPath*>(subPath.path())
+			    ->stencil(renderer, subPathTransform);
+		}
+		return;
+	}
+
+	glBindBuffer(GL_ARRAY_BUFFER, m_ContourBuffer);
+	auto vertexCount = m_ContourVertices.size();
+
+	if (vertexCount < 5)
+	{
+		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};
+
+	glUniformMatrix4fv(renderer->transformUniformIndex(), 1, GL_FALSE, m4);
+
+	glEnableVertexAttribArray(0);
+	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * 4, (void*)0);
+
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->indexBuffer());
+
+	// Draw bounds.
+	glDrawElements(GL_TRIANGLES, 2 * 3, GL_UNSIGNED_SHORT, (void*)(0));
 }
\ No newline at end of file
diff --git a/renderer/library/src/opengl/opengl_renderer.cpp b/renderer/library/src/opengl/opengl_renderer.cpp
index 9313548..fa0f741 100644
--- a/renderer/library/src/opengl/opengl_renderer.cpp
+++ b/renderer/library/src/opengl/opengl_renderer.cpp
@@ -15,18 +15,17 @@
 	glDeleteShader(m_VertexShader);
 	glDeleteShader(m_FragmentShader);
 	glDeleteBuffers(1, &m_IndexBuffer);
+	glDeleteVertexArrays(1, &m_VertexArray);
 }
 
 bool OpenGLRenderer::initialize(void* data)
 {
-	fprintf(stderr, "init opengl\n");
 	assert(m_VertexShader == 0 && m_FragmentShader == 0 && m_Program == 0);
 
 	m_VertexShader =
 	    createAndCompileShader(GL_VERTEX_SHADER, vertexShaderSource);
 	if (m_VertexShader == 0)
 	{
-		fprintf(stderr, "init opengl no 1");
 		return false;
 	}
 
@@ -34,7 +33,6 @@
 	    createAndCompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
 	if (m_FragmentShader == 0)
 	{
-		fprintf(stderr, "init opengl no 2");
 		return false;
 	}
 
@@ -57,18 +55,29 @@
 
 	// Create index buffer which we'll grow and populate as necessary.
 	glGenBuffers(1, &m_IndexBuffer);
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBuffer);
 
-	// TODO: CLEANUP
-	GLuint vao;
-	glGenVertexArrays(1, &vao);
-	glBindVertexArray(vao);
-	printf("VAO: %i\n", vao);
+	// Two triangles for bounds.
+	m_Indices.emplace_back(0);
+	m_Indices.emplace_back(1);
+	m_Indices.emplace_back(2);
+	m_Indices.emplace_back(2);
+	m_Indices.emplace_back(3);
+	m_Indices.emplace_back(0);
+
+	glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+	             m_Indices.size() * sizeof(unsigned short),
+	             &m_Indices[0],
+	             GL_STATIC_DRAW);
+
+	glGenVertexArrays(1, &m_VertexArray);
+	glBindVertexArray(m_VertexArray);
+
 	glUseProgram(m_Program);
 
 	m_ProjectionUniformIndex = glGetUniformLocation(m_Program, "projection");
 	m_TransformUniformIndex = glGetUniformLocation(m_Program, "transform");
-	GLint position = glGetAttribLocation(m_Program, "position");
-	fprintf(stderr, "POSITION: %i\n", position);
+
 	float projection[16] = {0.0f};
 	orthographicProjection(projection, 0.0f, 800, 800, 0.0f, 0.0f, 1.0f);
 	modelViewProjection(projection);
@@ -85,19 +94,34 @@
 	}
 
 	auto glPath = static_cast<OpenGLRenderPath*>(path);
+
+	// Set up stencil buffer.
+	glStencilMask(0xFF);
+	glStencilFunc(GL_ALWAYS, 0x0, 0xFF);
+	glColorMask(false, false, false, false);
+	glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
+	glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
+
 	glPath->stencil(this, transform());
+
+	glColorMask(true, true, true, true);
+	glStencilFunc(GL_NOTEQUAL, 0, 0xFF);
+	glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
+
+	glPath->cover(this, transform());
 }
 
 void OpenGLRenderer::clipPath(RenderPath* path) {}
 
 void OpenGLRenderer::startFrame()
 {
-	// glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
-	// glClear(GL_COLOR_BUFFER_BIT);
 	glUseProgram(m_Program);
 	glEnableVertexAttribArray(0);
 	glUniformMatrix4fv(
 	    m_ProjectionUniformIndex, 1, GL_FALSE, m_ModelViewProjection);
+	glEnable(GL_STENCIL_TEST);
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 }
 
 void OpenGLRenderer::endFrame() {}
@@ -114,22 +138,21 @@
 	{
 		return;
 	}
-	auto edgeCount = m_Indices.size() / 3;
+	auto edgeCount = (m_Indices.size() - 6) / 3;
 	auto targetEdgeCount = contourLength - 2;
 	if (edgeCount < targetEdgeCount)
 	{
-
 		while (edgeCount < targetEdgeCount)
 		{
-			m_Indices.push_back(0);
-			m_Indices.push_back(edgeCount + 1);
-			m_Indices.push_back(edgeCount + 2);
+			m_Indices.push_back(3);
+			m_Indices.push_back(edgeCount + 4);
+			m_Indices.push_back(edgeCount + 5);
 			edgeCount++;
 		}
 
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBuffer);
 		glBufferData(GL_ELEMENT_ARRAY_BUFFER,
-		             edgeCount * 3 * sizeof(unsigned short),
+		             m_Indices.size() * sizeof(unsigned short),
 		             &m_Indices[0],
 		             GL_STATIC_DRAW);
 	}
diff --git a/renderer/library/src/opengl/opengl_shaders.cpp b/renderer/library/src/opengl/opengl_shaders.cpp
index 5e593f4..8ada16f 100644
--- a/renderer/library/src/opengl/opengl_shaders.cpp
+++ b/renderer/library/src/opengl/opengl_shaders.cpp
@@ -36,7 +36,7 @@
 
 void main()
 {
-    fragColor = vec4(0.3, 0.3, 0.3, 0.3);
+    fragColor = vec4(0.0, 0.0, 0.0, 0.35);
 
     // if (fillType == 0)
     // {
diff --git a/renderer/viewer/build/premake5.lua b/renderer/viewer/build/premake5.lua
index d047561..79d9b46 100644
--- a/renderer/viewer/build/premake5.lua
+++ b/renderer/viewer/build/premake5.lua
@@ -23,6 +23,7 @@
     links {"Cocoa.framework", "IOKit.framework", "CoreVideo.framework", "Metal.framework", "QuartzCore.framework",
            "OpenGL.framework", "glfw3"}
     defines {"RIVE_HAS_OPENGL", "RIVE_HAS_METAL"}
+    defines {"GL_SILENCE_DEPRECATION"}
     includedirs {"%{DEPENDENCIES_DIR}/include/gl3w"}
     files {"../src/**.mm"}
 end
diff --git a/renderer/viewer/src/gl.cpp b/renderer/viewer/src/gl.cpp
index e17e845..e2339d0 100644
--- a/renderer/viewer/src/gl.cpp
+++ b/renderer/viewer/src/gl.cpp
@@ -2,6 +2,16 @@
 #include "opengl/opengl_renderer.hpp"
 namespace rive
 {
-	LowLevelRenderer* makeRendererOpenGL() { return new OpenGLRenderer(); }
+	class ViewerGLRenderer : public OpenGLRenderer
+	{
+	public:
+		void startFrame() override
+		{
+			glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
+			glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+			OpenGLRenderer::startFrame();
+		}
+	};
+	LowLevelRenderer* makeRendererOpenGL() { return new ViewerGLRenderer(); }
 } // namespace rive
 #endif
\ No newline at end of file
diff --git a/renderer/viewer/src/viewer.cpp b/renderer/viewer/src/viewer.cpp
index dcca39c..79b3112 100644
--- a/renderer/viewer/src/viewer.cpp
+++ b/renderer/viewer/src/viewer.cpp
@@ -63,6 +63,7 @@
 		glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
 		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
 		glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+		glfwWindowHint(GLFW_SAMPLES, 16);
 	}
 	else
 	{
@@ -77,8 +78,6 @@
 		return -1;
 	}
 
-	void* windowHandle = nullptr;
-
 #ifdef __APPLE__
 	glfwMakeContextCurrent(window);
 	viewerNativeWindowHandle = static_cast<void*>(glfwGetCocoaWindow(window));
diff --git a/src/contour_render_path_recursive.cpp b/src/contour_render_path_recursive.cpp
index 560e512..51ab2ad 100644
--- a/src/contour_render_path_recursive.cpp
+++ b/src/contour_render_path_recursive.cpp
@@ -119,7 +119,13 @@
 	m_IsDirty = false;
 	assert(m_ContourVertices.empty());
 	RecursiveCubicSegmenter segmenter(&m_ContourVertices, m_ContourThreshold);
+
+	// First four vertices are the bounds.
 	m_ContourVertices.emplace_back(Vec2D());
+	m_ContourVertices.emplace_back(Vec2D());
+	m_ContourVertices.emplace_back(Vec2D());
+	m_ContourVertices.emplace_back(Vec2D());
+
 	for (rive::PathCommand& command : m_Commands)
 	{
 		switch (command.type())
@@ -154,9 +160,21 @@
 	segmenter.close();
 
 	// TODO: consider if there's a case with no points.
-	Vec2D& first = m_ContourVertices[0];
 	AABB::copy(m_ContourBounds, segmenter.bounds());
+	Vec2D& first = m_ContourVertices[0];
 	first[0] = m_ContourBounds.minX;
 	first[1] = m_ContourBounds.minY;
+
+	Vec2D& second = m_ContourVertices[1];
+	second[0] = m_ContourBounds.maxX;
+	second[1] = m_ContourBounds.minY;
+
+	Vec2D& third = m_ContourVertices[2];
+	third[0] = m_ContourBounds.maxX;
+	third[1] = m_ContourBounds.maxY;
+
+	Vec2D& fourth = m_ContourVertices[3];
+	fourth[0] = m_ContourBounds.minX;
+	fourth[1] = m_ContourBounds.maxY;
 }
 #endif
\ No newline at end of file