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":
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)