--  This file has been autogenerated. DO NOT CHANGE IT.
--

--- @module tove

--[[
*****************************************************************
TÖVE - Animated vector graphics for LÖVE.
https://github.com/poke1024/tove2d

Copyright (c) 2018, Bernhard Liebl

Distributed under the MIT license. See LICENSE file for details.

All rights reserved.
*****************************************************************
]]--

local ffi = require 'ffi'

ffi.cdef [[
typedef uint16_t tove_gpu_float_t;
// rgba32f support:
// typedef float tove_gpu_float_t;

typedef enum {
	TOVE_REPORT_DEBUG = 1,
	TOVE_REPORT_SLOW,
	TOVE_REPORT_WARN,
	TOVE_REPORT_ERR,
	TOVE_REPORT_FATAL
} ToveReportLevel;

typedef void (*ToveReportFunction)(
	const char *s, ToveReportLevel level);

typedef uint32_t ToveChangeFlags;

typedef uint32_t ToveMeshUpdateFlags;

typedef struct {
	ToveMeshUpdateFlags update;
} ToveMeshResult;

typedef enum {
	TRIANGLES_LIST,
	TRIANGLES_STRIP
} ToveTrianglesMode;

typedef enum {
	TOVE_GLSL2,
	TOVE_GLSL3
} ToveShaderLanguage;

typedef enum {
	TOVE_POINT,
	TOVE_CURVE,
	TOVE_SUBPATH,
	TOVE_PATH
} ToveElementType;

typedef uint16_t ToveVertexIndex;

typedef struct {
	ToveTrianglesMode mode;
	const ToveVertexIndex *array;
	int size;
} ToveTriangles;

typedef enum {
	PAINT_UNDEFINED = -1,
	PAINT_NONE = 0,
	PAINT_SOLID = 1,
	PAINT_SHADER = 2,
	PAINT_LINEAR_GRADIENT = 3,
	PAINT_RADIAL_GRADIENT = 4
} TovePaintType;

typedef enum {
	TOVE_FILLRULE_NON_ZERO = 0,
	TOVE_FILLRULE_EVEN_ODD = 1
} ToveFillRule;

typedef enum {
	TOVE_LINEJOIN_MITER,
	TOVE_LINEJOIN_ROUND,
	TOVE_LINEJOIN_BEVEL
} ToveLineJoin;

typedef enum {
	TOVE_LINECAP_BUTT,
	TOVE_LINECAP_ROUND,
	TOVE_LINECAP_SQUARE
} ToveLineCap;

typedef enum {
	TOVE_ORIENTATION_CW = 0,
	TOVE_ORIENTATION_CCW = 1
} ToveOrientation;

typedef enum {
	TOVE_HANDLE_FREE,
	TOVE_HANDLE_ALIGNED
} ToveHandle;

enum {
	CHANGED_FILL_STYLE = 1,
	CHANGED_LINE_STYLE = 2,
	CHANGED_POINTS = 4,
	CHANGED_GEOMETRY = 8,
	CHANGED_LINE_ARGS = 16,
	CHANGED_BOUNDS = 32,
	CHANGED_EXACT_BOUNDS = 64,
	CHANGED_RECREATE = 128,
	CHANGED_FILL_ARGS = 256,
	CHANGED_INITIAL = 1024,
	CHANGED_PAINT_INDICES = 2048,
	CHANGED_ANYTHING = 511,
	CHANGED_COLORS = CHANGED_FILL_STYLE | CHANGED_LINE_STYLE
};

enum {
	UPDATE_MESH_VERTICES = 1,
	UPDATE_MESH_COLORS = 2,
	UPDATE_MESH_TRIANGLES = 4,
	UPDATE_MESH_GEOMETRY = 8,
	UPDATE_MESH_EVERYTHING = 15,
	UPDATE_MESH_AUTO_TRIANGLES = 128
};

typedef struct {
	float x, y;
} TovePoint;

typedef struct {
	void *ptr;
} TovePaintRef;

typedef struct {
	void *ptr;
} ToveSubpathRef;

typedef struct {
	void *ptr;
} ToveCommandRef;

typedef struct {
	void *ptr;
} TovePathRef;

typedef struct {
	void *ptr;
} ToveGraphicsRef;

typedef struct {
	void *ptr;
} ToveTesselatorRef;

typedef struct {
	void *ptr;
} TovePaletteRef;

typedef struct {
	void *ptr;
} ToveNameRef;

typedef enum {
	TOVE_REC_DEPTH,
	TOVE_ANTIGRAIN,
	TOVE_MAX_ERROR
} ToveStopCriterion;

typedef struct {
	float distanceTolerance;
	float colinearityEpsilon;
	float angleEpsilon;
	float angleTolerance;
	float cuspLimit;
} AntiGrainSettings;

typedef struct {
	ToveStopCriterion stopCriterion;
	int recursionLimit;
	float arcTolerance;
	AntiGrainSettings antigrain;
	float maximumError;
	float clipperScale;
} ToveTesselationSettings;

typedef enum {
	TOVE_DITHER_NONE = 0,
	TOVE_DITHER_DIFFUSION = 1,
	TOVE_DITHER_ORDERED = 2
} ToveDitherType;

typedef struct {
	ToveDitherType type;
	const float *matrix;
	uint16_t matrix_width;
	uint16_t matrix_height;
	float spread;
} ToveDither;

typedef struct {
	float tessTolerance;
	float distTolerance;
	struct {
		ToveDither dither;
		struct {
			float amount;
			const float *matrix;
			int16_t n;
		} noise;
		TovePaletteRef palette;
	} quality;
} ToveRasterizeSettings;

typedef struct {
	float r, g, b, a;
} ToveRGBA;

typedef struct {
	ToveRGBA rgba;
	float offset;
} ToveColorStop;

typedef struct {
	float values[6];
} ToveGradientParameters;

typedef struct {
	union {
		float bounds[4];
		struct {
			float x0;
			float y0;
			float x1;
			float y1;
		};
	};
} ToveBounds;

typedef struct {
	float x, y;
} ToveVec2;

typedef struct {
	float x, y, z, w;
} ToveVec4;

typedef struct {
	float t;
	float distanceSquared;
} ToveNearest;

typedef struct {
	int16_t numPaints;
	int16_t numGradients;
	int16_t numColors;
} TovePaintColorAllocation;

typedef struct {
	int16_t numGradients;
	int16_t numColors;
	int8_t matrixRows;
	float *matrix;
	uint8_t *colorsTexture;
	int16_t colorsTextureRowBytes;
	int16_t colorsTextureHeight;
} ToveGradientData;

typedef struct {
	int8_t style; // TovePaintType
	ToveRGBA rgba;
	ToveGradientData gradient;
	void *shader;
} TovePaintData;

typedef struct {
	TovePaintData *array;
	int16_t size;
} TovePaintDataArray;

typedef struct {
	int n[2]; // number of used lookup table elements for x and y
	int bsearch; // number of needed (compound) bsearch iterations
} ToveLookupTableMeta;

typedef struct {
	int16_t curveIndex;
	int16_t numCurves;
	bool isClosed;
} ToveLineRun;

typedef struct {
	int16_t maxCurves;
	int16_t numCurves;
	int16_t numSubPaths;

	ToveBounds *bounds;
	float strokeWidth;
	float miterLimit;
	int8_t fillRule;
	bool fragmentShaderLine;
	bool opaqueLine;
	ToveLineRun *lineRuns;

	float *lookupTable[2];
	int16_t lookupTableSize;
	ToveLookupTableMeta *lookupTableMeta;

// #if TOVE_GPUX_MESH_BAND
	float *bandsVertices[3];
	int16_t numBandsVertices[3];
	int16_t maxBandsVertices;
// #endif

	uint8_t *listsTexture;
	int listsTextureRowBytes;
	int listsTextureSize[2];
	const char *listsTextureFormat;

	tove_gpu_float_t *curvesTexture;
	int curvesTextureRowBytes;
	int curvesTextureSize[2];
	const char *curvesTextureFormat;
} ToveShaderGeometryData;

typedef struct {
	TovePaintData line;
	TovePaintData fill;
} ToveShaderLineFillColorData;

typedef struct {
	ToveShaderLineFillColorData color;
	ToveShaderGeometryData geometry;
	bool hasNormalFillStrokeOrder;
} ToveShaderData;

typedef struct {
	void *ptr;
} ToveFeedRef;

typedef struct {
	void *ptr;
} ToveMeshRef;

typedef struct {
	float curvature;
	float balance;
	float angle1;
	float angle2;
} ToveCurvature;

typedef struct {
	const char *code;
	uint32_t embedded[2];
} ToveShaderCode;

typedef struct {
	// the following values are uint32_t by design: these will be unboxed,
	// will fit into Lua doubles and can act as hash table values in Lua.
	// uint64_t would not work.

	// http://luajit.org/ext_ffi_semantics.html
	//	 - Conversions from C types to Lua objects
	//   - cdata objects as table keys
	// https://stackoverflow.com/questions/43655668/
	//   are-all-integer-values-perfectly-represented-as-doubles

	// at 100 fps, version will work for > 1 year without overflowing.

	uint32_t id; // >= 1
	uint32_t version;
} ToveSendArgs;
const char *GetVersion();
void SetReportFunction(ToveReportFunction f);
void SetReportLevel(ToveReportLevel l);

TovePaletteRef NoPalette();
TovePaletteRef DefaultPalette(const char *name);
bool SetRasterizeSettings(ToveRasterizeSettings *settings, const char *algorithm,
	TovePaletteRef palette, float spread, float noise, const float *noiseMatrix, int noiseMatrixSize);
bool GenerateBlueNoise(int s, float *m);

TovePaintType PaintGetType(TovePaintRef paint);
TovePaintRef ClonePaint(TovePaintRef paint);
TovePaintRef NewEmptyPaint();
TovePaintRef NewShaderPaint(const char* s);
const ToveSendArgs *ShaderNextSendArgs(TovePaintRef shader);
int PaintGetNumColors(TovePaintRef color);
ToveColorStop PaintGetColorStop(TovePaintRef color, int i, float opacity);
TovePaintRef NewColor(float r, float g, float b, float a);
void ColorSet(TovePaintRef color, float r, float g, float b, float a);
ToveRGBA ColorGet(TovePaintRef color, float opacity);
TovePaintRef NewLinearGradient(float x1, float y1, float x2, float y2);
TovePaintRef NewRadialGradient(float cx, float cy, float fx, float fy, float r);
ToveGradientParameters GradientGetParameters(TovePaintRef gradient);
void GradientAddColorStop(TovePaintRef gradient, float offset, float r, float g, float b, float a);
void GradientSetColorStop(TovePaintRef gradient, int i, float offset, float r, float g, float b, float a);
void ReleasePaint(TovePaintRef color);

ToveSubpathRef NewSubpath();
ToveSubpathRef CloneSubpath(ToveSubpathRef subpath);
int SubpathGetNumCurves(ToveSubpathRef subpath);
int SubpathGetNumPoints(ToveSubpathRef subpath);
bool SubpathIsClosed(ToveSubpathRef subpath);
void SubpathSetIsClosed(ToveSubpathRef subpath, bool closed);
float *SubpathGetPointsPtr(ToveSubpathRef subpath);
void SubpathFixLoop(ToveSubpathRef subpath);
void SubpathSetPoint(ToveSubpathRef subpath, int index, float x, float y);
void SubpathSetPoints(ToveSubpathRef subpath, const float *pts, int npts);
float SubpathGetCurveValue(ToveSubpathRef subpath, int curve, int index);
void SubpathSetCurveValue(ToveSubpathRef subpath, int curve, int index, float value);
float SubpathGetPtValue(ToveSubpathRef subpath, int index, int dim);
void SubpathSetPtValue(ToveSubpathRef subpath, int index, int dim, float value);
void SubpathInvert(ToveSubpathRef subpath);
void SubpathSetOrientation(ToveSubpathRef subpath, ToveOrientation orientation);
void SubpathClean(ToveSubpathRef subpath, float eps);
int SubpathMoveTo(ToveSubpathRef subpath, float x, float y);
int SubpathLineTo(ToveSubpathRef subpath, float x, float y);
int SubpathCurveTo(ToveSubpathRef subpath, float cpx1, float cpy1,
	float cpx2, float cpy2, float x, float y);
int SubpathArc(ToveSubpathRef subpath, float x, float y, float r,
	float startAngle, float endAngle, bool counterclockwise);
int SubpathDrawRect(ToveSubpathRef subpath, float x, float y,
	float w, float h, float rx, float ry);
int SubpathDrawEllipse(ToveSubpathRef subpath, float x, float y, float rx, float ry);
float SubpathGetCommandValue(ToveSubpathRef subpath, int command, int property);
void SubpathSetCommandValue(ToveSubpathRef subpath, int command, int property, float value);
ToveVec2 SubpathGetPosition(ToveSubpathRef subpath, float t);
ToveVec2 SubpathGetNormal(ToveSubpathRef subpath, float t);
ToveNearest SubpathNearest(ToveSubpathRef subpath, float x, float y, float dmin, float dmax);
int SubpathInsertCurveAt(ToveSubpathRef subpath, float t);
void SubpathRemoveCurve(ToveSubpathRef subpath, int curve);
int SubpathMould(ToveSubpathRef subpath, float t, float x, float y);
bool SubpathIsLineAt(ToveSubpathRef subpath, int k, int dir);
void SubpathMakeFlat(ToveSubpathRef subpath, int k, int dir);
void SubpathMakeSmooth(ToveSubpathRef subpath, int k, int dir, float a);
void SubpathMove(ToveSubpathRef subpath, int k, float x, float y, ToveHandle handle);
void SubpathCommit(ToveSubpathRef subpath);
void SubpathSet(ToveSubpathRef subpath, ToveSubpathRef source,
	float a, float b, float c, float d, float e, float f);
ToveCurvature *SubpathSaveCurvature(ToveSubpathRef subpath);
void SubpathRestoreCurvature(ToveSubpathRef subpath);
void ReleaseSubpath(ToveSubpathRef subpath);

TovePathRef NewPath(const char *d);
TovePathRef ClonePath(TovePathRef path);
ToveSubpathRef PathBeginSubpath(TovePathRef path);
void PathAddSubpath(TovePathRef path, ToveSubpathRef traj);
void PathRemoveSubpath(TovePathRef path, ToveSubpathRef traj);
void PathSetFillColor(TovePathRef path, TovePaintRef color);
void PathSetLineColor(TovePathRef path, TovePaintRef color);
TovePaintRef PathGetFillColor(TovePathRef path);
TovePaintRef PathGetLineColor(TovePathRef path);
void PathSetLineWidth(TovePathRef path, float width);
void PathSetMiterLimit(TovePathRef path, float limit);
float PathGetMiterLimit(TovePathRef path);
void PathSetLineDash(TovePathRef path, const float *dashes, int count);
void PathSetLineDashOffset(TovePathRef path, float offset);
float PathGetLineWidth(TovePathRef path);
int PathGetNumSubpaths(TovePathRef path);
ToveSubpathRef PathGetSubpath(TovePathRef path, int i);
void PathAnimate(TovePathRef path, TovePathRef a, TovePathRef b, float t);
int PathGetNumCurves(TovePathRef path);
ToveFillRule PathGetFillRule(TovePathRef path);
void PathSetFillRule(TovePathRef path, ToveFillRule rule);
float PathGetOpacity(TovePathRef path);
void PathSetOpacity(TovePathRef path, float opacity);
void PathSetOrientation(TovePathRef path, ToveOrientation orientation);
void PathClean(TovePathRef path, float eps);
bool PathIsInside(TovePathRef path, float x, float y);
void PathSet(TovePathRef path, TovePathRef source,
	bool scaleLineWidth, float a, float b, float c, float d, float e, float f);
ToveLineJoin PathGetLineJoin(TovePathRef path);
void PathSetLineJoin(TovePathRef path, ToveLineJoin join);
ToveLineCap PathGetLineCap(TovePathRef path);
void PathSetLineCap(TovePathRef path, ToveLineCap cap);
const char *PathGetId(TovePathRef path);
void PathRefine(TovePathRef path, int factor);
void PathRotate(TovePathRef path, ToveElementType what, int k);
bool PathHasNormalFillStrokeOrder(TovePathRef path);
void ReleasePath(TovePathRef path);

ToveGraphicsRef NewGraphics(const char *svg, const char* units, float dpi);
ToveGraphicsRef CloneGraphics(ToveGraphicsRef graphics, bool deep);
TovePathRef GraphicsBeginPath(ToveGraphicsRef graphics);
void GraphicsClosePath(ToveGraphicsRef graphics);
ToveSubpathRef GraphicsBeginSubpath(ToveGraphicsRef graphics);
void GraphicsCloseSubpath(ToveGraphicsRef graphics, bool close);
void GraphicsInvertSubpath(ToveGraphicsRef graphics);
TovePathRef GraphicsGetCurrentPath(ToveGraphicsRef shape);
void GraphicsAddPath(ToveGraphicsRef shape, TovePathRef path);
void GraphicsRemovePath(ToveGraphicsRef shape, TovePathRef path);
int GraphicsGetNumPaths(ToveGraphicsRef shape);
TovePathRef GraphicsGetPath(ToveGraphicsRef shape, int i);
TovePathRef GraphicsGetPathByName(ToveGraphicsRef shape, const char *name);
ToveChangeFlags GraphicsFetchChanges(ToveGraphicsRef shape, ToveChangeFlags flags);
void GraphicsSetFillColor(ToveGraphicsRef shape, TovePaintRef color);
void GraphicsSetLineColor(ToveGraphicsRef shape, TovePaintRef color);
void GraphicsSetLineWidth(ToveGraphicsRef shape, float width);
void GraphicsSetMiterLimit(ToveGraphicsRef shape, float limit);
void GraphicsSetLineDash(ToveGraphicsRef shape, const float *dashes, int count);
void GraphicsSetLineDashOffset(ToveGraphicsRef shape, float offset);
void GraphicsFill(ToveGraphicsRef shape);
void GraphicsStroke(ToveGraphicsRef shape);
void GraphicsGetBounds(ToveGraphicsRef shape, bool exact, ToveBounds *bounds);
void GraphicsSet(ToveGraphicsRef graphics, ToveGraphicsRef source,
	bool scaleLineWidth, float a, float b, float c, float d, float e, float f);
void GraphicsRasterize(ToveGraphicsRef shape, uint8_t *pixels,
	int width, int height, int stride, float tx, float ty, float scale,
	const ToveRasterizeSettings *settings);
void GraphicsAnimate(ToveGraphicsRef shape, ToveGraphicsRef a, ToveGraphicsRef b, float t);
void GraphicsSetOrientation(ToveGraphicsRef shape, ToveOrientation orientation);
void GraphicsClean(ToveGraphicsRef shape, float eps);
TovePathRef GraphicsHit(ToveGraphicsRef graphics, float x, float y);
void GraphicsClear(ToveGraphicsRef graphics);
bool GraphicsAreColorsSolid(ToveGraphicsRef shape);
void GraphicsClearChanges(ToveGraphicsRef shape);
ToveLineJoin GraphicsGetLineJoin(ToveGraphicsRef shape);
void GraphicsSetLineJoin(ToveGraphicsRef shape, ToveLineJoin join);
bool GraphicsMorphify(const ToveGraphicsRef *graphics, int n);
void GraphicsRotate(ToveGraphicsRef graphics, ToveElementType what, int k);
void ReleaseGraphics(ToveGraphicsRef shape);

ToveFeedRef NewColorFeed(ToveGraphicsRef graphics, float scale);
ToveFeedRef NewGeometryFeed(TovePathRef path, bool enableFragmentShaderStrokes);
ToveChangeFlags FeedBeginUpdate(ToveFeedRef link);
ToveChangeFlags FeedEndUpdate(ToveFeedRef link);
ToveShaderData *FeedGetData(ToveFeedRef link);
TovePaintColorAllocation FeedGetColorAllocation(ToveFeedRef link);
void FeedBindPaintIndices(ToveFeedRef link, const ToveGradientData *data);
void ReleaseFeed(ToveFeedRef link);

ToveMeshRef NewMesh(ToveNameRef name);
ToveMeshRef NewColorMesh(ToveNameRef name);
ToveMeshRef NewPaintMesh(ToveNameRef name);
int MeshGetVertexCount(ToveMeshRef mesh);
void MeshSetVertexBuffer(
	ToveMeshRef mesh, void *buffer, int32_t size);
ToveTrianglesMode MeshGetIndexMode(ToveMeshRef mesh);
int MeshGetIndexCount(ToveMeshRef mesh);
void MeshCopyIndexData(
	ToveMeshRef mesh, void *buffer, int32_t size);
void MeshCacheKeyFrame(ToveMeshRef mesh);
void MeshSetCacheSize(ToveMeshRef mesh, int size);
void ReleaseMesh(ToveMeshRef mesh);

void ConfigureShaderCode(ToveShaderLanguage language, int matrixRows);
const char *GetPaintShaderCode(int numPaints, int numGradients);

ToveShaderCode GetGPUXFillShaderCode(
	const ToveShaderData *data, bool fragLine, bool meshBand, bool debug);
ToveShaderCode GetGPUXLineShaderCode(const ToveShaderData *data);

ToveTesselatorRef NewAdaptiveTesselator(float resolution, int recursionLimit);
ToveTesselatorRef NewRigidTesselator(int subdivisions);
ToveTesselatorRef NewAntiGrainTesselator(const AntiGrainSettings *settings);
ToveMeshUpdateFlags TesselatorTessGraphics(ToveTesselatorRef tess,
	ToveGraphicsRef graphics, ToveMeshRef mesh, ToveMeshUpdateFlags flags);
ToveMeshUpdateFlags TesselatorTessPath(ToveTesselatorRef tess,
	ToveGraphicsRef graphics, TovePathRef path,
	ToveMeshRef fillMesh, ToveMeshRef lineMesh, ToveMeshUpdateFlags flags);
void TesselatorSetMaxSubdivisions(int subdivisions);
bool TesselatorHasFixedSize(ToveTesselatorRef tess);
void ReleaseTesselator(ToveTesselatorRef tess);

TovePaletteRef NewPalette(const uint8_t *colors, int n);
void ReleasePalette(TovePaletteRef palette);

ToveNameRef NewName(const char *s);
void ReleaseName(ToveNameRef name);
ToveNameRef CloneName(ToveNameRef name);
void NameSet(ToveNameRef name, const char *s);
const char *NameCStr(ToveNameRef name);
]]

