butterfly is an object-oriented 2D graphics library for Lua, targeting the Quartz graphics engine.
-
Build and install the library and header files:
make && make installYou can specify the deployment target using the
MACOSX_DEPLOYMENT_TARGETenvironment variable, for exampleexport MACOSX_DEPLOYMENT_TARGET=10.13. -
Set the Xcode build settings for your target:
- Add
/usr/local/includeto Header Search Paths (HEADER_SEARCH_PATHS). - Add
/usr/local/lib/libbutterfly.ato Other Linker Flags (OTHER_LDFLAGS).
- Add
-
Load butterfly graphics classes in your Lua state:
- Add
#include <butterfly/lua.h>to the file where you initialize your Lua state. - Call
bf_lua_loadto set up the globals and metatables for the butterfly graphics classes.
- Add
-
Create a canvas object for your Lua code to draw into:
- Add
#include <butterfly/quartz.h>. - Call
BFCanvasMetricsCreateandBFCanvasCreateForDisplayto create a butterfly canvas from a Quartz graphics context. - Call
bf_lua_pushto push the canvas onto the Lua stack. You can use Lua APIs likelua_setglobalorlua_pcallto assign the canvas to a global variable or call a function passing it as a parameter.
- Add
-
Draw into the canvas from your Lua scripts.
The bf_lua_load C function installs the following global variables in the Lua state:
ColorFontGradientIconPaintModePathStyledStringTransformation
A canvas represents a Quartz graphics context that can be drawn to. This can be a view or an offscreen image. Unlike the other classes which can be instantiated from Lua scripts, canvases must be provided to Lua from the host environment.
canvas:setPaint(color)
canvas:setFont(font)
canvas:setOpacity(opacity)
canvas:setThickness(thickness)
canvas:concatTransformation(transformation)canvas:fill(path)
canvas:stroke(path)canvas:drawText(text, x, y)
canvas:strokeText(text, x, y)local color = Color.rgba(red, green, blue, alpha)red, green, blue, and alpha range from 0 to 1.
canvas:setPaint(color)Applies the color to subsequent drawing operations in the canvas.
local params = { name = 'Comic Sans MS', size = 18 }
local font = Font.get(params)In addition to the font name and size, the parameters can include the following key-value pairs to request additional font features if they’re supported by the font:
lowercase = 'smallCaps'numberCase = 'lowercase'ornumberCase = 'uppercase'numberSpacing = 'proportional'ornumberSpacing = 'monospaced'
local systemFont = Font.system(size)
local boldSystemFont = Font.boldSystem(size)canvas:setFont(font)Applies the font to subsequent text drawing operations in the canvas.
Also see Creating a styled string.
local path = Path.new()path:addSubpath({ x = x, y = y })Starts a new subpath starting at x and y.
path:addLine({ x = x, y = y })Continues from the current point to x and y.
path:addCurve({ cx1 = cx1, cy1 = cy1, cx2 = cx2, cy2 = cy2, x = x, y = y })Adds a cubic Bézier curve using the specified control points and ending at x and y.
path:closeSubpath()Returns to the starting point of the current subpath and closes the subpath.
path:addRect({ left = left, bottom = bottom, right = right, top = top }, radius)Adds a new subpath for a rectangle with the specified boundaries. radius is optional and if specified will produce a rounded rectangle.
path:addOval({ left = left, bottom = bottom, right = right, top = top })Adds a new subpath for an oval with the specified boundaries.
A path is not drawn until it’s passed to the canvas fill or stroke method:
canvas:fill(path)
canvas:stroke(path)local params = { font = font }
local styledString = StyledString.new(string, params)local styledString = styledString1 .. styledString2canvas:drawText(styledString, x, y)
canvas:strokeText(styledString, x, y)local transformation = Transformation.identity()transformation:translate(x, y)
transformation:scale(multiple)
transformation:rotate(radians)transformation:concat(anotherTransformation)canvas:concatTransformation(transformation)Applies the transformation to subsequent drawing operations in the canvas. It’s often a good idea to put this in a preserve block so that you can easily return to the original coordinate system:
canvas:preserve(function(canvas)
canvas:concatTransformation(transformation)
-- drawing commands here use the transformed coordinate system
end)
-- drawing commands here use the original coordinate systemmake lua2png
./lua2png <width> <height> <input.lua> <output.png>The Lua script returns a function taking a canvas object as its only argument. For example:
return function(canvas)
local rect = canvas:metrics():rect()
local path = Path.new()
:addSubpath{ x = 0.5, y = 0.7 }
:addLine{ x = 0.8, y = 1.0 }
:addLine{ x = 1.0, y = 0.7 }
:addLine{ x = 0.7, y = 0.4 }
:addLine{ x = 0.8, y = 0.2 }
:addLine{ x = 0.6, y = 0.0 }
:addLine{ x = 0.5, y = 0.2 }
:addLine{ x = 0.4, y = 0.0 }
:addLine{ x = 0.2, y = 0.2 }
:addLine{ x = 0.3, y = 0.4 }
:addLine{ x = 0.0, y = 0.7 }
:addLine{ x = 0.2, y = 1.0 }
:closeSubpath()
canvas:concatTransformation(Transformation.identity():scale(rect.right - rect.left))
:setPaint(Color.rgba(1.0, 0.4, 0.3, 1.0))
:fill(path)
end