The canvas functions
Alpha blending
Using clips
Affine transformations
The canvas functions
The canvas is a rectangular region of pixels that can be displayed
in the viewport. Using the following functions, Lua scripts have
total control over what the canvas looks like.
The examples given below assume a script has started with these lines:
local glu = glu()
local cv = canvas()
local cp = require "cplus"
blend(i)
Set alpha blending off (i = 0), full
(i = 1) or fast (i = 2) and return the previous blend state.
The fast mode should only be used when the target pixels are opaque.
The functions affected by alpha blending are:
ellipse,
fill,
flood,
line,
paste,
scale and
setpixel.
Note that setting alpha blending to full or fast also forces ellipses and lines to be antialiased.
Example: local oldblend = cv.blend(1)
copy(x, y, wd, ht, clipname)
Copy the pixels in the given rectangle from the current render target into the named
clip for later use in a function like paste
or scale.
If the given width or height is less than 1 then it is treated as an inset from
the render target's current width or height (this makes it easy to copy the entire
render target with a call like copy(0,0,0,0,"all")).
Any parts of the rectangle outside the render target are filled with transparent pixels.
The clip name can be any non-empty string.
Example: cv.copy(0, 0, 100, 200, "tempbox")
create(wd, ht, clipname)
Create a clip with the given name, width and height.
The clip name can be any non-empty string.
If no clipname is supplied then create a canvas with the given width and height.
If the given width or height is less than 1 then it is treated as an inset from
the viewport's width or height.
If no arguments are supplied then create a canvas with the size of the viewport.
All pixels are initially transparent (their RGBA values are set to 0,0,0,0).
When creating a canvas the following are also set:
The initial RGBA values used by later drawing functions are
set to 255,255,255,255 (opaque white).
The canvas's initial position is set to "topleft",
the cursor is set to the standard arrow,
the transform values are set to 1,0,0,1 (identity)
and alpha blending is off.
The initial font for drawing text is the default system font at a size of 10pt.
The text alignment for multi-line text is set to "left"
and the text background is set to transparent (0,0,0,0).
The initial line width (used by the ellipse and
line functions) is set to 1.
The render target is set to be the canvas.
Examples:
cv.create() -- create a canvas with size of viewport
cv.create(-2, 0) -- ditto but 2 pixels narrower
cv.create(400, 300) -- create a 400x300 canvas
cv.create(400, 300, "myclip") -- create a 400x300 clip
cv.create(0, 0, "myclip") -- create a clip with size of viewport
cursor(name)
Specify which cursor to use when the mouse moves over a
non-transparent pixel within the canvas. The valid cursor names are:
arrow | | – standard arrow |
cross | | – crosshairs |
hand | | – hand |
hidden | | – invisible cursor |
pencil | | – pencil |
pick | | – color picker |
wait | | – a lengthy task is in progress |
zoomin | | – magnifying glass with "+" |
zoomout | | – magnifying glass with "-" |
The previous cursor name is returned.
Example: local oldcursor = cv.cursor("pencil")
delete(clipname)
Delete the canvas if no clip name is supplied, otherwise delete the named clip.
Note that it is an error to delete a clip that is the current render target.
Deleting the canvas will stop any sound that is playing.
Examples:
cv.delete("myclip") -- delete the given clip
cv.delete() -- delete the canvas
ellipse(x, y, wd, ht)
Draw an ellipse inside the given rectangle using the current rgba values onto
the render target.
Any parts of the ellipse outside the render target are automatically clipped.
A width or height less than 1 is relative to the render target's width or height.
If the width equals the height (and both are positive) then the result is a circle.
If alpha blending is turned on then the edges of the ellipse are antialiased.
The linewidth function determines the thickness
of the ellipse.
Example: cv.ellipse(10, 10, 60, 40)
fill(x, y, wd, ht)
Fill the given rectangle in the render target with the
current rgba values.
Any parts of the rectangle outside the render target are automatically clipped.
If no rectangle is given then the entire render target is filled.
A width or height less than 1 is relative to the render target's width or height,
so a call like cv.fill(1,1,-2,-2) would fill all of the render target
except for a 1 pixel border around the edges.
Examples:
cv.fill(10, 10, 30, 50)
cv.fill() -- equivalent to cv.fill(0,0,0,0)
flood(x, y)
Flood a connected region of pixels in the render target that
match the given starting pixel with the current rgba values.
Example: cv.flood(10, 20)
font(fontname, fontsize)
Set the font used by later text calls.
If the font name is "" then only the font size will be changed.
The font size must be from 1 to 1000.
The valid font names (apart from an empty string) are:
default | | – standard system font |
default-bold | | – bold system font |
default-italic | | – italic system font |
mono | | – mono-spaced font |
mono-bold | | – bold mono-spaced font |
mono-italic | | – italic mono-spaced font |
roman | | – roman font |
roman-bold | | – bold roman font |
roman-italic | | – italic roman font |
The previous font is returned as a string and an integer.
Examples:
local oldfont, oldsize = cv.font("roman-bold", 15)
local oldfont, oldsize = cv.font("", 20) -- change the size of the current font
getpixel(x, y)
Return the RGBA values of the given pixel in the current render target.
If the pixel is inside the render target then all values are from 0 to 255,
otherwise -1 is returned for each component.
Example: local R,G,B,A = cv.getpixel(10, 20)
getsize(name)
Return the width and height of a clip if the given name is a non-empty string,
or the canvas if the name is empty, or the current render target if no name is supplied.
Examples:
local clipwd, clipht = cv.getsize("myclip")
local canvaswd, canvasht = cv.getsize("")
local targetwd, targetht = cv.getsize()
getxy()
Return the x,y pixel coordinates of the current cursor position within the canvas.
If the cursor is outside the canvas then -1,-1 is returned.
The top left pixel in the canvas is 0,0; x values increase
to the right and y values increase downwards.
Example: local x, y = cv.getxy()
line(x1, y1, x2, y2, ... xn, yn)
Draw one or more connected lines between the supplied coordinates x1,y1 to xn,yn
on the render target using the current rgba values.
Any pixels outside the render target are automatically clipped.
If alpha blending is turned on then the lines are antialiased.
The linewidth function sets the thickness of the lines.
Example:
-- draw triangle with vertices at (0,0), (100,0) and (50,100)
cv.line(0, 0, 100, 0, 50, 100, 0, 0)
linewidth(value)
Set the line width used by the ellipse and line functions.
The given value must be an integer from 1 to 1000.
The previous line width is returned.
Example: local oldwidth = cv.linewidth(3)
load(file, clipname)
Load the given BMP/GIF/PNG/TIFF file into the named clip
(a non-empty string).
Returns the size of the image as 2 integers: width, height.
Example: local imgwd, imght = cv.load("foo.png", "imgclip")
optimize(clipname)
Optimize the named clip for improved paste performance when
using alpha blending.
Returns the bounding box of all the non-zero alpha pixels within the clip
as 4 integers: x, y, width, height.
A clip that is modified after optimization must be optimized again before use to get the
performance benefit.
Especially effective for text rendering and used automatically by the
cp.maketext function.
Example: local x, y, w, h = cv.optimize("textclip")
paste(clipname, x, y)
Paste the named clip (created by an earlier create, copy,
load or text call) into the render target
at the given location. Any pixels outside the render target are automatically clipped.
Example: cv.paste("tempbox", 10, 20)
position(pos)
Specify where the canvas is to be displayed within the viewport.
The valid positions are:
topleft | | – top left corner |
topright | | – top right corner |
bottomright | | – bottom right corner |
bottomleft | | – bottom left corner |
middle | | – middle of viewport |
middletop | | – middle of top edge |
middleright | | – middle of right edge |
middlebottom | | – middle of bottom edge |
middleleft | | – middle of left edge |
Example: cv.position("middle")
replace("red green blue alpha")
Replace pixels that match the given color components in the render target
with the current rgba values.
The given string must contain 4 components separated by single spaces.
Returns the number of pixels replaced.
Example:
-- replace opaque black pixels with semi-transparent blue pixels in clip named 'myclip'
local oldtarget = cv.target("myclip")
cv.rgba(0, 0, 255, 128)
local replaced = cv.replace("0 0 0 255")
cv.target(oldtarget)
There are two special characters that can be used in the color specification for
more advanced matches: ! and *.
A color component may be specified as the wildcard * which means match any value for
that component.
Example:
-- replace any opaque pixel with opaque blue
cv.rgba(cp.blue)
local replaced = cv.replace("* * * 255")
If the match specification is prefixed with ! then pixels that don't match
the specification are replaced.
Example:
-- replace any pixel that isn't white with yellow
cv.rgba(cp.yellow)
local replaced = cv.replace("!255 255 255 255")
Alternatively if the alpha specification is prefixed with ! then pixels
that don't have the specified alpha value are replaced.
Example:
-- replace any non-opaque pixel with opaque blue
cv.rgba(cp.blue)
local replaced = cv.replace("* * * !255")
Normally when a match is found the clip pixel is replaced with the current
rgba values. This can be overridden by postfixing
one or more color components with r, g, b, a or #.
Each color component in the clip pixel can either be left unchanged
(in the case of #) or replaced with the specified color component from the clip pixel.
Examples:
-- make transparent pixels opaque (r g b 0 -> r g b 255)
cv.rgba(0, 0, 0, 255)
local replaced = cv.replace("*# *# *# 0")
-- swap the red and green components (r g b a -> g r b a)
local replaced = cv.replace("*g *r *# *#")
-- set the pixels to opaque gray based on the alpha level (r g b a -> a a a 255)
cv.rgba(0, 0, 0, 255)
local replaced = cv.replace("*a *a *a *")
If the postfix character is followed by - then the component value is inverted.
Examples:
-- invert the r g b components but leave alpha unchanged (r g b a -> 255-r 255-g 255-b a)
local replaced = cv.replace("*#- *#- *#- *#")
-- make transparent pixels opaque and vice versa (r g b a -> r g b 255-a)
local replaced = cv.replace("*# *# *# *#-")
Color components can also be adjusted by using -- to decrement, ++ to increment,
-value to subtract a constant or +value to add a constant.
Component values are clamped to the range 0 to 255.
Examples:
-- increase the brightness of all pixels
local replaced = cv.replace("*#+50 *#+50 *#+50 *#")
-- fade all pixels to black
while cv.replace("*#-- *#-- *#-- *#") > 0 do cv.update() end
The postfixes can also be used without a target component. In this case the current
rgba values are adjusted.
Example:
-- replace green pixels with the current drawing color minus 32 alpha (0 255 0 255 -> 255 0 0 96)
cv.rgba(255, 0, 0, 128)
local replaced = cv.replace("0 255 0 255-32")
resize(wd, ht, clipname)
Resize the named clip to the given width and height.
The clip name can be any non-empty string.
If no clipname is supplied then resize the current canvas to the given width and height.
A width or height less than 1 is treated as an inset from the viewport's width or height.
If no arguments are supplied then resize the canvas to the size of the viewport.
The previous width and height are returned.
This function is typically used if a window resize is detected and you
wish to keep the canvas covering the entire viewport.
Note that all pixels in the resized canvas/clip become transparent
(their RGBA values are reset to 0,0,0,0).
Examples:
local oldwd, oldht = cv.resize()
local oldwd, oldht = cv.resize(1000, 800)
local oldwd, oldht = cv.resize(300, 240, "myclip")
local oldwd, oldht = cv.resize(0, 0, "myclip")
rgba(red, green, blue, alpha) or
rgba{red, green, blue, alpha}
Set the current RGBA values used by later drawing functions
and return the old values in a table.
The RGBA values can be supplied as 4 integers, or as a table with 4 integers.
All values must be from 0 to 255.
The functions that use the current RGBA values are:
ellipse,
fill,
flood,
line,
replace,
setpixel and
text.
Example: local oldrgba = cv.rgba(255, 0, 0, 255)
save(x, y, wd, ht, file)
Save the pixels in the given rectangle of the render target in the given PNG file.
An error will occur if any part of the rectangle is outside the render target.
If the given width or height is less than 1 then it is treated as an inset from
the render target's current width or height (this makes it easy to save the entire
render target with a call like save(0,0,0,0,"canvas.png")).
Example: cv.save(0, 0, 100, 80, glu.getdir("temp").."foo.png")
setpixel(x, y)
Set the specified pixel in the render target to the current
rgba values.
A pixel outside the render target is silently ignored.
Example: cv.setpixel(10, 20)
scale(clipname, x, y, wd, ht, quality)
Paste the named clip into the current render target, scaling it so it fits into
the specified rectangle. Any pixels outside the render target are automatically clipped.
The given quality must be one of these strings:
best | | –
use a high quality, but slow, scaling algorithm
|
fast | | –
use a fast, but lower quality, scaling algorithm
|
Example: cv.scale("imgclip", 10, 10, 100, 200, "best")
sound(command, soundfile, level)
Controls playback of audio files. If present the soundfile argument must point
to a WAV or OGG format file containing the sound to be played.
If the volume level is supplied it must be a number from 0.0 (silent) to 1.0 (maximum).
Multiple sounds can be played simultaneously.
If the function is invoked with no arguments then it returns an integer indicating
whether sound is available:
0 | | – sound support is not available |
1 | | – sound support is available but failed to initialize |
2 | | – sound support is available and ready to use |
There are seven sound commands:
play soundfile (level) |
Play the named soundfile at volume level asynchronously and return immediately.
The volume is set to maximum level (1.0) if not specified. |
|
loop soundfile (level) |
Play the named soundfile at volume level asynchronously and loop until
the stop command is used. |
|
stop (soundfile) |
Stop all sound playback or just the specified soundfile. |
|
pause (soundfile) | |
Pause all sound playback or just the specified soundfile. |
|
resume (soundfile) |
Resume all sound playback or just the specified soundfile. |
|
volume soundfile level |
Set the named soundfile volume level from 0.0 (silent) to 1.0 (maximum).
This is typically used to change the volume of a sound that is already playing. |
|
state (soundfile) |
Returns the playback state of "playing" if any sound (or the specified sound) is playing,
"paused" if the specified sound is paused or "stopped" otherwise.
|
Notes:
- Deleting the canvas will stop any sounds that are playing.
- The state command will return "unknown" if the specified sound is not found.
- If sound support is not available or failed to initialize then the sound commands will
silently do nothing.
Examples:
cv.sound("play", "beep.wav", 0.5) -- play beep.wav at half volume
cv.sound("play", "beep.wav") -- play beep.wav at full volume
cv.sound("loop", "background.ogg") -- play background.ogg in a loop at full volume
cv.sound("volume", "background.ogg", 0.7) -- set background.ogg volume to 0.7
cv.sound("pause", "background.ogg") -- pause playback of background.ogg
cv.sound("resume") -- resume playback of all paused sounds
cv.sound("stop") -- stop all sounds playing
target (clipname)
Set the render target to the named clip or to the canvas if no
clip is specified. All subsequent drawing functions will use the new render target.
Returns the previous target as a string (an empty string if it was the canvas).
It is an error to attempt to delete a clip while it is the
render target.
The functions that use the render target are:
copy (as the source),
ellipse,
fill,
flood,
getpixel (as the source),
line,
paste,
replace,
save (as the source),
scale and
setpixel.
Example: local oldtarget = cv.target("myclip")
text(clipname, "one or more lines of text ...")
Create a named clip containing the given text in the
current font (as set by the most recent font call).
Return the dimensions of the clip as 3 integers: width, height, descent.
The descent is the number of pixels under the baseline (useful if you want to
place text with different sized fonts on the same line).
The text's color depends on the most recent rgba call.
The text background color can be set with the textback
function. By default the text has a transparent background, so you'll probably need to set
blend to 1 before drawing the text with paste.
If you specify an opaque background then there's no need to turn on alpha blending
when doing the paste, so drawing such text will be significantly faster.
You can also specify the alignment of multi-line text with the
textalign function.
Example: local w, h, d = cv.text("tempclip", "First line.\nSecond line.")
textalign(option)
Set the alignment of multi-line text for future text calls
and return the previous option.
The valid alignment options are "left", "right" or "center".
The create function initializes the
alignment to "left".
Example: local oldalign = cv.textalign("right")
textback(red, green, blue, alpha) or
textback{red, green, blue, alpha}
Set the text background for future text calls.
The RGBA values can be supplied as 4 integers, or as a table with 4 integers.
All values must be from 0 to 255.
The previous background is returned as a table with 4 integers.
The create function initializes the
text background to be transparent (0,0,0,0).
Example: local oldbg = cv.textback(255, 0, 0, 255)
transform(axx, axy, ayx, ayy) or
transform{axx, axy, ayx, ayy}
Set the affine transformation values used by later
paste calls and return the old values in a table
with 4 integers.
The transform values can be supplied as 4 integers, or as a table with 4 integers.
The allowed values are 0, 1 or -1.
Example: local oldt = cv.transform(0, -1, 1, 0)
update()
Tell Glu to update the canvas without drawing the viewport's background.
This will be faster than calling glu.update() but
should only be used in cases where the canvas covers the entire viewport
and all pixels in the canvas are opaque.
Example: cv.update()
Alpha blending
Alpha blending is a way of drawing translucent pixels on top of existing
pixels to get nicer looking results. When a canvas is created, alpha blending
is initially turned off. To turn it on a script needs to call blend(1)
for blending with any target, or blend(2) for faster blending when
you know the target pixels are opaque.
Consider this simple script which draws a translucent blue square on top
of a translucent red square (both on top of an opaque white background):
local glu = glu()
local cv = canvas()
cv.create(100, 80)
cv.fill()
cv.blend(1)
cv.rgba(255, 0, 0, 128)
cv.fill(10, 10, 50, 50)
cv.rgba(0, 0, 255, 128)
cv.fill(30, 20, 50, 50)
|
|
 |