local tove = {}

tove.init = function(path)

	local dynamicLibraryPathResolver = function()

		local getSystemPathSeparator = function()
			if love.system.getOS() == "Windows" then
				return "\\"
			else
				return "/"
			end
		end

		local libName = {
			["OS X"] = "libTove.dylib",
			["Windows"] = "libTove.dll",
			["Linux"] = "libTove.so"
		}

		local envPath = os.getenv("TOVE_DYNAMIC_LIB_PREFIX")
		local fileSep = getSystemPathSeparator()

		if envPath == nil then
			local projectPrefix = love.filesystem.getSource()

			return projectPrefix .. fileSep .. "tove".. fileSep .. libName[love.system.getOS()]
		else
			return envPath .. fileSep .. libName[love.system.getOS()]
		end
	end

	libPath = dynamicLibraryPathResolver()
	local lib = ffi.load(libPath)
	tove.lib = lib
	tove.getVersion = function()
		return ffi.string(lib.GetVersion())
	end

	do
		local reportLevel

		local levels = {"debug", "slow", "warn", "err", "fatal"}
		local ilevels = {}
		local outputs = {}

		for i, n in ipairs(levels) do
			ilevels[n] = i
		end

		tove.setReportLevel = function(level)
			local l = ilevels[level]
			if l ~= nil then
				reportLevel = l
				lib.SetReportLevel(l)
			else
				print("illegal report level " .. level)
			end
		end

		tove.getReportLevel = function()
			return reportLevel
		end

		local report = function(s, l)
			l = tonumber(l)
			if l >= reportLevel then
				if outputs[l] ~= s then  -- dry
					outputs[l] = s
					print("TÖVE [" .. levels[l] .. "] " .. s)
					if l >= math.min(reportLevel + 1, lib.TOVE_REPORT_ERR) then
						print("     " .. debug.traceback())
					end
				end
			end
		end

		tove.report = report
		tove.warn = function(s)
			report(s, lib.TOVE_REPORT_WARN)
		end
		tove.slow = function(s)
			report(s, lib.TOVE_REPORT_SLOW)
		end

		lib.SetReportFunction(ffi.cast("ToveReportFunction", function(s, level)
			report(ffi.string(s), level)
		end))

		tove.setReportLevel("slow")
	end

	tove._highdpi = 1

	tove.configure = function(config)
		if config.debug ~= nil then
			tove_debug = config.debug
			tove.slow = tove.warn
		end
		if config.performancewarnings ~= nil then
			tove.slow = enabled and tove.warn or function() end
		end
		if config.highdpi ~= nil then
			tove._highdpi = config.highdpi and 2 or 1
		end
	end

	local env = {
		graphics = love.graphics.getSupported(),
		rgba16f = love.graphics.getCanvasFormats()["rgba16f"],
		matrows = 2 -- number of mat3x2 rows
	}

	do
		local _, _, _, device = love.graphics.getRendererInfo()
		if string.find(device, "Parallels") ~= nil then
			env.matrows = 4
		end
	end

	env.matsize = ffi.sizeof("float[?]", 3 * env.matrows)

	lib.ConfigureShaderCode(
		env.graphics.glsl3 and lib.TOVE_GLSL3 or lib.TOVE_GLSL2,
		env.matrows)

	function deepcopy(orig)
	    local orig_type = type(orig)
	    local copy
	    if orig_type == 'table' then
	        copy = {}
	        for orig_key, orig_value in next, orig, nil do
	            copy[deepcopy(orig_key)] = deepcopy(orig_value)
	        end
	        setmetatable(copy, deepcopy(getmetatable(orig)))
	    else -- number, string, boolean, etc
	        copy = orig
	    end
	    return copy
	end

	local _attributes = {
		cp1x = 0,
		cp1y = 1,
		cp2x = 2,
		cp2y = 3,
		x = 4,
		y = 5,
		x0 = -2,
		y0 = -1,

		w = 6,
		h = 7,

		cx = 100,
		cy = 101,
		rx = 102,
		ry = 103,
		r = 104,
	}

	local function totable(u, n)
		local t = {}
		for i = 1, n do
			t[i] = u[i - 1]
		end
		return t
	end

	tove.Transform = {}

	tove.transformed = function(source, ...)
		local args = {...}
		local t
		if type(args[1]) == "number" then
			t = love.math.newTransform(...)
		else
			t = args[1]
		end
		local a, b, _, c, d, e, _, f = t:getMatrix()
		return setmetatable({
			s = source, args = {a, b, c, d, e, f}}, tove.Transform)
	end

	tove.ipairs = function(a)
		local i = 1
		local n = a.count
		return function()
			local j = i
			if j <= n then
				i = i + 1
				return j, a[j]
			else
				return nil
			end
		end
	end

	local bit = require("bit")

	--- The visual approach with which lines are joined.

	tove.lineJoins = {
		miter = lib.TOVE_LINEJOIN_MITER,
		round = lib.TOVE_LINEJOIN_ROUND,
		bevel = lib.TOVE_LINEJOIN_BEVEL
	}

	--- The shape used to draw the end points of lines.

	tove.lineCaps = {
		butt = lib.TOVE_LINECAP_BUTT,
		round = lib.TOVE_LINECAP_ROUND,
		square = lib.TOVE_LINECAP_SQUARE
	}

	--- The rule by which solidness in paths is defined.

	tove.fillRules = {
		nonzero = lib.TOVE_FILLRULE_NON_ZERO,
		evenodd = lib.TOVE_FILLRULE_EVEN_ODD
	}

	tove.elements = {
		curve = lib.TOVE_CURVE,
		point = lib.TOVE_POINT,
		subpath = lib.TOVE_SUBPATH,
		path = lib.TOVE_PATH
	}

	local function findEnum(enums, value)
		for k, v in pairs(enums) do
			if v == value then
				return k
			end
		end
		error("illegal enum value")
	end

	tove.createBlueNoise = function(s)
		local d = love.data.newByteData(ffi.sizeof("float[?]", s * s))
		if not lib.GenerateBlueNoise(s, d:getPointer()) then d = nil end
		return d
	end

	local function warmupShader(shader)
		love.graphics.setShader(shader)
		love.graphics.rectangle("fill", 0, 0, 0, 0)
	end

	tove._str = function(name)
		return ffi.string(lib.NameCStr(name))
	end

