OpenGL.GL.NV.path_rendering
OpenGL extension NV.path_rendering
This module customises the behaviour of the
OpenGL.raw.GL.NV.path_rendering to provide a more
Python-friendly API
Overview (from the spec)
Conventional OpenGL supports rendering images (pixel rectangles and
bitmaps) and simple geometric primitives (points, lines, polygons).
This extension adds a new rendering paradigm, known as path rendering,
for rendering filled and stroked paths. Path rendering is not novel
but rather a standard part of most resolution-independent 2D rendering
systems such as Flash, PDF, Silverlight, SVG, Java 2D, Office
drawings, TrueType fonts, PostScript and its fonts, Quartz 2D, XML
Paper Specification (XPS), and OpenVG. What is novel is the ability
to mix path rendering with arbitrary OpenGL 3D rendering and imaging.
With this extension, path rendering becomes a first-class rendering
mode within the OpenGL graphics system that can be arbitrarily mixed
with existing OpenGL rendering and can take advantage of OpenGL's
existing mechanisms for texturing, programmability, and per-fragment
operations.
Unlike geometric primitive rendering, paths are specified on a 2D
(non-projective) plane rather than in 3D (projective) space.
Even though the path is defined in a 2D plane, every path can
be transformed into 3D clip space allowing for 3D view frustum &
user-defined clipping, depth offset, and depth testing in the same
manner as geometric primitive rendering.
Both geometric primitive rendering and path rendering support
rasterization of edges defined by line segments; however, path
rendering also allows path segments to be specified by Bezier (cubic
or quadratic) curves or partial elliptical arcs. This allows path
rendering to define truly curved primitive boundaries unlike the
straight edges of line and polygon primitives. Whereas geometric
primitive rendering requires convex polygons for well-defined
rendering results, path rendering allows (and encourages!) concave
and curved outlines to be specified. These paths are even allowed
to self-intersect.
When filling closed paths, the winding of paths (counterclockwise
or clockwise) determines whether pixels are inside or outside of
the path.
Paths can also be stroked whereby, conceptually, a fixed-width "brush"
is pulled along the path such that the brush remains orthogonal to
the gradient of each path segment. Samples within the sweep of this
brush are considered inside the stroke of the path.
This extension supports path rendering through a sequence of three
operations:
1. Path specification is the process of creating and updating
a path object consisting of a set of path commands and a
corresponding set of 2D vertices.
Path commands can be specified explicitly from path command
and coordinate data, parsed from a string based on standard
grammars for representing paths, or specified by a particular
glyph of standard font representations. Also new paths can
be specified by weighting one or more existing paths so long
as all the weighted paths have consistent command sequences.
Each path object contains zero or more subpaths specified
by a sequence of line segments, partial elliptical arcs,
and (cubic or quadratic) Bezier curve segments. Each path
may contain multiple subpaths that can be closed (forming
a contour) or open.
2. Path stenciling is the process of updating the stencil buffer
based on a path's coverage transformed into window space.
Path stenciling can determine either the filled or stroked
coverage of a path.
The details of path stenciling are explained within the core
of the specification.
Stenciling a stroked path supports all the standard
embellishments for path stroking such as end caps, join
styles, miter limits, dashing, and dash caps. These stroking
properties specified are parameters of path objects.
3. Path covering is the process of emitting simple (convex &
planar) geometry that (conservatively) "covers" the path's
sample coverage in the stencil buffer. During path covering,
stencil testing can be configured to discard fragments not
within the actual coverage of the path as determined by
prior path stenciling.
Path covering can cover either the filled or stroked coverage
of a path.
The details of path covering are explained within the core
of the specification.
To render a path object into the color buffer, an application specifies
a path object and then uses a two-step rendering process. First, the
path object is stenciled whereby the path object's stroked or filled
coverage is rasterized into the stencil buffer. Second, the path object
is covered whereby conservative bounding geometry for the path is
transformed and rasterized with stencil testing configured to test against
the coverage information written to the stencil buffer in the first step
so that only fragments covered by the path are written during this second
step. Also during this second step written pixels typically have
their stencil value reset (so there's no need for clearing the
stencil buffer between rendering each path).
Here is an example of specifying and then rendering a five-point
star and a heart as a path using Scalable Vector Graphics (SVG)
path description syntax:
GLuint pathObj = 42;
const char *svgPathString =
// star
"M100,180 L40,10 L190,120 L10,120 L160,10 z"
// heart
"M300 300 C 100 400,100 200,300 100,500 200,500 400,300 300Z";
glPathStringNV(pathObj, GL_PATH_FORMAT_SVG_NV,
(GLsizei)strlen(svgPathString), svgPathString);
Alternatively applications oriented around the PostScript imaging
model can use the PostScript user path syntax instead:
const char *psPathString =
// star
"100 180 moveto"
" 40 10 lineto 190 120 lineto 10 120 lineto 160 10 lineto closepath"
// heart
" 300 300 moveto"
" 100 400 100 200 300 100 curveto"
" 500 200 500 400 300 300 curveto closepath";
glPathStringNV(pathObj, GL_PATH_FORMAT_PS_NV,
(GLsizei)strlen(psPathString), psPathString);
The PostScript path syntax also supports compact and precise binary
encoding and includes PostScript-style circular arcs.
Or the path's command and coordinates can be specified explicitly:
static const GLubyte pathCommands
=
{ GL_MOVE_TO_NV, GL_LINE_TO_NV, GL_LINE_TO_NV, GL_LINE_TO_NV,
GL_LINE_TO_NV, GL_CLOSE_PATH_NV,
'M', 'C', 'C', 'Z' }; // character aliases
static const GLshort pathCoords[12
[2] =
{ {100, 180}, {40, 10}, {190, 120}, {10, 120}, {160, 10},
{300,300}, {100,400}, {100,200}, {300,100},
{500,200}, {500,400}, {300,300} };
glPathCommandsNV(pathObj, 10, pathCommands, 24, GL_SHORT, pathCoords);
Before rendering to a window with a stencil buffer, clear the stencil
buffer to zero and the color buffer to black:
glClearStencil(0);
glClearColor(0,0,0,0);
glStencilMask(~0);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
Use an orthographic path-to-clip-space transform to map the
range of the star's path coordinates to the [-1..1
clip space cube:
glMatrixLoadIdentityEXT(GL_PROJECTION);
glMatrixLoadIdentityEXT(GL_MODELVIEW);
glMatrixOrthoEXT(GL_MODELVIEW, 0, 500, 0, 400, -1, 1);
Stencil the path:
glStencilFillPathNV(pathObj, GL_COUNT_UP_NV, 0x1F);
The 0x1F mask means the counting uses modulo-32 arithmetic. In
principle the star's path is simple enough (having a maximum winding
number of 2) that modulo-4 arithmetic would be sufficient so the mask
could be 0x3. Or a mask of all 1's (~0) could be used to count with
all available stencil bits.
Now that the coverage of the star and the heart have been rasterized
into the stencil buffer, cover the path with a non-zero fill style
(indicated by the GL_NOTEQUAL stencil function with a zero reference
value):
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NOTEQUAL, 0, 0x1F);
glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
glColor3f(1,1,0); // yellow
glCoverFillPathNV(pathObj, GL_BOUNDING_BOX_NV);
The result is a yellow star (with a filled center) to the left of
a yellow heart.
The GL_ZERO stencil operation ensures that any covered samples
(meaning those with non-zero stencil values) are zero'ed when
the path cover is rasterized. This allows subsequent paths to be
rendered without clearing the stencil buffer again.
A similar two-step rendering process can draw a white outline
over the star and heart.
Before rendering, configure the path object with desirable path
parameters for stroking. Specify a wider 6.5-unit stroke and
the round join style:
glPathParameteriNV(pathObj, GL_PATH_JOIN_STYLE_NV, GL_ROUND_NV);
glPathParameterfNV(pathObj, GL_PATH_STROKE_WIDTH_NV, 6.5);
Now stencil the path's stroked coverage into the stencil buffer,
setting the stencil to 0x1 for all stencil samples within the
transformed path.
glStencilStrokePathNV(pathObj, 0x1, ~0);
Cover the path's stroked coverage (with a hull this time instead
of a bounding box; the choice doesn't really matter here) while
stencil testing that writes white to the color buffer and again
zero the stencil buffer.
glColor3f(1,1,1); // white
glCoverStrokePathNV(pathObj, GL_CONVEX_HULL_NV);
In this example, constant color shading is used but the application
can specify their own arbitrary shading and/or blending operations,
whether with Cg compiled to fragment program assembly, GLSL, or
fixed-function fragment processing.
More complex path rendering is possible such as clipping one path to
another arbitrary path. This is because stencil testing (as well
as depth testing, depth bound test, clip planes, and scissoring)
can restrict path stenciling.
Now let's render the word "OpenGL" atop the star and heart.
First create a sequence of path objects for the glyphs for the
characters in "OpenGL":
GLuint glyphBase = glGenPathsNV(6);
const unsigned char *word = "OpenGL";
const GLsizei wordLen = (GLsizei)strlen(word);
const GLfloat emScale = 2048; // match TrueType convention
GLuint templatePathObject = ~0; // Non-existent path object
glPathGlyphsNV(glyphBase,
GL_SYSTEM_FONT_NAME_NV, "Helvetica", GL_BOLD_BIT_NV,
wordLen, GL_UNSIGNED_BYTE, word,
GL_SKIP_MISSING_GLYPH_NV, ~0, emScale);
glPathGlyphsNV(glyphBase,
GL_SYSTEM_FONT_NAME_NV, "Arial", GL_BOLD_BIT_NV,
wordLen, GL_UNSIGNED_BYTE, word,
GL_SKIP_MISSING_GLYPH_NV, ~0, emScale);
glPathGlyphsNV(glyphBase,
GL_STANDARD_FONT_NAME_NV, "Sans", GL_BOLD_BIT_NV,
wordLen, GL_UNSIGNED_BYTE, word,
GL_USE_MISSING_GLYPH_NV, ~0, emScale);
Glyphs are loaded for three different fonts in priority order:
Helvetica first, then Arial, and if neither of those loads, use the
standard sans-serif font. If a prior glPathGlyphsNV is successful
and specifies the path object range, the subsequent glPathGlyphsNV
commands silently avoid re-specifying the already existent path
objects.
Now query the (kerned) separations for the word "OpenGL" and build
a set of horizontal translations advancing each successive glyph by
its kerning distance with the following glyph.
GLfloat xtranslate[6+1]; // wordLen+1
glGetPathSpacingNV(GL_ACCUM_ADJACENT_PAIRS_NV,
wordLen+1, GL_UNSIGNED_BYTE,
" ", // repeat last letter twice
glyphBase,
1.0f, 1.0f,
GL_TRANSLATE_X_NV,
xtranslate);
Next determine the font-wide vertical minimum and maximum for the
font face by querying the per-font metrics of any one of the glyphs
from the font face.
GLfloat yMinMax[2];
glGetPathMetricRangeNV(GL_FONT_Y_MIN_BOUNDS_BIT_NV|GL_FONT_Y_MAX_BOUNDS_BIT_NV,
glyphBase, /*count*/1,
2*sizeof(GLfloat),
yMinMax);
Use an orthographic path-to-clip-space transform to map the
word's bounds to the [-1..1] clip space cube:
glMatrixLoadIdentityEXT(GL_PROJECTION);
glMatrixOrthoEXT(GL_MODELVIEW,
0, xtranslate
yMinMax[0
, yMinMax[1],
-1, 1);
Stencil the filled paths of the sequence of glyphs for "OpenGL",
each transformed by the appropriate 2D translations for spacing.
glStencilFillPathInstancedNV(6, GL_UNSIGNED_BYTE,
" ",
glyphBase,
GL_PATH_FILL_MODE_NV, 0xFF,
GL_TRANSLATE_X_NV, xtranslate);
Cover the bounding box union of the glyphs with 50% gray.
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NOTEQUAL, 0, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
glColor3f(0.5,0.5,0.5); // 50% gray
glCoverFillPathInstancedNV(6, GL_UNSIGNED_BYTE,
" ",
glyphBase,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV,
GL_TRANSLATE_2D_NV, xtranslate);
Voila, the word "OpenGL" in gray is now stenciled into the framebuffer.
Instead of solid 50% gray, the cover operation can apply a linear
gradient that changes from green (RGB=0,1,0) at the top of the word
"OpenGL" to blue (RGB=0,0,1) at the bottom of "OpenGL":
GLfloat rgbGen
= {
0, 0, 0, // red = constant zero
0, 1, 0, // green = varies with y from bottom (0) to top (1)
0, -1, 1 // blue = varies with y from bottom (1) to top (0)
};
glPathColorGenNV(GL_PRIMARY_COLOR, GL_PATH_OBJECT_BOUNDING_BOX_NV,
GL_RGB, &rgbGen[0
[0]);
Instead of loading just the glyphs for the characters in "OpenGL",
the entire character set could be loaded. This allows the characters
of the string to be mapped (offset by the glyphBase) to path object names.
A range of glyphs can be loaded like this:
const int numChars = 256; // ISO/IEC 8859-1 8-bit character range
GLuint glyphBase = glGenPathsNV(numChars);
glPathGlyphRangeNV(glyphBase,
GL_SYSTEM_FONT_NAME_NV, "Helvetica", GL_BOLD_BIT_NV,
0, numChars,
GL_SKIP_MISSING_GLYPH_NV, ~0, emScale);
glPathGlyphRangeNV(glyphBase,
GL_SYSTEM_FONT_NAME_NV, "Arial", GL_BOLD_BIT_NV,
0, numChars,
GL_SKIP_MISSING_GLYPH_NV, ~0, emScale);
glPathGlyphRangeNV(glyphBase,
GL_STANDARD_FONT_NAME_NV, "Sans", GL_BOLD_BIT_NV,
0, numChars,
GL_USE_MISSING_GLYPH_NV, ~0, emScale);
Given a range of glyphs loaded as path objects, (kerned) spacing
information can now be queried for the string:
glGetPathSpacingNV(GL_ACCUM_ADJACENT_PAIRS_NV,
7, GL_UNSIGNED_BYTE, "OpenGLL", // repeat L to get final spacing
glyphBase,
1.0f, 1.0f,
GL_TRANSLATE_X_NV,
kerning);
Using the range of glyphs, stenciling and covering the instanced
paths for "OpenGL" can be done this way:
glStencilFillPathInstancedNV(6, GL_UNSIGNED_BYTE, "OpenGL",
glyphBase,
GL_PATH_FILL_MODE_NV, 0xFF,
GL_TRANSLATE_2D_NV, xtranslate);
glCoverFillPathInstancedNV(6, GL_UNSIGNED_BYTE, "OpenGL",
glyphBase,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV,
GL_TRANSLATE_2D_NV, xtranslate);
XXX add path clipping example to demonstrate glPathStencilFuncNV.
The official definition of this extension is available here:
http://www.opengl.org/registry/specs/NV/path_rendering.txt
Functions
Constants
GL_ACCUM_ADJACENT_PAIRS_NV (37037)
GL_ADJACENT_PAIRS_NV (37038)
GL_AFFINE_2D_NV (37010)
GL_AFFINE_3D_NV (37012)
GL_ARC_TO_NV (254)
GL_BEVEL_NV (37030)
GL_BOLD_BIT_NV (1)
GL_BOUNDING_BOX_NV (37005)
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV (37020)
GL_CIRCULAR_CCW_ARC_TO_NV (248)
GL_CIRCULAR_CW_ARC_TO_NV (250)
GL_CIRCULAR_TANGENT_ARC_TO_NV (252)
GL_CLOSE_PATH_NV (0)
GL_CONVEX_HULL_NV (37003)
GL_COUNT_DOWN_NV (37001)
GL_COUNT_UP_NV (37000)
GL_CUBIC_CURVE_TO_NV (12)
GL_DUP_FIRST_CUBIC_CURVE_TO_NV (242)
GL_DUP_LAST_CUBIC_CURVE_TO_NV (244)
GL_FILE_NAME_NV (36980)
GL_FIRST_TO_REST_NV (37039)
GL_FONT_ASCENDER_BIT_NV (2097152)
GL_FONT_DESCENDER_BIT_NV (4194304)
GL_FONT_HAS_KERNING_BIT_NV (268435456)
GL_FONT_HEIGHT_BIT_NV (8388608)
GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV (33554432)
GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV (16777216)
GL_FONT_UNDERLINE_POSITION_BIT_NV (67108864)
GL_FONT_UNDERLINE_THICKNESS_BIT_NV (134217728)
GL_FONT_UNITS_PER_EM_BIT_NV (1048576)
GL_FONT_X_MAX_BOUNDS_BIT_NV (262144)
GL_FONT_X_MIN_BOUNDS_BIT_NV (65536)
GL_FONT_Y_MAX_BOUNDS_BIT_NV (524288)
GL_FONT_Y_MIN_BOUNDS_BIT_NV (131072)
GL_GLYPH_HAS_KERNING_BIT_NV (256)
GL_GLYPH_HEIGHT_BIT_NV (2)
GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV (16)
GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV (4)
GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV (8)
GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV (128)
GL_GLYPH_VERTICAL_BEARING_X_BIT_NV (32)
GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV (64)
GL_GLYPH_WIDTH_BIT_NV (1)
GL_HORIZONTAL_LINE_TO_NV (6)
GL_ITALIC_BIT_NV (2)
GL_LARGE_CCW_ARC_TO_NV (22)
GL_LARGE_CW_ARC_TO_NV (24)
GL_LINE_TO_NV (4)
GL_MITER_REVERT_NV (37031)
GL_MITER_TRUNCATE_NV (37032)
GL_MOVE_TO_CONTINUES_NV (37046)
GL_MOVE_TO_NV (2)
GL_MOVE_TO_RESETS_NV (37045)
GL_PATH_CLIENT_LENGTH_NV (36991)
GL_PATH_COMMAND_COUNT_NV (37021)
GL_PATH_COMPUTED_LENGTH_NV (37024)
GL_PATH_COORD_COUNT_NV (37022)
GL_PATH_COVER_DEPTH_FUNC_NV (37055)
GL_PATH_DASH_ARRAY_COUNT_NV (37023)
GL_PATH_DASH_CAPS_NV (36987)
GL_PATH_DASH_OFFSET_NV (36990)
GL_PATH_DASH_OFFSET_RESET_NV (37044)
GL_PATH_END_CAPS_NV (36982)
GL_PATH_ERROR_POSITION_NV (37035)
GL_PATH_FILL_BOUNDING_BOX_NV (37025)
GL_PATH_FILL_COVER_MODE_NV (36994)
GL_PATH_FILL_MASK_NV (36993)
GL_PATH_FILL_MODE_NV (36992)
GL_PATH_FOG_GEN_MODE_NV (37036)
GL_PATH_FORMAT_PS_NV (36977)
GL_PATH_FORMAT_SVG_NV (36976)
GL_PATH_GEN_COEFF_NV (37041)
GL_PATH_GEN_COLOR_FORMAT_NV (37042)
GL_PATH_GEN_COMPONENTS_NV (37043)
GL_PATH_GEN_MODE_NV (37040)
GL_PATH_INITIAL_DASH_CAP_NV (36988)
GL_PATH_INITIAL_END_CAP_NV (36983)
GL_PATH_JOIN_STYLE_NV (36985)
GL_PATH_MITER_LIMIT_NV (36986)
GL_PATH_OBJECT_BOUNDING_BOX_NV (37002)
GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV (37053)
GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV (37054)
GL_PATH_STENCIL_FUNC_NV (37047)
GL_PATH_STENCIL_REF_NV (37048)
GL_PATH_STENCIL_VALUE_MASK_NV (37049)
GL_PATH_STROKE_BOUNDING_BOX_NV (37026)
GL_PATH_STROKE_COVER_MODE_NV (36995)
GL_PATH_STROKE_MASK_NV (36996)
GL_PATH_STROKE_WIDTH_NV (36981)
GL_PATH_TERMINAL_DASH_CAP_NV (36989)
GL_PATH_TERMINAL_END_CAP_NV (36984)
GL_PRIMARY_COLOR (34167)
GL_PRIMARY_COLOR_NV (34092)
GL_QUADRATIC_CURVE_TO_NV (10)
GL_RECT_NV (246)
GL_RELATIVE_ARC_TO_NV (255)
GL_RELATIVE_CUBIC_CURVE_TO_NV (13)
GL_RELATIVE_HORIZONTAL_LINE_TO_NV (7)
GL_RELATIVE_LARGE_CCW_ARC_TO_NV (23)
GL_RELATIVE_LARGE_CW_ARC_TO_NV (25)
GL_RELATIVE_LINE_TO_NV (5)
GL_RELATIVE_MOVE_TO_NV (3)
GL_RELATIVE_QUADRATIC_CURVE_TO_NV (11)
GL_RELATIVE_SMALL_CCW_ARC_TO_NV (19)
GL_RELATIVE_SMALL_CW_ARC_TO_NV (21)
GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV (17)
GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV (15)
GL_RELATIVE_VERTICAL_LINE_TO_NV (9)
GL_RESTART_PATH_NV (240)
GL_ROUND_NV (37028)
GL_SECONDARY_COLOR_NV (34093)
GL_SKIP_MISSING_GLYPH_NV (37033)
GL_SMALL_CCW_ARC_TO_NV (18)
GL_SMALL_CW_ARC_TO_NV (20)
GL_SMOOTH_CUBIC_CURVE_TO_NV (16)
GL_SMOOTH_QUADRATIC_CURVE_TO_NV (14)
GL_SQUARE_NV (37027)
GL_STANDARD_FONT_NAME_NV (36978)
GL_SYSTEM_FONT_NAME_NV (36979)
GL_TRANSLATE_2D_NV (37008)
GL_TRANSLATE_3D_NV (37009)
GL_TRANSLATE_X_NV (37006)
GL_TRANSLATE_Y_NV (37007)
GL_TRANSPOSE_AFFINE_2D_NV (37014)
GL_TRANSPOSE_AFFINE_3D_NV (37016)
GL_TRIANGULAR_NV (37029)
GL_USE_MISSING_GLYPH_NV (37034)
GL_UTF16_NV (37019)
GL_UTF8_NV (37018)
GL_VERTICAL_LINE_TO_NV (8)