blend(1) |
|
|
 |
blend(0) |
|
The left image shows the resulting canvas. The right image shows
the canvas produced by the same script but with the cv.blend(1) line removed
(note that the square colors will depend on the viewport background color
— dark gray in the above example).
Drawing with alpha blending on is significantly slower than drawing
with it off, so it's a good idea to turn it on only when necessary.
The canvas functions that can do alpha blending are:
ellipse,
fill,
flood,
line,
paste,
scale,
setpixel.
Using clips
Scripts can use multiple "clipboards" called clips.
A clip is simply a chunk of memory containing a rectangular area of pixels.
Each clip has a unique name which can be any non-empty string of characters.
Clips are created using these functions: create,
copy, load or text.
Clips are drawn using paste or scale.
Clips can be the render target for any of the drawing functions.
The following script illustrates the use of clips. It creates a tiled
background pattern on which some translucent text is drawn.
The image on the right shows the resulting canvas.
local glu = glu()
local cv = canvas()
cv.create(199, 177)
cv.rgba(255, 255, 0, 255)
cv.fill()
-- create the tile
cv.rgba(0, 0, 0, 255)
cv.line(10, 0, 0, 20)
cv.line(10, 0, 20, 20)
cv.line(0, 20, 20, 20)
cv.rgba(200, 200, 255, 255)
cv.flood(10, 10)
cv.copy(0, 0, 21, 21, "mytile")
-- tile the canvas
for y = 0, 177, 21 do
for x = 0, 199, 21 do
cv.paste("mytile", x, y)
end
end
-- create translucent red text
cv.rgba(255, 0, 0, 180)
cv.font("roman-bold", 80)
cv.text("mytext", "Glu")
cv.blend(1)
cv.paste("mytext", 10, 10)
|
|
|
Affine transformations
The transform function allows a limited set of
affine transformations, namely rotation by multiples of 90 degrees and
reflection about the x, y or diagonal axes.
Only the paste function is affected by the
current transform settings.
The following script shows how text can be rotated or reflected.
Note that the cv.transform calls use pre-defined tables from
the cplus module.
The image on the right shows the resulting canvas.
local glu = glu()
local cv = canvas()
local cp = require "cplus"
cv.create(200, 380)
cv.fill()
cv.blend(1)
cv.font("default-bold", 12)
-- do rotations
cv.rgba(0, 0, 255, 255)
cv.text("temp", "ROTATE")
cv.setpixel(100, 100)
cv.paste("temp", 100, 100)
cv.transform(cp.rcw)
cv.paste("temp", 100, 100)
cv.transform(cp.racw)
cv.paste("temp", 100, 100)
cv.transform(cp.r180)
cv.paste("temp", 100, 100)
-- do reflections
cv.rgba(255, 0, 0, 255)
cv.transform(cp.identity)
cv.text("temp", "-- REFLECT")
cv.setpixel(100, 270)
cv.paste("temp", 100, 270)
cv.transform(cp.flip_x)
cv.paste("temp", 100, 270)
cv.transform(cp.flip_y)
cv.paste("temp", 100, 270)
cv.transform(cp.swap_xy)
cv.paste("temp", 100, 270)
cv.transform(cp.swap_xy_flip)
cv.paste("temp", 100, 270)
|
|
|