local Paint = (function()

local _attr = {r = 1, g = 2, b = 3, a = 4, rgba = 0}

--- Colors and gradients used in fills and lines.

local Paint = {}
Paint.__index = function (self, key)
	if _attr[key] ~= nil then
		local rgba = lib.ColorGet(self, 1.0)
		if key == "rgba" then
			return {rgba.r, rgba.g, rgba.b, rgba.a}
		else
			return rgba[key]
		end
	else
		return Paint[key]
	end
end
Paint.__newindex = function(self, key, value)
	local i = _attr[key]
	if i ~= nil then
		local rgba = {self:get()}
		rgba[i] = value
		self:set(unpack(rgba))
	end
end

local function fromRef(ref)
	if ref.ptr == nil then
		return nil
	else
		return ffi.gc(ref, lib.ReleasePaint)
	end
end

Paint._fromRef = fromRef

ffi.metatype("TovePaintRef", Paint)

local noColor = fromRef(lib.NewEmptyPaint())

local newColor = function(r, g, b, a)
	if r == nil then
		return noColor
	else
		return fromRef(lib.NewColor(r, g or 0, b or 0, a or 1))
	end
end

--- Create a new color.

tove.newColor = newColor

--- Create a linear gradient.

tove.newLinearGradient = function(x0, y0, x1, y1)
	return fromRef(lib.NewLinearGradient(x0, y0, x1, y1))
end

--- Create a radial gradient.

tove.newRadialGradient = function(cx, cy, fx, fy, r)
	return fromRef(lib.NewRadialGradient(cx, cy, fx, fy, r))
end

local function unpackRGBA(rgba)
	return rgba.r, rgba.g, rgba.b, rgba.a
end

--- Get RGBA components.

function Paint:get(opacity)
	return unpackRGBA(lib.ColorGet(self, opacity or 1))
end

--- Set to RGBA.

function Paint:set(r, g, b, a)
	lib.ColorSet(self, r, g, b, a or 1)
end

--- types of paint

local paintTypes = {
	none = lib.PAINT_NONE,
	solid = lib.PAINT_SOLID,
	linear = lib.PAINT_LINEAR_GRADIENT,
	radial = lib.PAINT_RADIAL_GRADIENT,
	shader = lib.PAINT_SHADER
}

--- Query paint type.

function Paint:getType()
	local t = lib.PaintGetType(self)
	for name, enum in pairs(paintTypes) do
		if t == enum then
			return name
		end
	end
end

--- Get number of colors.

function Paint:getNumColors()
	return lib.PaintGetNumColors(self)
end

--- Get i-th color stop.

function Paint:getColorStop(i, opacity)
	local s = lib.PaintGetColorStop(self, i - 1, opacity or 1)
	return s.offset, unpackRGBA(s.rgba)
end

function Paint:getGradientParameters()
	local t = lib.PaintGetType(self)
	local v = lib.GradientGetParameters(self).values
	if t == lib.PAINT_LINEAR_GRADIENT then
		return v[0], v[1], v[2], v[3]
	elseif t == lib.PAINT_RADIAL_GRADIENT then
		return v[0], v[1], v[2], v[3], v[4]
	end
end

--- Add new color stop.

function Paint:addColorStop(offset, r, g, b, a)
	lib.GradientAddColorStop(self, offset, r, g, b, a or 1)
end

--- Clone.

function Paint:clone()
	return lib.ClonePaint(self)
end

local function exportGradientColors(paint)
	local n = paint:getNumColors()
	local colors = {}
	for i = 1, n do
		local stop = paint:getColorStop(i)
		table.insert(colors, {stop.offset, unpackRGBA(stop.rgba)})
	end
	return colors
end

local function importGradientColors(g, colors)
	for _, c in ipairs(colors) do
		g:addColorStop(unpack(c))
	end
end

function Paint:serialize()
	local t = lib.PaintGetType(self)
	if t == lib.PAINT_SOLID then
		return {type = "solid", color = {self:get()}}
	elseif t == lib.PAINT_LINEAR_GRADIENT then
		local x0, y0, x1, y1 = self:getGradientParameters()
		return {type = "linear",
			x0 = x0, y0 = y0, x1 = x1, y1 = y1,
			colors = exportGradientColors(self)}
	elseif t == lib.PAINT_RADIAL_GRADIENT then
		local cx, cy, fx, fy, r = self:getGradientParameters()
		return {type = "radial",
			cx = cx, cy = cy, fx = fx, fy = fy, r = r,
			colors = exportGradientColors(self)}
	end
	return nil
end

tove.newPaint = function(p)
	if p.type == "solid" then
		return newColor(unpack(p.color))
	elseif p.type == "linear" then
		local g = tove.newLinearGradient(p.x0, p.y0, p.x1, p.y1)
		importGradientColors(g, p.colors)
		return g
	elseif p.type == "radial" then
		local g = tove.newRadialGradient(p.cx, p.cy, p.fx, p.fy, p.r)
		importGradientColors(g, p.colors)
		return g
	end
end


local _sent = {}
tove._sent = _sent

--- Send data to custom shader.

function Paint:send(k, ...)
	local args = lib.ShaderNextSendArgs(self)
	_sent[args.id][k] = {args.version, {...}}
end

--- Create new custom shader.
--	  uniform float time; 
--	  vec4 COLOR(vec2 pos) {
--	   }

tove.newShader = function(source)
	local function releaseShader(self)
		local args = lib.ShaderNextSendArgs(self)
		_sent[args.id] = nil
		lib.ReleasePaint(self)
	end
	
	local shader = lib.NewShaderPaint(source)
	local args = lib.ShaderNextSendArgs(shader)
	_sent[args.id] = {}
	return ffi.gc(shader, releaseShader)
end


Paint._wrap = function(r, g, b, a)
	if ffi.istype("TovePaintRef", r) then
		return r
	end
	local t = type(r)
	if t == "number" then
		return newColor(r, g, b, a)
	elseif t == "string" and r:sub(1, 1) == '#' then
		r = r:gsub("#","")
		return newColor(
			tonumber("0x" .. r:sub(1, 2)) / 255,
			tonumber("0x" .. r:sub(3, 4)) / 255,
			tonumber("0x" .. r:sub(5, 6)) / 255)
	elseif t == "nil" then
		return noColor
	else
		error("tove: cannot parse color: " .. tostring(r))
	end
end

return Paint
end)()
local newCommand = (function()

--- Represents a graphics primitive, for example a circle or a curve.

--- width

--- height

--- x coordinate of center

--- y coordinate of center

--- x radius

--- y radius

--- radius

--- @section Curves

--- x coordinate of curve point P0

--- y coordinate of curve point P0

--- x coordinate of first control point P1

--- y coordinate of first control point P1

--- x coordinate of second control point P2

--- y coordinate of second control point P2

--- x coordinate of curve point P3

--- y coordinate of curve point P3

local Command = {}
Command.__index = function(self, key)
	local a = _attributes[key]
	if a ~= nil then
		return lib.SubpathGetCommandValue(self._t, self._c, a)
	else
		return Command[key]
	end
end
Command.__newindex = function(self, key, value)
	lib.SubpathSetCommandValue(
		rawget(self, "_t"), rawget(self, "_c"), _attributes[key], value)
end

function Command:commit()
	lib.SubpathCommit(self._t)
end

return function(trajectory, command)
	return setmetatable({_t = trajectory, _c = command}, Command)
end
end)()
local Subpath = (function()

--- A vector graphics subpath (sometimes called trajectory), usually lives inside a @{Path}.

--- @{tove.List} of @{Curve}s in this @{Subpath}

--- is this subpath closed?


local _pt = {
	x = 0,
	y = 1
}

local Point = {}
Point.__index = function (self, key)
	return lib.SubpathGetPtValue(self.traj, self.i, _pt[key] or -1)
end
Point.__newindex = function (self, key, value)
	lib.SubpathSetPtValue(self.traj, self.i, _pt[key] or -1, value)
end

local Points = {}
Points.__index = function (self, i)
	if i == "count" then
		return lib.SubpathGetNumPoints(self.traj)
	else
		return setmetatable({traj = self.traj, i = self.i0 + i - 1}, Point)
	end
end

local Curve = {}
Curve.__index = function (self, key)
	if key == "p" then
		return setmetatable({traj = self.traj, i0 = self.curve * 4}, Points)
	end
	local i = _attributes[key]
	if i == nil then return nil end
	return lib.SubpathGetCurveValue(
		self.traj, self.curve, i)
end
Curve.__newindex = function (self, key, value)
	local i = _attributes[key]
	if i == nil then return nil end
	return lib.SubpathSetCurveValue(
		self.traj, self.curve, i, value)
end

function Curve:split(t)
	lib.SubpathInsertCurveAt(self.traj, self.curve + t)
end

function Curve:remove()
	lib.SubpathRemoveCurve(self.traj, self.curve)
end

function Curve:isLine(dir)
	return lib.SubpathIsLineAt(self.traj, self.curve, dir or 0)
end

function Curve:makeFlat(dir)
	return lib.SubpathMakeFlat(self.traj, self.curve, dir or 0)
end

function Curve:makeSmooth(a, dir)
	return lib.SubpathMakeSmooth(self.traj, self.curve, dir or 0, a or 0.5)
end


local Curves = {}
Curves.__index = function (self, curve)
	if curve == "count" then
		return lib.SubpathGetNumCurves(self.traj)
	else
		return setmetatable({traj = self.traj, curve = curve - 1}, Curve)
	end
end


local Subpath = {}
Subpath.__index = function(self, key)
	if key == "curves" then
		return setmetatable({traj = self}, Curves)
	elseif key == "points" then
		return setmetatable({traj = self, i0 = 0}, Points)
	elseif key == "isClosed" then
		return lib.SubpathIsClosed(self)
	else
		return Subpath[key]
	end
end
Subpath.__newindex = function(self, key, value)
	if key == "isClosed" then
		lib.SubpathSetIsClosed(self, value)
	end
end

--- Evaluate position at t.

function Subpath:position(t)
	local v = lib.SubpathGetPosition(self, t)
	return v.x, v.y
end

--- Evaluate normal at t.

function Subpath:normal(t)
	local v = lib.SubpathGetNormal(self, t)
	return v.x, v.y
end

--- Find nearest point.

function Subpath:nearest(x, y, dmax, dmin)
	local n = lib.SubpathNearest(self, x, y, dmin or 1e-4, dmax or 1e50)
	return n.t, math.sqrt(n.distanceSquared)
end

Subpath.insertCurveAt = lib.SubpathInsertCurveAt
Subpath.removeCurve = lib.SubpathRemoveCurve

--- Mould curve.

Subpath.mould = lib.SubpathMould

Subpath.commit = lib.SubpathCommit

--- Move to position (x, y).

Subpath.moveTo = lib.SubpathMoveTo

--- Draw a line to (x, y).

Subpath.lineTo = lib.SubpathLineTo

--- Draw a cubic bezier curve to (x, y).

Subpath.curveTo = lib.SubpathCurveTo

local handles = {
	free = lib.TOVE_HANDLE_FREE,
	aligned = lib.TOVE_HANDLE_ALIGNED
}

function Subpath:move(k, x, y, h)
	lib.SubpathMove(self, k, x, y, h and handles[h] or lib.TOVE_HANDLE_FREE)
end

--- Draw an arc.

function Subpath:arc(x, y, r, a1, a2, ccw)
	lib.SubpathArc(self, x, y, r, a1, a2, ccw or false)
end

--- Draw a rectangle.

function Subpath:drawRect(x, y, w, h, rx, ry)
	return newCommand(self, lib.SubpathDrawRect(
		self, x, y, w, h or w, rx or 0, ry or 0))
end

--- Draw a circle.

function Subpath:drawCircle(x, y, r)
	return newCommand(self, lib.SubpathDrawEllipse(
		self, x, y, r, r))
end

--- Draw an ellipse.

function Subpath:drawEllipse(x, y, rx, ry)
	return newCommand(self, lib.SubpathDrawEllipse(
		self, x, y, rx, ry or rx))
end

function Subpath:isLineAt(k, dir)
	return lib.SubpathIsLineAt(self, k, dir or 0)
end

function Subpath:makeFlat(k, dir)
	return lib.SubpathMakeFlat(self, k, dir or 0)
end

function Subpath:makeSmooth(k, a, dir)
	return lib.SubpathMakeSmooth(self, k, dir or 0, a or 0.5)
end

function Subpath:set(arg)
	if getmetatable(arg) == tove.Transform then
		lib.SubpathSet(
			self,
			arg.s,
			unpack(arg.args))
	else
		lib.SubpathSet(
			self,
			arg,
			1, 0, 0, 0, 1, 0)
	end
end

--- Transform this @{Subpath}.

function Subpath:transform(...)
	self:set(tove.transformed(self, ...))
end

--- Invert.

function Subpath:invert()
	lib.SubpathInvert(self)
end

--- Set points.

function Subpath:setPoints(...)
	local p = {...}
	local n = #p
	local f = ffi.new("float[?]", n)
	for i = 1, n do
		f[i - 1] = p[i]
	end
	lib.SubpathSetPoints(self, f, n / 2)
end

--- Warp.

function Subpath:warp(f)
	local c = lib.SubpathSaveCurvature(self)
	local n = lib.SubpathGetNumPoints(self)
	local p = lib.SubpathGetPointsPtr(self)
	local j = 0
	for i = 0, n - 1, 3 do
		x, y, curvature = f(p[2 * i + 0], p[2 * i + 1], c[j].curvature)
		p[2 * i + 0] = x
		p[2 * i + 1] = y
		if curvature ~= nil then c[j].curvature = curvature end
		j = j + 1
	end
	lib.SubpathFixLoop(self)
	lib.SubpathRestoreCurvature(self)
end

ffi.metatype("ToveSubpathRef", Subpath)

--- Create new subpath.

tove.newSubpath = function(t)
	local self = ffi.gc(lib.NewSubpath(), lib.ReleaseSubpath)
	if type(t) == "table" and t.points ~= nil then
		self:setPoints(unpack(t.points))
		self.isClosed = t.closed
	elseif type(t) == "table" and #t > 0 then
		self:setPoints(unpack(t))
	end
	return self
end

--- Clone.

function Subpath:clone()
	return lib.CloneSubpath(self)
end

function Subpath:serialize()
	local n = lib.SubpathGetNumPoints(self)
	local p = lib.SubpathGetPointsPtr(self)
	local q = {}
	if self.isClosed then
		n = n - 1
	end
	for i = 1, n * 2 do
		q[i] = p[i - 1]
	end
	return {
		points = q,
		closed = self.isClosed
	}
end

return Subpath
end)()
local Path = (function()

--- A vector graphics path, usually lives inside a @{Graphics}.

--- @{tove.List} of @{Subpath}s in this @{Path}

--- name of this path

local Subpaths = {}
Subpaths.__index = function (self, i)
	if i == "count" then
		return lib.PathGetNumSubpaths(self.path)
	else
		return ffi.gc(lib.PathGetSubpath(self.path, i), lib.ReleaseSubpath)
	end
end

local Path = {}
Path.__index = function (path, key)
	if key == "subpaths" then
		return setmetatable({path = path}, Subpaths)
	elseif key == "id" then
		return ffi.string(lib.PathGetId(path))
	else
		return Path[key]
	end
end

--- Create a deep copy.

Path.clone = lib.ClonePath

--- Set line dash offset.

Path.setLineDashOffset = lib.PathSetLineDashOffset

--- Get line width.

Path.getLineWidth = lib.PathGetLineWidth

--- Set line width.

Path.setLineWidth = lib.PathSetLineWidth

--- Set miter limit.

Path.setMiterLimit = lib.PathSetMiterLimit

--- Get miter limit.

Path.getMiterLimit = lib.PathGetMiterLimit

--- Get opacity.

Path.getOpacity = lib.PathGetOpacity

--- Set opacity.

Path.setOpacity = lib.PathSetOpacity

--- Check if inside.

Path.inside = lib.PathIsInside

--- Refine curves.

Path.refine = lib.PathRefine

function Path:beginSubpath()
	return ffi.gc(lib.PathBeginSubpath(self), lib.ReleaseSubpath)
end

--- Add a @{Subpath}.

function Path:addSubpath(t)
	lib.PathAddSubpath(self, t)
end

--- Remove a @{Subpath}.

function Path:removeSubpath(t)
	lib.PathRemoveSubpath(self, t)
end

--- Move to position (x, y).

function Path:moveTo(x, y)
	lib.SubpathMoveTo(self:beginSubpath(), x, y)
end

--- Draw a line to (x, y).

function Path:lineTo(x, y)
	lib.SubpathLineTo(self:beginSubpath(), x, y)
end

--- Draw a cubic bezier curve to (x, y).

function Path:curveTo(...)
	lib.SubpathCurveTo(self:beginSubpath(), ...)
end

--- Get fill color.

function Path:getFillColor()
	return Paint._fromRef(lib.PathGetFillColor(self))
end

--- Get line color.

function Path:getLineColor()
	return Paint._fromRef(lib.PathGetLineColor(self))
end

--- Set fill color.

function Path:setFillColor(r, g, b, a)
	lib.PathSetFillColor(self, Paint._wrap(r, g, b, a))
end

--- Set line color.

function Path:setLineColor(r, g, b, a)
	lib.PathSetLineColor(self, Paint._wrap(r, g, b, a))
end

--- Animate between two @{Path}s.

function Path:animate(a, b, t)
	lib.PathAnimate(self, a, b, t)
end

--- Warp points.

function Path:warp(f)
	local subpaths = self.subpaths
	for i = 1, subpaths.count do
		subpaths[i]:warp(f)
	end
end

--- Rotate elements.

function Path:rotate(w, k)
	lib.PathRotate(self, tove.elements[w], k)
end

--- Find nearest point.

function Path:nearest(x, y, max, min)
	local n = lib.PathGetNumSubpaths(self)
	local subpaths = self.subpaths
	for i = 1, n do
		local traj = subpaths[i]
		local t, d = traj:nearest(x, y, max, min)
		if t >= 0 then
			return d, t, traj
		end
	end
	return false
end

function Path:set(arg, swl)
	if getmetatable(arg) == tove.Transform then
		lib.PathSet(
			self,
			arg.s,
			swl or false,
			unpack(arg.args))
	else
		lib.PathSet(
			self,
			arg,
			false, 1, 0, 0, 0, 1, 0)
	end
end

--- Transform this @{Path}.

function Path:transform(...)
	self:set(tove.transformed(self, ...))
end

--- Get line join.

function Path:getLineJoin()
	return findEnum(tove.lineJoins, lib.PathGetLineJoin(self))
end

--- Set line join.

function Path:setLineJoin(join)
	lib.PathSetLineJoin(self, tove.lineJoins[join])
end

--- Get line cap.

function Path:getLineCap()
	return findEnum(tove.lineCaps, lib.PathGetLineCap(self))
end

--- Set line cap.

function Path:setLineCap(cap)
	lib.PathSetLineCap(self, tove.lineCaps[cap])
end

--- Get fill rule.

function Path:getFillRule()
	return findEnum(tove.fillRules, lib.PathGetFillRule(self))
end

--- Set fill rule.

function Path:setFillRule(rule)
	lib.PathSetFillRule(self, tove.fillRules[rule])
end

function Path:serialize()
	local subpaths = self.subpaths
	local s = {}
	for i = 1, subpaths.count do
		s[i] = subpaths[i]:serialize()
	end
	local line = nil
	local lineColor = self:getLineColor()
	if lineColor then
		line = {
			paint = lineColor:serialize(),
			join = self:getLineJoin(),
			miter = self:getMiterLimit(),
			width = self:getLineWidth()
		}
	end
	local fill = nil
	local fillColor = self:getFillColor()
	if fillColor then
		fill = {
			paint = fillColor:serialize(),
			rule = self:getFillRule()
		}
	end
	local opacity = self:getOpacity()
	return {
		line = line,
		fill = fill,
		opacity = opacity < 1 and opacity or nil,
		subpaths = s
	}
end

function Path:deserialize(t)
	for _, s in ipairs(t.subpaths) do
		self:addSubpath(tove.newSubpath(s))
	end
	if t.line then
		self:setLineColor(tove.newPaint(t.line.paint))
		self:setLineJoin(t.line.join)
		self:setMiterLimit(t.line.miter)
		self:setLineWidth(t.line.width)
	end
	if t.fill then
		self:setFillColor(tove.newPaint(t.fill.paint))
		self:setFillRule(t.fill.rule)
	end
	self:setOpacity(t.opacity or 1)
end

ffi.metatype("TovePathRef", Path)

tove.newPath = function(data)
	local t = type(data)
	local p = ffi.gc(lib.NewPath(
		t == "string" and data or nil), lib.ReleasePath)
	if t == "table" then
		p:deserialize(data)
	end
	return p
end

return Path
end)()

local _mesh = (function()

local floatSize = ffi.sizeof("float")
local indexSize = ffi.sizeof("ToveVertexIndex")

local function getTrianglesMode(cmesh)
	return lib.MeshGetIndexMode(cmesh) == lib.TRIANGLES_LIST
		and "triangles" or "strip"
end


local AbstractMesh = {}
AbstractMesh.__index = AbstractMesh

function AbstractMesh:getNumTriangles()
	return lib.MeshGetIndexCount(self._tovemesh) / 3
end

function AbstractMesh:getUsage(what)
	return self._usage[what]
end

function AbstractMesh:updateVertices()
	local mesh = self._mesh
	if mesh ~= nil then
		local vdata = self._vdata
		lib.MeshSetVertexBuffer(
			self._tovemesh, vdata:getPointer(), vdata:getSize())
		mesh:setVertices(vdata)
	end
end

function AbstractMesh:updateTriangles()
	local mesh = self._mesh
	if mesh ~= nil then
		local indexCount = lib.MeshGetIndexCount(self._tovemesh)
		if indexCount < 1 then
			return
		end
		local size = indexCount * indexSize

		if size ~= self._idatasize then
			self._idata = love.data.newByteData(size)
			self._idatasize = size
		end

		local idata = self._idata
		lib.MeshCopyIndexData(
			self._tovemesh, idata:getPointer(), idata:getSize())

		mesh:setVertexMap(idata, indexSize == 2 and "uint16" or "uint32")
	else
		self:getMesh()
	end
end

function AbstractMesh:cacheKeyFrame()
	lib.MeshCacheKeyFrame(self._tovemesh)
end

function AbstractMesh:setCacheSize(size)
	lib.MeshSetCacheSize(self._tovemesh, size)
end

function AbstractMesh:getMesh()
	if self._mesh ~= nil then
		return self._mesh
	end

	local n = lib.MeshGetVertexCount(self._tovemesh)
	if n < 1 then
		return nil
	end

	local ni = lib.MeshGetIndexCount(self._tovemesh)
	if ni < 1 then
		return nil
	end

	local mesh = love.graphics.newMesh(
		self._attributes, n, getTrianglesMode(self._tovemesh), self:getMeshUsage())
	self._mesh = mesh
	self._vdata = love.data.newByteData(n * self._vertexByteSize)

	self:updateVertices()
	self:updateTriangles()

	return mesh
end


local PositionMesh = {}
PositionMesh.__index = PositionMesh
setmetatable(PositionMesh, {__index = AbstractMesh})
PositionMesh._attributes = {{"VertexPosition", "float", 2}}
PositionMesh._vertexByteSize = 2 * floatSize

function PositionMesh:getMeshUsage()
	return self._usage.points or "static"
end

tove.newPositionMesh = function(name, usage)
	return setmetatable({
		_name = name,
		_tovemesh = ffi.gc(lib.NewMesh(name), lib.ReleaseMesh),
		_mesh = nil,
		_usage = usage or {},
		_vdata = nil}, PositionMesh)
end


local PaintMesh = {}
PaintMesh.__index = PaintMesh
setmetatable(PaintMesh, {__index = AbstractMesh})
PaintMesh._attributes = {
	{"VertexPosition", "float", 2},
	{"VertexPaint", "byte", 4}}
PaintMesh._vertexByteSize = 3 * floatSize

function PaintMesh:getMeshUsage()
	return self._usage.points or "static"
end

tove.newPaintMesh = function(name, usage)
	return setmetatable({
		_name = name,
		_tovemesh = ffi.gc(lib.NewPaintMesh(name), lib.ReleaseMesh),
		_mesh = nil,
		_usage = usage or {},
		_vdata = nil}, PaintMesh)
end


local ColorMesh = {}
ColorMesh.__index = ColorMesh
setmetatable(ColorMesh, {__index = AbstractMesh})
ColorMesh._attributes = {
	{"VertexPosition", "float", 2},
	{"VertexColor", "byte", 4}}
ColorMesh._vertexByteSize = 2 * floatSize + 4

function ColorMesh:getMeshUsage()
	local u = self._usage
	local p = u.points
	local c = u.colors
	if p == "stream" or c == "stream" then
		return "stream"
	elseif p == "dynamic" or c == "dynamic" then
		return "dynamic"
	else
		return "static"
	end
end

tove.newColorMesh = function(name, usage, tess)
	local cmesh = ffi.gc(lib.NewColorMesh(name), lib.ReleaseMesh)
	tess(cmesh, -1)
	return setmetatable({
		_name = name, _tovemesh = cmesh, _mesh = nil,
		_tess = tess, _usage = usage,
		_vdata = nil}, ColorMesh)
end

function ColorMesh:retesselate(flags)
	local updated = self._tess(self._tovemesh, flags)
	if bit.band(updated, lib.UPDATE_MESH_GEOMETRY) ~= 0 then
		self._mesh = nil
		return true  -- the underlying mesh changed
	end

	if bit.band(updated,
		lib.UPDATE_MESH_VERTICES +
		lib.UPDATE_MESH_COLORS) ~= 0 then
		self:updateVertices()
	end
	if bit.band(updated, lib.UPDATE_MESH_TRIANGLES) ~= 0 then
		self:updateTriangles()
	end

	return false
end
end)()
local _shaders = (function()

local lg = love.graphics

local function createSendSync(shader, code, sync)
	local embedded = code.embedded
	local sent = {}
	for i = 0, 1 do
		local k = embedded[i]
		if k ~= 0 then
			table.insert(sent, tove._sent[k])
		end
	end
	if #sent == 0 then
		return sync
	else
		local version = 0
		local max = math.max
		return function()
			local nversion = version
			for _, s in ipairs(sent) do
				for name, values in pairs(s) do
					local v = values[1]
					if v > version then
						nversion = max(nversion, v)
						shader:send(name, unpack(values[2]))
					end
				end
			end
			version = nversion
			if sync ~= nil then
				sync()
			end
		end
	end
end

local function newGeometryFillShader(data, sync, fragLine, meshBand, debug)
	local geometry = data.geometry

	local code = lib.GetGPUXFillShaderCode(
		data, fragLine, meshBand, debug)

	local shader = lg.newShader(ffi.string(code.code))

	shader:send("constants", {geometry.maxCurves,
		geometry.listsTextureSize[0], geometry.listsTextureSize[1]})

	return shader, createSendSync(shader, code, sync)
end

local function newGeometryLineShader(data, sync)
	local style = data.color.line.style
	if style < 1 then
		return nil
	end

	local code = lib.GetGPUXLineShaderCode(data)

	local shader = lg.newShader(ffi.string(code.code))

	shader:send("max_curves", data.geometry.maxCurves)

	return shader, createSendSync(shader, code, sync)
end

local function newPaintShader(numPaints, numGradients)
	return lg.newShader(ffi.string(lib.GetPaintShaderCode(numPaints, numGradients)))
end

local feed = (function()

local function fillGradientData(gradient)
	local n = gradient.numColors

	local matrixData = love.data.newByteData(env.matsize)
	gradient.matrix = matrixData:getPointer()

	local imageData = love.image.newImageData(1, n, "rgba8")
	gradient.numGradients = 1
	gradient.colorsTexture = imageData:getPointer()
	gradient.colorsTextureRowBytes = imageData:getSize() / n
	gradient.colorsTextureHeight = n

	local s = 0.5 / n
	return {numColors = n, matrixData = matrixData,
		imageData = imageData, texture = nil, cscale = {s, 1 - 2 * s}}
end

local function newGradientTexture(d)
	d.texture = lg.newImage(d.imageData)
	d.texture:setFilter("nearest", "linear")
	d.texture:setWrap("clamp", "clamp")
end

local function reloadGradientTexture(d)
	d.texture:replacePixels(d.imageData)
end

local function sendColor(shader, uniform, rgba)
	shader:send(uniform, {rgba.r, rgba.g, rgba.b, rgba.a})
end

local function makeSendLUT(feed, shader)
	local bounds = feed.boundsByteData
	local lutX, lutY = unpack(feed.lookupTableByteData)
	local n = feed.data.lookupTableMeta.n
	local floatSize = ffi.sizeof("float[?]", 1)
	local meta = feed.lookupTableMetaByteData

	return function()
		shader:send("bounds", bounds)
		shader:send("lutX", lutX, 0, n[0] * floatSize)
		shader:send("lutY", lutY, 0, n[1] * floatSize)
		shader:send("tablemeta", meta)
	end
end

local function sendLineArgs(shader, data)
	shader:send("lineargs", {data.strokeWidth / 2, data.miterLimit})
end


local NullColorFeed = {}
NullColorFeed.__index = NullColorFeed

function NullColorFeed:beginInit()
end
function NullColorFeed:endInit(path)
end
function NullColorFeed:updateUniforms(chg1, path)
end


local ColorSend = {}
ColorSend.__index = ColorSend

local _flags = {
	fill = lib.CHANGED_FILL_STYLE,
	line = lib.CHANGED_LINE_STYLE
}

local _uniforms = {
	fill = {},
	line = {}
}

for _, name in ipairs({"color", "colors", "matrix", "cscale"}) do
	_uniforms.fill[name] = "fill" .. name
	_uniforms.line[name] = "line" .. name
end

local function newColorSend(shader, type, colorData)
	if shader == nil then
		return setmetatable({}, NullColorFeed)
	end
	return setmetatable({shader = shader, colorData = colorData,
		uniforms = _uniforms[type], flag = _flags[type]}, ColorSend)
end

function ColorSend:beginInit()
	local colorData = self.colorData
	if colorData.style >= lib.PAINT_LINEAR_GRADIENT then
		self.gradientData = fillGradientData(colorData.gradient)
	end
end

function ColorSend:endInit(path)
	local gradientData = self.gradientData

	if gradientData ~= nil then
		newGradientTexture(gradientData)
	end

	local colorData = self.colorData
	local shader = self.shader
	local uniforms = self.uniforms

	if colorData.style == lib.PAINT_SOLID then
		sendColor(shader, uniforms.color, colorData.rgba)
	elseif colorData.style >= lib.PAINT_LINEAR_GRADIENT then
		shader:send(uniforms.colors, gradientData.texture)
		shader:send(uniforms.matrix, gradientData.matrixData)
		shader:send(uniforms.cscale, gradientData.cscale)
	end
end

function ColorSend:updateUniforms(chg1, path)
	if bit.band(chg1, self.flag) ~= 0 then
		local shader = self.shader
		local uniforms = self.uniforms
		local colorData = self.colorData
		if colorData.style == lib.PAINT_SOLID then
			sendColor(shader, uniforms.color, colorData.rgba)
		elseif colorData.style >= lib.PAINT_LINEAR_GRADIENT then
			local gradientData = self.gradientData
			reloadGradientTexture(gradientData)
			shader:send(uniforms.matrix, gradientData.matrixData)
			shader:send(uniforms.cscale, gradientData.cscale)
		end
	end
end



local MeshBands = {}
MeshBands.__index = MeshBands

MeshBands.FLOATS_PER_VERTEX = {4, 2, 4}

local function newMeshBands(data)
	return setmetatable({
		data = data,
		meshes = {nil, nil, nil},
		bandsVertexByteData = {nil, nil, nil},
		bandsVertexViewCache = {{}, {}, {}}
	}, MeshBands)
end

function MeshBands:initData()
	local data = self.data
	for i = 1, 3 do
		local bandDataSize = ffi.sizeof(
			"float[?]", data.maxBandsVertices * MeshBands.FLOATS_PER_VERTEX[i])
		local bandsByteData = love.data.newByteData(bandDataSize)
		self.bandsVertexByteData[i] = bandsByteData
		data.bandsVertices[i - 1] = bandsByteData:getPointer()
	end
end

function MeshBands:createMeshes(fillShader)
	local data = self.data

	self.meshes[1] = love.graphics.newMesh(
		{{"VertexPosition", "float", 2}, {"VertexTexCoord", "float", 2}},
		data.maxBandsVertices,
		"triangles")

	self.meshes[2] = love.graphics.newMesh(
		{{"VertexPosition", "float", 2}},
		data.maxBandsVertices,
		"triangles")

	self.meshes[3] = love.graphics.newMesh(
		{{"VertexPosition", "float", 2}, {"VertexTexCoord", "float", 2}},
		data.maxBandsVertices,
		"triangles")
	
	local lg = love.graphics

	local drawUnsafeBands = function(...)
		lg.setShader()
		lg.draw(self.meshes[2], ...)
	end

	return function(...)
		lg.stencil(drawUnsafeBands, "replace", 1)
	
		lg.setShader(fillShader)
		lg.draw(self.meshes[1], ...)
		lg.setStencilTest("greater", 0)
		lg.draw(self.meshes[3], ...)
		lg.setStencilTest()
	end
end

local function bandsGetVertexView(self, i)
	local size = self.data.numBandsVertices[i - 1]
	local cache = self.bandsVertexViewCache[i]
	local view = cache[size]
	if view == nil and size > 0 then
		view = love.data.newDataView(
			self.bandsVertexByteData[i], 0, ffi.sizeof(
				"float[?]", size * MeshBands.FLOATS_PER_VERTEX[i]))
		cache[size] = view
	end
	return view, size
end

function MeshBands:update()
	for i, mesh in ipairs(self.meshes) do
		local view, n = bandsGetVertexView(self, i)
		if n > 0 then
			mesh:setDrawRange(1, n)
			mesh:setVertices(view)
		end
	end
end


local GeometrySend = {}
GeometrySend.__index = GeometrySend

local function newGeometrySend(fillShader, lineShader, data, meshBand)
	return setmetatable({
		fillShader = fillShader,
		lineShader = lineShader,
		numLineSegments = 0,
		data = data,
		bands = meshBand and newMeshBands(data) or nil,
		boundsByteData = nil,
		listsImageData = nil,
		curvesImageData = nil,
		lookupTableByteData = {nil, nil},
		lookupTableMetaByteData = nil,
		_warmup = false}, GeometrySend)
end

function GeometrySend:warmup()
	if not self._warmup then
		if self.fillShader ~= nil then
			warmupShader(self.fillShader)
		end
		if self.lineShader ~= nil and self.lineShader ~= self.fillShader then
			warmupShader(self.lineShader)
		end
		self._warmup = true
		return true
	else
		return false
	end
end

function GeometrySend:beginInit()
	local data = self.data


	self.boundsByteData = love.data.newByteData(ffi.sizeof("ToveBounds"))
	data.bounds = self.boundsByteData:getPointer()

	local listsImageData = love.image.newImageData(
		data.listsTextureSize[0], data.listsTextureSize[1],
		ffi.string(data.listsTextureFormat))
	data.listsTextureRowBytes = listsImageData:getSize() / data.listsTextureSize[1]

	local curvesImageData = love.image.newImageData(
		data.curvesTextureSize[0], data.curvesTextureSize[1],
		ffi.string(data.curvesTextureFormat))
	data.curvesTextureRowBytes = curvesImageData:getSize() / data.curvesTextureSize[1]

	data.listsTexture = listsImageData:getPointer()
	data.curvesTexture = curvesImageData:getPointer()

	self.listsImageData = listsImageData
	self.curvesImageData = curvesImageData

	for i = 1, 2 do
		local lutData = love.data.newByteData(
			ffi.sizeof("float[?]", data.lookupTableSize))
		self.lookupTableByteData[i] = lutData
		data.lookupTable[i - 1] = lutData:getPointer()
	end

	self.lookupTableMetaByteData = love.data.newByteData(
		ffi.sizeof("ToveLookupTableMeta"))
	data.lookupTableMeta = self.lookupTableMetaByteData:getPointer()

	if self.bands ~= nil then
		self.bands:initData()
	end
end

local function makeDrawLine(feed)
	local lineShader = feed.lineShader
	local geometry = feed.data
	local lg = love.graphics

	local lineMesh = love.graphics.newMesh(
		{{0, -1}, {0, 1}, {1, -1}, {1, 1}}, "strip")
	local lineJoinMesh = love.graphics.newMesh(
		{{0, 0}, {1, -1}, {1, 1}, {-1, -1}, {-1, 1}}, "strip")

	local drawLine = function(...)
		local lineRuns = geometry.lineRuns
		if lineRuns == nil then
			return
		end

		lg.setShader(lineShader)

		local numSegments = feed.numLineSegments
		for i = 0, geometry.numSubPaths - 1 do
			local run = lineRuns[i]
			local numCurves = run.numCurves
			local numInstances = numSegments * numCurves

			lineShader:send("curve_index", run.curveIndex)
			lineShader:send("num_curves", numCurves)

			lineShader:send("draw_mode", 0)
			lg.drawInstanced(lineMesh, numInstances, ...)

			lineShader:send("draw_mode", 1)
			lg.drawInstanced(lineJoinMesh,
				numCurves - (run.isClosed and 0 or 1), ...)
		end
	end

	local bounds = ffi.cast("ToveBounds*", geometry.bounds)

	local drawTransparentLine = function(...)
		local border = geometry.strokeWidth + geometry.miterLimit * 2
		lg.stencil(drawLine, "replace", 1)
		lg.setStencilTest("greater", 0)
		local x0, y0 = bounds.x0, bounds.y0
		lineShader:send("draw_mode", 2)
		lg.rectangle("fill",
			x0 - border, y0 - border,
			bounds.x1 - x0 + 2 * border,
			bounds.y1 - y0 + 2 * border)
		lg.setStencilTest()
	end
	
	return function(...)
		if geometry.opaqueLine then
			drawLine(...)
		else
			drawTransparentLine()
		end
	end
end

function GeometrySend:endInit(lineStyle)
	local listsTexture = love.graphics.newImage(self.listsImageData)
	local curvesTexture = love.graphics.newImage(self.curvesImageData)
	listsTexture:setFilter("nearest", "nearest")
	listsTexture:setWrap("clamp", "clamp")
	curvesTexture:setFilter("nearest", "nearest")
	curvesTexture:setWrap("clamp", "clamp")
	self.listsTexture = listsTexture
	self.curvesTexture = curvesTexture

	local fillShader = self.fillShader
	local lineShader = self.lineShader
	local data = self.data

	if fillShader == nil then
		self.drawFill = function() end
	else
		if self.bands == nil then
			local mesh = love.graphics.newMesh(
				{{"VertexPosition", "float", 2}},
				{{0, 0}, {0, 1}, {1, 0}, {1, 1}}, "strip")

			local lg = love.graphics

			self.drawFill = function(...)
				lg.setShader(fillShader)
				lg.draw(mesh, ...)
			end
		else
			self.drawFill = self.bands:createMeshes(fillShader)
		end
	end

	if fillShader ~= lineShader and lineShader ~= nil then
		self.drawLine = makeDrawLine(self)
	else
		self.drawLine = function() end
	end

	if self.bands == nil then
		if fillShader ~= nil then
			self._sendUniforms = makeSendLUT(self, fillShader)
		else
			self._sendUniforms = function() end
		end
	else
		self._sendUniforms = function() self.bands:update() end
	end

	self._sendUniforms()

	if fillShader ~= nil then
		fillShader:send("lists", listsTexture)
		fillShader:send("curves", curvesTexture)
	end

	if lineShader ~= fillShader and lineShader ~= nil then
		lineShader:send("curves", curvesTexture)
	end

	if lineStyle > 0 and lineShader ~= nil then
		sendLineArgs(lineShader, data)
	end
end

function GeometrySend:updateUniforms(flags)
	if bit.band(flags, lib.CHANGED_POINTS) ~= 0 then
		self._sendUniforms()

		if bit.band(flags, lib.CHANGED_LINE_ARGS) ~= 0 then
			local fillShader = self.fillShader
			local lineShader = self.lineShader

			if lineShader ~= nil then
				sendLineArgs(lineShader, self.data)
			elseif fillShader ~= nil then
				sendLineArgs(fillShader, self.data)
			end
		end

		self.listsTexture:replacePixels(self.listsImageData)
		self.curvesTexture:replacePixels(self.curvesImageData)
	end
end

return {
	newColorSend = newColorSend,
	newGeometrySend = newGeometrySend
}
end)()

local MeshShader = {}
MeshShader.__index = MeshShader

local function newMeshFeedData(name, graphics, tess, usage, resolution)
	local mesh = tove.newPaintMesh(name, usage)
	tess(mesh._tovemesh, lib.UPDATE_MESH_EVERYTHING)

	local link = ffi.gc(
		lib.NewColorFeed(graphics._ref, resolution), lib.ReleaseFeed)

	local alloc = lib.FeedGetColorAllocation(link)
	local matrixData = love.data.newByteData(
		(1 + alloc.numGradients) * env.matsize)
	local imageData = love.image.newImageData(
		alloc.numPaints, alloc.numColors, "rgba8")
	local colorsTexture = lg.newImage(imageData)
	local gradientData = ffi.new("ToveGradientData")
	gradientData.numColors = alloc.numColors
	gradientData.numGradients = alloc.numGradients
	gradientData.matrix = matrixData:getPointer()
	gradientData.matrixRows = env.matrows
	gradientData.colorsTexture = imageData:getPointer()
	gradientData.colorsTextureRowBytes = imageData:getSize() / alloc.numColors
	gradientData.colorsTextureHeight = alloc.numColors
	lib.FeedBindPaintIndices(link, gradientData)
	local paintShader = newPaintShader(alloc.numPaints, alloc.numGradients)
	colorsTexture:setFilter("nearest", "linear")
	colorsTexture:setWrap("clamp", "clamp")

	lib.FeedEndUpdate(link)

	colorsTexture:replacePixels(imageData)
	paintShader:send("matrix", matrixData)
	paintShader:send("colors", colorsTexture)
	paintShader:send("cstep", 0.5 / gradientData.colorsTextureHeight)

	lib.GraphicsClearChanges(graphics._ref)

	return {
		link = link,
		mesh = mesh,
		shader = paintShader,

		matrixData = matrixData,
		imageData = imageData,
		colorsTexture = colorsTexture
	}
end

local newMeshShader = function(name, graphics, tess, usage, resolution)
	return setmetatable({
		graphics = graphics, tess = tess, usage = usage, _name = name,
		resolution = resolution,
		linkdata = newMeshFeedData(
			name, graphics, tess, usage, resolution)}, MeshShader)
end

function MeshShader:getMesh()
	return self.linkdata.mesh
end

function MeshShader:recreate()
	tove.warn("full mesh recreation triggered in " .. tove._str(self._name))
	self.linkdata = newMeshFeedData(
		self.name, self.graphics, self.tess, self.usage, self.resolution)
end

function MeshShader:update()
	local linkdata = self.linkdata
	local graphics = self.graphics

	local flags = graphics:fetchChanges(lib.CHANGED_POINTS + lib.CHANGED_GEOMETRY)

	if bit.band(flags, lib.CHANGED_GEOMETRY) ~= 0 then
		self:recreate()
		return
	elseif bit.band(flags, lib.CHANGED_POINTS) ~= 0 then
		if self.usage["points"] == "static" then
			tove.slow("static mesh points changed in " .. tove._str(self._name))
			self:recreate()
			return
		end

		local tessFlags = lib.UPDATE_MESH_VERTICES
		if self.usage["triangles"] ~= "static" then
			tessFlags = bit.bor(tessFlags, lib.UPDATE_MESH_AUTO_TRIANGLES)
		end
		local updated = self.tess(linkdata.mesh._tovemesh, tessFlags)

		linkdata.mesh:updateVertices()
		if bit.band(updated, lib.UPDATE_MESH_TRIANGLES) ~= 0 then
			linkdata.mesh:updateTriangles()
		end
	end

	local link = linkdata.link
	local chg1 = lib.FeedBeginUpdate(link)

	if chg1 ~= 0 then
		if bit.band(chg1, lib.CHANGED_RECREATE) ~= 0 then
			self:recreate()
			return
		end

		lib.FeedEndUpdate(link)
		linkdata.colorsTexture:replacePixels(linkdata.imageData)
		local shader = linkdata.shader
		shader:send("matrix", linkdata.matrixData)
	end
end

function MeshShader:draw(...)
	local linkdata = self.linkdata
	local mesh = linkdata.mesh:getMesh()
	if mesh ~= nil then
		lg.setShader(linkdata.shader)
		lg.draw(mesh, ...)
		lg.setShader(nil)
	end
end

function MeshShader:warmup(...)
	warmupShader(self.linkdata.shader)
end

local function setLineQuality(linkData, lineQuality)
	linkData.lineQuality = lineQuality
	if linkData.data.color.line.style > 0 then
		if linkData.lineShader == linkData.fillShader then
			linkData.fillShader:send("line_quality", lineQuality)
		else
			local numSegments = math.max(2, math.ceil(50 * lineQuality))
			linkData.lineShader:send("segments_per_curve", numSegments)
			linkData.geometryFeed.numLineSegments = numSegments
		end
	end
end

local ComputeShader = {}
ComputeShader.__index = ComputeShader

local function parseQuality(q)
	local lineType = "fragment"
	local lineQuality = 1.0
	if type(q) == "table" then
		lineType = q[1] or "fragment"
		lineQuality = q[2] or 1.0
		if lineType == "vertex" then
			if not env.graphics.instancing then
				tove.warn("falling back on fragment line mode.")
				lineType = "fragment"
				lineQuality = 1
			end
		elseif lineType ~= "fragment" then
			error("line type must be \"fragment\" or \"vertex\".")
		end
	end
	return lineType, lineQuality
end

local function newComputeFeedData(path, quality, debug)
	local lineType, lineQuality = parseQuality(quality)
	local fragLine = (lineType == "fragment")
	local syncShader = nil

	local meshBand = false

	local link = ffi.gc(
		lib.NewGeometryFeed(path, fragLine), lib.ReleaseFeed)
	local data = lib.FeedGetData(link)

	lib.FeedBeginUpdate(link)

	local fillShader = nil
	if fragLine or data.color.fill.style > 0 then
		fillShader, syncShader = newGeometryFillShader(
			data, nil, fragLine, meshBand, debug or false)
	end

	local lineShader
	if fragLine then
		lineShader = fillShader
	else
		lineShader, syncShader = newGeometryLineShader(data, syncShader)
	end

	local lineColorSend = feed.newColorSend(
		lineShader, "line", data.color.line)
	lineColorSend:beginInit()
	local fillColorSend = feed.newColorSend(
		fillShader, "fill", data.color.fill)
	fillColorSend:beginInit()

	local geometryFeed = feed.newGeometrySend(
		fillShader, lineShader, data.geometry, meshBand)
	geometryFeed:beginInit()

	lib.FeedEndUpdate(link)

	lineColorSend:endInit(path)
	fillColorSend:endInit(path)
	geometryFeed:endInit(data.color.line.style)

	local linkData = {
		link = link,
		data = data,
		fillShader = fillShader,
		lineShader = lineShader,
		syncShader = syncShader,
		lineType = lineType,
		quality = quality,
		lineQuality = lineQuality,
		geometryFeed = geometryFeed,
		lineColorSend = lineColorSend,
		fillColorSend = fillColorSend,
		paintOrder = lib.PathHasNormalFillStrokeOrder(path)
	}
	setLineQuality(linkData, lineQuality)
	return linkData
end

local newComputeShader = function(path, quality)
	return setmetatable({
		path = path,
		linkdata = newComputeFeedData(path, quality)
	}, ComputeShader)
end

function ComputeShader:update()
	local path = self.path
	local linkdata = self.linkdata
	local link = linkdata.link

	local sync = linkdata.syncShader
	if sync ~= nil then sync() end

	local chg1 = lib.FeedBeginUpdate(link)

	if bit.band(chg1, lib.CHANGED_RECREATE) ~= 0 then
		self.linkdata = newComputeFeedData(path, linkdata.quality)
		return
	end

	local chg2 = lib.FeedEndUpdate(link)

	linkdata.lineColorSend:updateUniforms(chg1, path)
	linkdata.fillColorSend:updateUniforms(chg1, path)

	linkdata.geometryFeed:updateUniforms(chg2)
end

function ComputeShader:updateQuality(quality)
	local linkdata = self.linkdata
	linkdata.quality = quality
	local lineType, lineQuality = parseQuality(quality)
	if lineType == linkdata.lineType then
		setLineQuality(linkdata, lineQuality)
		return true
	else
		return false
	end
end

function ComputeShader:draw(...)
	local linkdata = self.linkdata
	local feed = linkdata.geometryFeed
	if linkdata.paintOrder then
		feed.drawFill(...)
		feed.drawLine(...)
	else
		feed.drawLine(...)
		feed.drawFill(...)
	end
end

function ComputeShader:warmup(...)
	return self.linkdata.geometryFeed:warmup(...)
end

function ComputeShader:debug(curve)
	if curve == "off" then
		self.linkdata = newComputeFeedData(
			self.path, self.linkdata.quality, false)
		self.debugging = nil
		return
	end
	if self.debugging == nil then
		self.debugging = true

		self.linkdata = newComputeFeedData(
			self.path, self.linkdata.quality, true)
	end
	self.linkdata.fillShader:send("debug_curve", curve)	
end

return {
	newMeshShader = newMeshShader,
	newComputeShader = newComputeShader
}
end)()
local Graphics = (function()

--- A vector graphics.

local createTesselator = (function()

tove.newAdaptiveTesselator = function(resolution, recursionLimit)
	return ffi.gc(lib.NewAdaptiveTesselator(
		resolution or 128, recursionLimit or 8), lib.ReleaseTesselator)
end

tove.newRigidTesselator = function(subdivisions)
	return ffi.gc(lib.NewRigidTesselator(
		subdivisions), lib.ReleaseTesselator)
end

return function (usage, quality, ...)
	local t = type(quality)
	if t == "string" then
		if quality == "rigid" then
			return tove.newRigidTesselator(...)
		elseif quality == "adaptive" then
			return tove.newAdaptiveTesselator(...)
		else
			error("tesselator must be \"rigid\" or \"adaptive\".")			
		end
	elseif t == "number" then
		if usage["points"] ~= "static" then
			return tove.newRigidTesselator(
				math.log(quality) / math.log(2), "cw")
		else
			return tove.newAdaptiveTesselator(quality)
		end
	elseif t == "nil" then
		if usage["points"] ~= "static" then
			return tove.newRigidTesselator(4, "cw")
		else
			return tove.newAdaptiveTesselator(128)
		end
	else
		return quality
	end
end
end)()

local function gcpath(p)
	return p and ffi.gc(p, lib.ReleasePath)
end

---@{tove.List} of @{Path}s in this @{Graphics}

local Paths = {}
Paths.__index = function (self, i)
	if i == "count" then
		return lib.GraphicsGetNumPaths(self._ref)
	else
		local t = type(i)
		if t == "number" then
			return gcpath(lib.GraphicsGetPath(self._ref, i))
		elseif t == "string" then
			return gcpath(lib.GraphicsGetPathByName(self._ref, i))
		end
	end
end

local Graphics = {}
Graphics.__index = Graphics

local function bind(methodName, libFuncName)
	Graphics[methodName] = function(self, ...)
		lib[libFuncName](self._ref, ...)
		return self
	end
end

local function newUsage()
	return {points = "static", colors = "static"}
end

--- Create a new Graphics.
tove.newGraphics = function(data, size)
	local svg = nil
	if type(data) == "string" then
		svg = data
	end
	local name = "unnamed"
	if tove.getReportLevel() == lib.TOVE_REPORT_DEBUG then
		name = "Graphics originally created at " .. debug.traceback()
	end
	local ref = ffi.gc(lib.NewGraphics(svg, "px", 72), lib.ReleaseGraphics)
	local graphics = setmetatable({
		_ref = ref,
		_cache = nil,
		_display = {mode = "texture", quality = {}},
		_resolution = 1,
		_usage = newUsage(),
		_name = ffi.gc(lib.NewName(name), lib.ReleaseName),
		paths = setmetatable({_ref = ref}, Paths)}, Graphics)
	if type(data) == "table" then
		for _, p in ipairs(data.paths) do
			graphics:addPath(tove.newPath(p))
		end
		mode, quality = unpack(data.display)
		graphics:setDisplay(mode, unpack(quality))
		for k, v in pairs(data.usage) do
			graphics:setUsage(k, v)
		end
	end
	if data then
		if size == "copy" then
		elseif type(size) == "number" then
			graphics:rescale(size)
		elseif size == nil or size == "auto" then
			local x0, y0, x1, y1 = graphics:computeAABB()
			graphics:rescale(math.min(1024, math.max(x1 - x0, y1 - y0)))
		end
	end
	return graphics
end

--- Remove all @{Path}s.
function Graphics:clear()
	lib.GraphicsClear(self._ref)
end

local function attachTesselator(display, usage)
	if display.mode == "mesh" then
		display.tesselator = createTesselator(usage, unpack(display.quality))
	end
	return display
end

local function makeDisplay(mode, quality, usage)
	local clonedQuality = quality
	if type(quality) == "table" then
		clonedQuality = deepcopy(quality)
	else
		quality = quality or 1
	end
	return attachTesselator({mode = mode, quality = clonedQuality,
		tesselator = nil}, usage)
end

--- Set name.
function Graphics:setName(name)
	lib.NameSet(self._name, name)
end

--- Create a deep copy.
function Graphics:clone()
	local ref = lib.CloneGraphics(self._ref, true)
	local d = self._display
	local g = setmetatable({
		_ref = ref,
		_cache = nil,
		_display = makeDisplay(d.mode, d.quality, self._usage),
		_resolution = self._resolution,
		_usage = newUsage(),
		_name = ffi.gc(lib.CloneName(self._name), lib.ReleaseName),
		paths = setmetatable({_ref = ref}, Paths)}, Graphics)
	for k, v in pairs(self._usage) do
		g._usage[k] = v
	end
	return g
end

--- Start a fresh @{Path} for drawing.
function Graphics:beginPath()
	return ffi.gc(lib.GraphicsBeginPath(self._ref), lib.ReleasePath)
end

--- Close the current @{Path}, thereby creating a loop.

bind("closePath", "GraphicsClosePath")

--- Inside the current @{Path}, start a fresh @{Subpath}.

function Graphics:beginSubpath()
	return ffi.gc(lib.GraphicsBeginSubpath(self._ref), lib.ReleaseSubpath)
end

--- Inside the current @{Path}, close the current @{Subpath}, thereby creating a loop.

function Graphics:closeSubpath()
	lib.GraphicsCloseSubpath(self._ref, true)
end
bind("invertSubpath", "GraphicsInvertSubpath")

--- Get the current @{Path} for drawing.

function Graphics:getCurrentPath()
	return gcpath(lib.GraphicsGetCurrentPath(self._ref))
end

--- Add a @{Path}.

bind("addPath", "GraphicsAddPath")

--- Remove a @{Path}.

bind("removePath", "GraphicsRemovePath")

function Graphics:fetchChanges(flags)
	return lib.GraphicsFetchChanges(self._ref, flags)
end

--- Move to position (x, y).

function Graphics:moveTo(x, y)
	lib.GraphicsCloseSubpath(self._ref, false)
	local t = self:beginSubpath()
	return newCommand(t, lib.SubpathMoveTo(t, x, y))
end

--- Draw a line to (x, y).

function Graphics:lineTo(x, y)
	local t = self:beginSubpath()
	return newCommand(t, lib.SubpathLineTo(t, x, y))
end

--- Draw a cubic Bézier curve to (x, y).

function Graphics:curveTo(...)
	local t = self:beginSubpath()
	return newCommand(t, lib.SubpathCurveTo(t, ...))
end

--- Draw an arc.

function Graphics:arc(x, y, r, startAngle, endAngle, ccw)
	lib.SubpathArc(self:beginSubpath(), x, y, r, startAngle, endAngle, ccw or false)
end

--- Draw a rectangle.

function Graphics:drawRect(x, y, w, h, rx, ry)
	local t = self:beginSubpath()
	return newCommand(t, lib.SubpathDrawRect(t, x, y, w, h or w, rx or 0, ry or 0))
end

--- Draw a circle.

function Graphics:drawCircle(x, y, r)
	local t = self:beginSubpath()
	return newCommand(t, lib.SubpathDrawEllipse(t, x, y, r, r))
end

--- Draw an ellipse.

function Graphics:drawEllipse(x, y, rx, ry)
	local t = self:beginSubpath()
	return newCommand(t, lib.SubpathDrawEllipse(t, x, y, rx, ry or rx))
end

--- Set fill color.

function Graphics:setFillColor(r, g, b, a)
	local color = Paint._wrap(r, g, b, a)
	lib.GraphicsSetFillColor(self._ref, color)
	return color
end

--- Set line dash pattern.

function Graphics:setLineDash(...)
	local dashes = {...}
	local n = #dashes
	local data = ffi.new("float[" .. tostring(n) .. "]", dashes)
	lib.GraphicsSetLineDash(self._ref, data, n)
	return self
end

--- Set line width.

bind("setLineWidth", "GraphicsSetLineWidth")

--- Set miter limit.

bind("setMiterLimit", "GraphicsSetMiterLimit")

--- Set line dash offset.

bind("setLineDashOffset", "GraphicsSetLineDashOffset")

--- Rotate elements.

function Graphics:rotate(w, k)
	lib.GraphicsRotate(tove.elements[w], k)
end

--- Set line color.

function Graphics:setLineColor(r, g, b, a)
	local color = Paint._wrap(r, g, b, a)
	lib.GraphicsSetLineColor(self._ref, color)
	return color
end

--- Add fill to shape.

bind("fill", "GraphicsFill")

--- Add stroke to shape.

bind("stroke", "GraphicsStroke")

--- Compute bounding box.

function Graphics:computeAABB(prec)
	local bounds = ffi.new("ToveBounds")
	lib.GraphicsGetBounds(self._ref, prec == "high", bounds)
	return bounds.x0, bounds.y0, bounds.x1, bounds.y1
end

--- Get width.

function Graphics:getWidth(prec)
	local bounds = ffi.new("ToveBounds")
	lib.GraphicsGetBounds(self._ref, prec == "high", bounds)
	return bounds.x1 - bounds.x0
end

--- Get height.

function Graphics:getHeight(prec)
	local bounds = ffi.new("ToveBounds")
	lib.GraphicsGetBounds(self._ref, prec == "high", bounds)
	return bounds.y1 - bounds.y0
end

--- Set display mode.

function Graphics:setDisplay(mode, ...)
	local quality = {...}
	if mode.__index == Graphics then
		local g = mode
		mode = g._display.mode
		quality = g._display.quality
	end
	if self._cache ~= nil and mode == self._display.mode then
		if self._cache.updateQuality(quality) then
			self._display = makeDisplay(mode, quality, self._usage)
			return
		end
	end
	self._cache = nil
	self._display = makeDisplay(mode, quality, self._usage)
	self:setResolution(1)
end

--- Get display mode.

function Graphics:getDisplay()
	return self._display.mode, unpack(self._display.quality)
end

--- Get display quality.

function Graphics:getQuality()
	return unpack(self._display.quality)
end

--- Get usage.

function Graphics:getUsage()
	return self._usage
end

--- Set resolution.

function Graphics:setResolution(resolution)
	if resolution ~= self._resolution then
		self._cache = nil
		self._resolution = resolution
	end
end

--- Set usage.

function Graphics:setUsage(what, usage)
	if what.__index == Graphics then
		for k, v in pairs(what._usage) do
			self:setUsage(k, v)
		end
	end
	if usage ~= self._usage[what] then
		self._cache = nil
		self._usage[what] = usage
		if what == "points" then
			self._usage["triangles"] = usage
		end
		attachTesselator(self._display, self._usage)
	end
end

--- Clear internal drawing cache.

function Graphics:clearCache()
	self._cache = nil
end

--- Cache the current shape as key frame.

function Graphics:cacheKeyFrame()
	self:_create()
	self._cache.cacheKeyFrame(self._cache)
end

--- Set internal key frame cache size.

function Graphics:setCacheSize(size)
	self:_create()
	self._cache.setCacheSize(self._cache, size)
end

function Graphics:set(arg, swl)
	if getmetatable(arg) == tove.Transform then
		lib.GraphicsSet(
			self._ref,
			arg.s._ref,
			swl or false,
			unpack(arg.args))
	else
		lib.GraphicsSet(
			self._ref,
			arg._ref,
			false, 1, 0, 0, 0, 1, 0)
	end
end

--- Transform this @{Graphics}.

function Graphics:transform(...)
	self:set(tove.transformed(self, ...))
end

--- Rescale to given size.

function Graphics:rescale(size, center)
	local x0, y0, x1, y1 = self:computeAABB()
	local r = math.max(x1 - x0, y1 - y0)
	if math.abs(r) > 1e-8 then
		local s = size / r
		if center or true then
			self:set(tove.transformed(
				self, 0, 0, 0, s, s, (x0 + x1) / 2, (y0 + y1) / 2), true)
		else
			self:set(tove.transformed(self, 0, 0, 0, s, s), true)
		end
	end
end

--- Get number of triangles.

function Graphics:getNumTriangles()
	self:_create()
	if self._cache ~= nil and self._cache.mesh ~= nil then
		return self._cache.mesh:getNumTriangles()
	else
		return 2
	end
end

--- Draw to screen.

function Graphics:draw(x, y, r, sx, sy)
	self:_create().draw(x, y, r, sx, sy)
end

--- Warm-up shaders.

function Graphics:warmup()
	local r = self:_create().warmup()
	love.graphics.setShader()
	return r
end

--- Rasterize as bitmap.

function Graphics:rasterize(width, height, tx, ty, scale, settings)
	if width == "default" then
		settings = height -- second arg

		local resolution = self._resolution * tove._highdpi
		local x0, y0, x1, y1 = self:computeAABB("high")

		x0 = math.floor(x0)
		y0 = math.floor(y0)
		x1 = math.ceil(x1)
		y1 = math.ceil(y1)

		if x1 - x0 < 1 or y1 - y0 < 1 then
			return nil
		end

 		return self:rasterize(
			resolution * (x1 - x0),
			resolution * (y1 - y0),
			-x0 * resolution,
			-y0 * resolution,
			resolution, settings), x0, y0, x1, y1, resolution
	end

	width = math.ceil(width)
	height = math.ceil(height)

	local imageData = love.image.newImageData(width, height, "rgba8")
	local stride = imageData:getSize() / height
	lib.GraphicsRasterize(
		self._ref, imageData:getPointer(), width, height, stride,
		tx or 0, ty or 0, scale or 1, settings)

	return imageData
end

--- Animate between two @{Graphics}.

function Graphics:animate(a, b, t)
	lib.GraphicsAnimate(self._ref, a._ref, b._ref, t or 0)
end

--- Warp points.

function Graphics:warp(f)
	local paths = self.paths
    for i = 1, paths.count do
		paths[i]:warp(f)
	end
end

local orientations = {
	cw = lib.TOVE_ORIENTATION_CW,
	ccw = lib.TOVE_ORIENTATION_CCW
}

--- Set orientation of all @{Subpath}s.

function Graphics:setOrientation(orientation)
	lib.GraphicsSetOrientation(self._ref, orientations[orientation])
end

--- Clean paths.

function Graphics:clean(eps)
	lib.GraphicsClean(self._ref, eps or 1e-2)
end

--- Check if inside.

function Graphics:hit(x, y)
	local path = lib.GraphicsHit(self._ref, x, y)
	if path.ptr ~= nil then
		return gcpath(path)
	else
		return nil
	end
end

function Graphics:shaders(gen)
	local npaths = lib.GraphicsGetNumPaths(self._ref)
	local shaders = {}
	for i = 1, npaths do
		shaders[i] = gen(lib.GraphicsGetPath(self._ref, i))
	end
	return shaders
end

--- Set all colors to one.

function Graphics:setMonochrome(r, g, b)
	local paths = self.paths
	r = r or 1
	g = g or 1
	b = b or 1
    for i = 1, paths.count do
        local p = paths[i]
        if p:getFillColor() ~= nil then
            p:setFillColor(r, g, b)
        end
        p:setLineColor(r, g, b)
    end
end

function Graphics:setAnchor(dx, dy, prec)
	dx = dx or 0
	dy = dy or 0
	local x0, y0, x1, y1 = self:computeAABB(prec)
	local ox = ((dx <= 0 and x0 or x1) + (dx < 0 and x0 or x1)) / 2
	local oy = ((dy <= 0 and y0 or y1) + (dy < 0 and y0 or y1)) / 2
	self:set(tove.transformed(self, -ox, -oy))
end

function Graphics:serialize()
	local paths = self.paths
	local p = {}
	for i = 1, paths.count do
		p[i] = paths[i]:serialize()
	end
	local d = self._display
	return {version = 1,
		paths = p,
		display = {d.mode, d.quality},
		usage = self._usage}
end

function Graphics:debug(...)
	if self._cache ~= nil and self._cache.debug ~= nil then
		self._cache.debug(...)
	end
end

local create = (function()

local function createDrawMesh(mesh, x0, y0, s)
	if mesh == nil then
		return function (x, y, r, sx, sy)
		end
	else
		local draw = love.graphics.draw
		return function (x, y, r, sx, sy)
			sx = sx or 1
			sy = sy or 1
			x = (x or 0) + x0 * sx
			y = (y or 0) + y0 * sy
			draw(mesh, x, y, r or 0, s * sx, s * sy)
		end
	end
end

local function createDrawShaders(shaders)
	local setShader = love.graphics.setShader
	return function(...)
		for _, s in ipairs(shaders) do
			s:draw(...)
		end
		setShader()
	end
end

local function _updateBitmap(graphics)
	return graphics:fetchChanges(lib.CHANGED_ANYTHING) ~= 0
end

local function _makeDrawFlatMesh(mesh)
	return createDrawMesh(mesh:getMesh(), 0, 0, 1)
end

local function _updateFlatMesh(graphics)
	local flags = graphics:fetchChanges(lib.CHANGED_ANYTHING)
	if flags >= lib.CHANGED_GEOMETRY then
		return true -- recreate from scratch
	end

	if bit.band(flags, lib.CHANGED_FILL_STYLE + lib.CHANGED_LINE_STYLE) ~= 0 then
		return true -- recreate from scratch (might no longer be flat)
	end

	if bit.band(flags, lib.CHANGED_POINTS) ~= 0 then
		local mesh = graphics._cache.mesh
		if mesh:getUsage("points") == "static" then
			tove.slow("static mesh points changed in " .. tove._str(graphics._name))
		end
		local tessFlags = lib.UPDATE_MESH_VERTICES
		if graphics._usage["triangles"] ~= "static" then
			tessFlags = bit.bor(tessFlags, lib.UPDATE_MESH_AUTO_TRIANGLES)
		end
		if mesh:retesselate(tessFlags) then
			graphics._cache.draw = _makeDrawFlatMesh(mesh)
		end
	end

	return false
end

local function _updateShaders(graphics)
	if graphics:fetchChanges(lib.CHANGED_GEOMETRY) ~= 0 then
		return true
	end
	for _, s in ipairs(graphics._cache.shaders) do
		s:update()
	end
	return false
end

local create = {}

local function _noCache()
end

create.texture = function(self)
	local quality = self._display.quality
	local settings = ffi.new("ToveRasterizeSettings")
	local palette  -- keep until rasterized
	local mode = quality[1]

	if mode == "fast" then
		lib.SetRasterizeSettings(settings, "fast", lib.NoPalette(), 1, 0, nil, 0)
	elseif (mode or "best") == "best" then
		local _, noise, spread, algo, palette = unpack(quality)
		local noiseData, noiseDataSize = nil,  0
		if type(noise) == "table" then
			local data, amount = unpack(noise)
			noise = amount
			noiseData = data:getPointer()
			noiseDataSize = math.floor(math.sqrt(data:getSize() / ffi.sizeof("float")))
		end
		if not lib.SetRasterizeSettings(
			settings, algo or "jarvis", palette or lib.NoPalette(), spread or 1,
			noise or 0.01, noiseData, noiseDataSize) then
			error(table.concat({"illegal rasterize settings:", unpack(quality)}, " "))
		end
	elseif mode == "retro" then
		local _, ipal, algo, spread, noise = unpack(quality)
		local size
		if ipal == nil then
			palette = lib.NoPalette()
		elseif type(ipal) == "string" then
			palette = lib.DefaultPalette(ipal)
		elseif type(ipal) == "table" then
			size = #ipal
			local colors = ffi.new("uint8_t[?]", size * 3)
			local i = 0
			for _, c in ipairs(ipal) do
				if type(c) == "table" then
					colors[i] = c[1] * 255
					colors[i + 1] = c[2] * 255
					colors[i + 2] = c[3] * 255
				elseif c:sub(1, 1) == '#' then
					colors[i] = tonumber("0x" .. c:sub(2, 3))
					colors[i + 1] = tonumber("0x" .. c:sub(4, 5))
					colors[i + 2] = tonumber("0x" .. c:sub(6, 7))
				end
				i = i + 3
			end
			palette = ffi.gc(lib.NewPalette(colors, size), lib.ReleasePalette)
		else
			palette = ipal
		end

		if not lib.SetRasterizeSettings(
			settings, algo or "stucki", palette, spread or 1, noise or 0, nil, 0) then
			error(table.concat({"illegal rasterize settings:", unpack(quality)}, " "))
		end
	else
		error("illegal texture quality " .. tostring(quality[1]))
	end

	local imageData, x0, y0, x1, y1, resolution =
		self:rasterize("default", settings)

	if imageData == nil then
		return {
			draw = function() end,
			update = _updateBitmap,
			cacheKeyFrame = _noCache,
			setCacheSize = _noCache
		}
	end

	local image = love.graphics.newImage(imageData)
	image:setFilter("linear", "linear")

	return {
		mesh = image,
		draw = createDrawMesh(image, x0, y0, 1 / resolution),
		warmup = function() end,
		update = _updateBitmap,
		updateQuality = function() return false end,
		cacheKeyFrame = _noCache,
		setCacheSize = _noCache
	}
end

local function _cacheMeshKeyFrame(data)
	data.mesh:cacheKeyFrame()
end

local function _setMeshCashSize(data, size)
	data.mesh:setCacheSize(size)
end

create.mesh = function(self)
	local display = self._display
	local tsref = display.tesselator
	local usage = self._usage
	local name = self._name

	local gref = self._ref
	local tess = function(cmesh, flags)
		return lib.TesselatorTessGraphics(tsref, gref, cmesh, flags)
	end

	if lib.GraphicsAreColorsSolid(self._ref) or
		usage["shaders"] == "avoid" then

		local mesh = tove.newColorMesh(name, usage, tess)
		local x0, y0, x1, y1 = self:computeAABB()
		return {
			mesh = mesh,
			draw = _makeDrawFlatMesh(mesh),
			warmup = function() end,
			update = _updateFlatMesh,
			updateQuality = function() return false end,
			cacheKeyFrame = _cacheMeshKeyFrame,
			setCacheSize = _setMeshCashSize
		}
	else
		local shader = _shaders.newMeshShader(
			name, self, tess, usage, 1)

		return {
			mesh = shader:getMesh(),
			draw = function(...) shader:draw(...) end,
			warmup = function()
				if shader:warmup() then
					return 1
				end
			end,
			update = function() shader:update() end,
			updateQuality = function() return false end,
			cacheKeyFrame = _cacheMeshKeyFrame,
			setCacheSize = _setMeshCashSize
		}
	end
end

create.gpux = function(self)
	local quality = self._display.quality
	local shaders = self:shaders(function(path)
		return _shaders.newComputeShader(path, quality)
	end)
	return {
		shaders = shaders,
		draw = createDrawShaders(shaders),
		warmup = function(...)
			for i, s in ipairs(shaders) do
				if s:warmup(...) then
					return i / #shaders
				end
			end
		end,
		update = _updateShaders,
		updateQuality = function(quality)
			for _, s in ipairs(shaders) do
				if not s:updateQuality(quality) then
					return false
				end
			end
			return true
		end,
		debug = function(i, ...)
			shaders[i]:debug(...)
		end,
		cacheKeyFrame = _noCache,
		setCacheSize = _noCache
	}
end

return function(self)
	local cache = self._cache
	if cache ~= nil then
		if not cache.update(self) then
			return cache
		end
		self._cache = nil
		cache = nil
	end

	local mode = self._display.mode
	local f = create[mode]
	if f ~= nil then
		self._cache = f(self)
	else
		error("invalid tove display mode: " .. (mode or "nil"))
	end
	self:fetchChanges(lib.CHANGED_ANYTHING) -- clear all changes
	return self._cache
end
end)()
Graphics._create = create
end)()
local Shape = (function()


--- @module stage

--- A container which allows you to draw a couple of things with one draw call.

local g = love.graphics

local Container = {}
Container.__index = Container

--- x position

--- y position

--- rotation in radians

--- x scale factor

--- y scale factor

--- Create new container.
tove.newContainer = function()
	return setmetatable({x = 0, y = 0, r = 0, sx = 1, sy = 1,
		_children = {}}, Container)
end

--- Draw container and its children.
function Container:draw()
	if next(self._children) ~= nil then
		g.push("transform")

		g.translate(self.x, self.y)
		g.rotate(self.r)
		g.scale(self.sx, self.sy)

		for _, child in ipairs(self._children) do
			child:draw()
		end

		g.pop()
	end
end

--- Add child(ren).
function Container:addChild(...)
	for _, child in ipairs({...}) do
		table.insert(self._children, child)
	end
end

--- Add child(ren) at position.
function Container:addChildAt(...)
	local args = {...}
	local at = args[#p]
	args[#p] = nil
	for i, child in ipairs(args) do
		table.insert(self._children, at + i - 1, child)
	end
end

local function remove(t, x)
	for i, y in ipairs(t) do
		if x == y then
			table.remove(t, i)
			break
		end
	end
end

--- Remove child(ren).
function Container:removeChild(...)
	for _, child in ipairs({...}) do
		remove(self._children, child)
	end
end

--- Remove child(ren) at position.

function Container:removeChildAt(...)
	local j = {...}
	table.sort(j, function(a, b) return a > b end)
	for _, i in ipairs(j) do
		table.remove(self._children, i)
	end
end

--- Move child to position.
function Container:setChildIndex(child, index)
	self:removeChild(child)
	self:addChildAt(child, index)
end

--- @type Stage

--- Create new stage.

tove.newStage = function()
	return tove.newContainer()
end


--- @type Shape

local Shape = {}
Shape.__index = Shape

---@number x x position

---@number y y position

---@number rotation rotation in radians

---@number sx x scale factor

---@number sy y scale factor

--- Create new shape.

tove.newShape = function(graphics)
	if graphics == nil then
		graphics = tove.newGraphics()
	end
	return setmetatable({graphics = graphics, x = 0, y = 0,
		r = 0, sx = 1, sy = 1, animation = nil}, Shape)
end

function Shape:animate(t)
	if self.animation ~= nil then
		if not self.animation:animate(self, t) then
			self.animation = nil
		end
	end

	for _, child in ipairs(self.children) do
		child:animate(t)
	end
end

--- Draw.

function Shape:draw()
	self.graphics:draw(self.x, self.y, self.r, self.sx, self.sy)
end

--- Set display mode.

function Shape:setDisplay(...)
	self.graphics:setDisplay(...)
end

function Shape:setFillColor(...)
	for _, path in self._graphics.paths do
		path:setFillColor(...)
	end
end

return Shape
end)()

local Animation = (function()

local function _linear(x)
	return x
end


--- @module animation

--- A tween.

local Tween = {}
Tween.__index = Tween

--- Create new tween.

tove.newTween = function(graphics)
	return setmetatable({_graphics0 = graphics, _to = {}, _duration = 0, _morph = false}, Tween)
end

tove.newMorph = function(graphics)
	local t = tove.newTween(graphics)
	t._morph = true
	return t
end

--- Add new tween step.

function Tween:to(graphics, duration, ease)
	table.insert(self._to, {graphics = graphics, duration = duration, ease = ease or _linear})
	self._duration = self._duration + duration
	return self
end

local function morphify(graphics)
	local n = #graphics
	local refs = ffi.new("ToveGraphicsRef[?]", n)
	for i, g in ipairs(graphics) do
		refs[i - 1] = g._ref
	end
	lib.GraphicsMorphify(refs, n)
end

local function createGraphics(graphics)
	if type(graphics) == "string" then
		graphics = tove.newGraphics(graphics)
	end
	return graphics
end


--- A flipbook.

local Flipbook = {}

--- Create new flipbook.
tove.newFlipbook = function(fps, tween, ...)
	display = {...}
	if next(display) == nil then
		display = {"texture"}
	end
	local frames = {}
	local looping = tween._graphics0 == tween._to[#tween._to].graphics
	for i, keyframe in ipairs(tween._to) do
		duration = math.ceil(keyframe.duration * fps)
		local g0 = frames[#frames]
		local j0 = 1
		if i == 1 then
			g0 = createGraphics(tween._graphics0)
			if not looping then
				j0 = 0
				duration = duration - 1
			end
		end

		local g1 = createGraphics(keyframe.graphics)
		local ease = keyframe.ease

		local ag0 = g0
		local ag1 = g1
		if j0 <= duration - 1 and tween._morph then
			ag0 = ag0:clone()
			ag1 = ag1:clone()
			morphify({ag0, ag1})
		end

		for j = j0, duration - 1 do
			if ease == "none" then
				table.insert(frames, g0)
			else		
				local inbetween = tove.newGraphics()
				inbetween:animate(ag0, ag1, ease(j / duration))
				inbetween:setDisplay(unpack(display))

				inbetween:cacheKeyFrame()

				table.insert(frames, inbetween)
			end
		end

		table.insert(frames, g1)
	end
	return setmetatable({_fps = fps, _frames = frames, _duration = tween._duration, _t = 0, _i = 1}, Flipbook)
end

Flipbook.__index = function(self, key)
	if key == "t" then
		return self._t
	else
		return Flipbook[key]
	end
end

Flipbook.__newindex = function(self, key, value)
	if key == "t" then
		self._t = math.max(0, math.min(value, self._duration))
		self._i = math.min(math.max(1,
			1 + math.floor(value * self._fps)), #self._frames)
	end
end

--- Draw.
function Flipbook:draw(...)
	self._frames[self._i]:draw(...)
end


local Animation = {}

tove.newAnimation = function(tween, ...)
	display = {...}
	if next(display) == nil then
		display = {"mesh", 0.5} -- tove.fixed(2)
	end
	local keyframes = {}
	local graphics = tove.newGraphics()
	graphics:setUsage("points", "stream")
	graphics:setUsage("colors", "stream")
	graphics:setDisplay(unpack(display))
	local offset = 0

	do
		local g = createGraphics(tween._graphics0)
		graphics:animate(g, g, 0)
		graphics:cacheKeyFrame()
		table.insert(keyframes, {graphics = g, offset = 0, duration = 0, ease = _linear})
	end

	for i, keyframe in ipairs(tween._to) do
		local g = createGraphics(keyframe.graphics)
		keyframe.graphics = g
		offset = offset + keyframe.duration
		keyframe.offset = offset
		graphics:animate(g, g, 0)
		graphics:cacheKeyFrame()
		table.insert(keyframes, keyframe)
	end

	if tween._morph then
		local g = {}
		for i, f in ipairs(keyframes) do
			table.insert(g, f.graphics)
		end
		morphify(g)
	end

	return setmetatable({_keyframes = keyframes, _graphics = graphics,
		_t = 0, _i = 1, _duration = tween._duration}, Animation)
end

Animation.__newindex = function(self, key, value)
	if key == "t" then
		local t = math.max(0, math.min(value, self._duration))
		self._t = t
		local f = self._keyframes
		local n = #f
		if n > 1 then
			local lo = 0
			local hi = n
			while lo < hi do
				local mid = math.floor((lo + hi) / 2)
				if f[mid + 1].offset < t then
					lo = mid + 1
				else
					hi = mid
				end
			end
			lo = math.min(math.max(lo, 1), n)
			local f0 = f[lo]
			local f1 = f[lo + 1]
			self._graphics:animate(f0.graphics, f1.graphics,
				f1.ease((t - f0.offset) / f1.duration))
		end
	end
end

Animation.__index = function(self, key)
	if key == "t" then
		return self._t
	elseif key == "paths" then
		return self._graphics.paths
	else
		return Animation[key]
	end
end

function Animation:draw(...)
	self._graphics:draw(...)
end

function Animation:debug(...)
	self._graphics:debug(...)
end

function Animation:setCacheSize(s)
	self._graphics:setCacheSize(s)
end

function Animation:setName(n)
	self._graphics:setName(n)
end
end)()
end

tove.init()

return tove

--- A list of elements, such as e.g. @{Path}s.

--- Total number of elements in this list.

--- Get element at index.

--- Get element with name.
