Name
NV_path_rendering
Name Strings
GL_NV_path_rendering
Contact
Mark Kilgard, NVIDIA (mjk 'at' nvidia.com)
Contributors
Jeff Bolz, NVIDIA
Chris Dalton, NVIDIA
Pierre-Loup Griffais, NVIDIA
Scott Nations, NVIDIA
Status
Released in NVIDIA Driver Release 275.33 (June 2011).
Substantially optimized in NVIDIA Driver Release 301.42 (May 2012).
Further optimized in NVIDIA Driver Release 314.xx (February 2013).
Version
Last Modified Date: November 5, 2013
Version: 12
Number
410
Dependencies
This extension is written against the OpenGL 3.2 Specification with
Compatibility Profile but can apply to OpenGL 1.1 and up.
For OpenGL 3.x and up, ARB_compatibility is required.
EXT_direct_state_access commands are used in specifying portions
of this extension but EXT_direct_state_access is not required to
implement this extension as long as the functionality implemented
is equivalent to the EXT_direct_state_access commands.
Overview
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[10] =
{ 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
[0..500]x[0..400] 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,
"\000\001\002\003\004\005\005", // 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[6], 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,
"\000\001\002\003\004\005",
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,
"\000\001\002\003\004\005",
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[3][3] = {
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.
New Procedures and Functions
PATH SPECIFICATION COMMANDS
void PathCommandsNV(uint path,
sizei numCommands, const ubyte *commands,
sizei numCoords, enum coordType,
const void *coords);
void PathCoordsNV(uint path,
sizei numCoords, enum coordType,
const void *coords);
void PathSubCommandsNV(uint path,
sizei commandStart, sizei commandsToDelete,
sizei numCommands, const ubyte *commands,
sizei numCoords, enum coordType,
const void *coords);
void PathSubCoordsNV(uint path,
sizei coordStart,
sizei numCoords, enum coordType,
const void *coords);
void PathStringNV(uint path, enum format,
sizei length, const void *pathString);
void PathGlyphsNV(uint firstPathName,
enum fontTarget,
const void *fontName,
bitfield fontStyle,
sizei numGlyphs, enum type,
const void *charcodes,
enum handleMissingGlyphs,
uint pathParameterTemplate,
float emScale);
void PathGlyphRangeNV(uint firstPathName,
enum fontTarget,
const void *fontName,
bitfield fontStyle,
uint firstGlyph,
sizei numGlyphs,
enum handleMissingGlyphs,
uint pathParameterTemplate,
float emScale);
void WeightPathsNV(uint resultPath,
sizei numPaths,
const uint paths[], const float weights[]);
void CopyPathNV(uint resultPath, uint srcPath);
void InterpolatePathsNV(uint resultPath,
uint pathA, uint pathB,
float weight);
void TransformPathNV(uint resultPath,
uint srcPath,
enum transformType,
const float *transformValues);
void PathParameterivNV(uint path, enum pname, const int *value);
void PathParameteriNV(uint path, enum pname, int value);
void PathParameterfvNV(uint path, enum pname, const float *value);
void PathParameterfNV(uint path, enum pname, float value);
void PathDashArrayNV(uint path,
sizei dashCount, const float *dashArray);
PATH NAME MANAGEMENT
uint GenPathsNV(sizei range);
void DeletePathsNV(uint path, sizei range);
boolean IsPathNV(uint path);
PATH STENCILING
void PathStencilFuncNV(enum func, int ref, uint mask);
void PathStencilDepthOffsetNV(float factor, float units);
void StencilFillPathNV(uint path,
enum fillMode, uint mask);
void StencilStrokePathNV(uint path,
int reference, uint mask);
void StencilFillPathInstancedNV(sizei numPaths,
enum pathNameType, const void *paths,
uint pathBase,
enum fillMode, uint mask,
enum transformType,
const float *transformValues);
void StencilStrokePathInstancedNV(sizei numPaths,
enum pathNameType, const void *paths,
uint pathBase,
int reference, uint mask,
enum transformType,
const float *transformValues);
PATH COVERING
void PathCoverDepthFuncNV(enum zfunc);
void PathColorGenNV(enum color,
enum genMode,
enum colorFormat, const float *coeffs);
void PathTexGenNV(enum texCoordSet,
enum genMode,
int components, const float *coeffs);
void PathFogGenNV(enum genMode);
void CoverFillPathNV(uint path, enum coverMode);
void CoverStrokePathNV(uint path, enum coverMode);
void CoverFillPathInstancedNV(sizei numPaths,
enum pathNameType, const void *paths,
uint pathBase,
enum coverMode,
enum transformType,
const float *transformValues);
void CoverStrokePathInstancedNV(sizei numPaths,
enum pathNameType, const void *paths,
uint pathBase,
enum coverMode,
enum transformType,
const float *transformValues);
PATH QUERIES
void GetPathParameterivNV(uint path, enum pname, int *value);
void GetPathParameterfvNV(uint path, enum pname, float *value);
void GetPathCommandsNV(uint path, ubyte *commands);
void GetPathCoordsNV(uint path, float *coords);
void GetPathDashArrayNV(uint path, float *dashArray);
void GetPathMetricsNV(bitfield metricQueryMask,
sizei numPaths,
enum pathNameType, const void *paths,
uint pathBase,
sizei stride,
float *metrics);
void GetPathMetricRangeNV(bitfield metricQueryMask,
uint firstPathName,
sizei numPaths,
sizei stride,
float *metrics);
void GetPathSpacingNV(enum pathListMode,
sizei numPaths,
enum pathNameType, const void *paths,
uint pathBase,
float advanceScale,
float kerningScale,
enum transformType,
float *returnedSpacing);
void GetPathColorGenivNV(enum color, enum pname, int *value);
void GetPathColorGenfvNV(enum color, enum pname, float *value);
void GetPathTexGenivNV(enum texCoordSet, enum pname, int *value);
void GetPathTexGenfvNV(enum texCoordSet, enum pname, float *value);
boolean IsPointInFillPathNV(uint path,
uint mask, float x, float y);
boolean IsPointInStrokePathNV(uint path,
float x, float y);
float GetPathLengthNV(uint path,
sizei startSegment, sizei numSegments);
boolean PointAlongPathNV(uint path,
sizei startSegment, sizei numSegments,
float distance,
float *x, float *y,
float *tangentX, float *tangentY);
New Tokens
Accepted in elements of the array parameter of
PathCommandsNV and PathSubCommandsNV:
CLOSE_PATH_NV 0x00
MOVE_TO_NV 0x02
RELATIVE_MOVE_TO_NV 0x03
LINE_TO_NV 0x04
RELATIVE_LINE_TO_NV 0x05
HORIZONTAL_LINE_TO_NV 0x06
RELATIVE_HORIZONTAL_LINE_TO_NV 0x07
VERTICAL_LINE_TO_NV 0x08
RELATIVE_VERTICAL_LINE_TO_NV 0x09
QUADRATIC_CURVE_TO_NV 0x0A
RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B
CUBIC_CURVE_TO_NV 0x0C
RELATIVE_CUBIC_CURVE_TO_NV 0x0D
SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E
RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F
SMOOTH_CUBIC_CURVE_TO_NV 0x10
RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11
SMALL_CCW_ARC_TO_NV 0x12
RELATIVE_SMALL_CCW_ARC_TO_NV 0x13
SMALL_CW_ARC_TO_NV 0x14
RELATIVE_SMALL_CW_ARC_TO_NV 0x15
LARGE_CCW_ARC_TO_NV 0x16
RELATIVE_LARGE_CCW_ARC_TO_NV 0x17
LARGE_CW_ARC_TO_NV 0x18
RELATIVE_LARGE_CW_ARC_TO_NV 0x19
RESTART_PATH_NV 0xF0
DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2
DUP_LAST_CUBIC_CURVE_TO_NV 0xF4
RECT_NV 0xF6
CIRCULAR_CCW_ARC_TO_NV 0xF8
CIRCULAR_CW_ARC_TO_NV 0xFA
CIRCULAR_TANGENT_ARC_TO_NV 0xFC
ARC_TO_NV 0xFE
RELATIVE_ARC_TO_NV 0xFF
Accepted by the parameter of PathStringNV:
PATH_FORMAT_SVG_NV 0x9070
PATH_FORMAT_PS_NV 0x9071
Accepted by the parameter of PathGlyphsNV and
PathGlyphRangeNV:
STANDARD_FONT_NAME_NV 0x9072
SYSTEM_FONT_NAME_NV 0x9073
FILE_NAME_NV 0x9074
Accepted by the parameter of PathGlyphsNV and
PathGlyphRangeNV:
SKIP_MISSING_GLYPH_NV 0x90A9
USE_MISSING_GLYPH_NV 0x90AA
Accepted by the parameter of PathParameterfNV,
PathParameterfvNV, GetPathParameterfvNV, PathParameteriNV,
PathParameterivNV, and GetPathParameterivNV:
PATH_STROKE_WIDTH_NV 0x9075
PATH_INITIAL_END_CAP_NV 0x9077
PATH_TERMINAL_END_CAP_NV 0x9078
PATH_JOIN_STYLE_NV 0x9079
PATH_MITER_LIMIT_NV 0x907A
PATH_INITIAL_DASH_CAP_NV 0x907C
PATH_TERMINAL_DASH_CAP_NV 0x907D
PATH_DASH_OFFSET_NV 0x907E
PATH_CLIENT_LENGTH_NV 0x907F
PATH_DASH_OFFSET_RESET_NV 0x90B4
PATH_FILL_MODE_NV 0x9080
PATH_FILL_MASK_NV 0x9081
PATH_FILL_COVER_MODE_NV 0x9082
PATH_STROKE_COVER_MODE_NV 0x9083
PATH_STROKE_MASK_NV 0x9084
Accepted by the parameter of PathParameterfNV and
PathParameterfvNV:
PATH_END_CAPS_NV 0x9076
PATH_DASH_CAPS_NV 0x907B
Accepted by the parameter of StencilFillPathNV and
StencilFillPathInstancedNV:
INVERT
COUNT_UP_NV 0x9088
COUNT_DOWN_NV 0x9089
PATH_FILL_MODE_NV see above
Accepted by the parameter of PathColorGenNV,
GetPathColorGenivNV, and GetPathColorGenfvNV:
PRIMARY_COLOR 0x8577 // from OpenGL 1.3
PRIMARY_COLOR_NV 0x852C // from NV_register_combiners
SECONDARY_COLOR_NV 0x852D // from NV_register_combiners
Accepted by the parameter of PathColorGenNV and
PathTexGenNV:
NONE
EYE_LINEAR
OBJECT_LINEAR
PATH_OBJECT_BOUNDING_BOX_NV 0x908A
Accepted by the parameter of CoverFillPathNV and
CoverFillPathInstancedNV:
CONVEX_HULL_NV 0x908B
BOUNDING_BOX_NV 0x908D
PATH_FILL_COVER_MODE_NV see above
Accepted by the parameter of CoverStrokePathNV and
CoverStrokePathInstancedNV:
CONVEX_HULL_NV see above
BOUNDING_BOX_NV see above
PATH_STROKE_COVER_MODE_NV see above
Accepted by the parameter of
StencilFillPathInstancedNV, StencilStrokePathInstancedNV,
CoverFillPathInstancedNV, and CoverStrokePathInstancedNV:
NONE
TRANSLATE_X_NV 0x908E
TRANSLATE_Y_NV 0x908F
TRANSLATE_2D_NV 0x9090
TRANSLATE_3D_NV 0x9091
AFFINE_2D_NV 0x9092
AFFINE_3D_NV 0x9094
TRANSPOSE_AFFINE_2D_NV 0x9096
TRANSPOSE_AFFINE_3D_NV 0x9098
Accepted by the parameter of TransformPathNV:
NONE
TRANSLATE_X_NV see above
TRANSLATE_Y_NV see above
TRANSLATE_2D_NV see above
TRANSLATE_3D_NV see above
AFFINE_2D_NV see above
AFFINE_3D_NV see above
TRANSPOSE_AFFINE_2D_NV see above
TRANSPOSE_AFFINE_3D_NV see above
Accepted by the or parameter of
StencilFillPathInstancedNV, StencilStrokePathInstancedNV,
CoverFillPathInstancedNV, CoverStrokePathInstancedNV,
GetPathMetricsNV, and GetPathSpacingNV:
UTF8_NV 0x909A
UTF16_NV 0x909B
Accepted by the parameter of CoverFillPathInstancedNV:
CONVEX_HULL_NV see above
BOUNDING_BOX_NV see above
BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C
PATH_FILL_COVER_MODE_NV see above
Accepted by the parameter of CoverStrokePathInstancedNV:
CONVEX_HULL_NV see above
BOUNDING_BOX_NV see above
BOUNDING_BOX_OF_BOUNDING_BOXES_NV see above
PATH_STROKE_COVER_MODE_NV see above
Accepted by the parameter of GetPathParameterfvNV and
GetPathParameterivNV:
PATH_COMMAND_COUNT_NV 0x909D
PATH_COORD_COUNT_NV 0x909E
PATH_DASH_ARRAY_COUNT_NV 0x909F
PATH_COMPUTED_LENGTH_NV 0x90A0
PATH_OBJECT_BOUNDING_BOX_NV see above
PATH_FILL_BOUNDING_BOX_NV 0x90A1
PATH_STROKE_BOUNDING_BOX_NV 0x90A2
Accepted by the parameter of PathParameterfNV,
PathParameterfvNV, PathParameteriNV, and PathParameterivNV
when is one of PATH_END_CAPS_NV, PATH_INTIAL_END_CAP_NV,
PATH_TERMINAL_END_CAP_NV, PATH_DASH_CAPS_NV, PATH_INITIAL_DASH_CAP_NV,
and PATH_TERMINAL_DASH_CAP_NV:
FLAT
SQUARE_NV 0x90A3
ROUND_NV 0x90A4
TRIANGULAR_NV 0x90A5
Accepted by the parameter of PathParameterfNV,
PathParameterfvNV, PathParameteriNV, and PathParameterivNV
when is PATH_JOIN_STYLE_NV:
NONE
ROUND_NV see above
BEVEL_NV 0x90A6
MITER_REVERT_NV 0x90A7
MITER_TRUNCATE_NV 0x90A8
Accepted by the parameter of PathParameterfNV,
PathParameterfvNV, PathParameteriNV, and PathParameterivNV when
is PATH_DASH_OFFSET_RESET_NV
MOVE_TO_RESETS_NV 0x90B5
MOVE_TO_CONTINUES_NV 0x90B6
Accepted by the parameter of PathGlyphsNV and
PathGlyphRangeNV:
NONE
BOLD_BIT_NV 0x01
ITALIC_BIT_NV 0x02
Accepted by the parameter of GetBooleanv, GetIntegerv,
GetInteger64v, GetFloatv, and GetDoublev:
PATH_ERROR_POSITION_NV 0x90AB
PATH_FOG_GEN_MODE_NV 0x90AC
PATH_STENCIL_FUNC_NV 0x90B7
PATH_STENCIL_REF_NV 0x90B8
PATH_STENCIL_VALUE_MASK_NV 0x90B9
PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD
PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE
PATH_COVER_DEPTH_FUNC_NV 0x90BF
Accepted as a bit within the parameter of
GetPathMetricRangeNV or GetPathMetricsNV:
// per-glyph metrics
GLYPH_WIDTH_BIT_NV 0x01
GLYPH_HEIGHT_BIT_NV 0x02
GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04
GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08
GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10
GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20
GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40
GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80
GLYPH_HAS_KERNING_BIT_NV 0x100
// per-font face metrics
FONT_X_MIN_BOUNDS_BIT_NV 0x00010000
FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000
FONT_X_MAX_BOUNDS_BIT_NV 0x00040000
FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000
FONT_UNITS_PER_EM_BIT_NV 0x00100000
FONT_ASCENDER_BIT_NV 0x00200000
FONT_DESCENDER_BIT_NV 0x00400000
FONT_HEIGHT_BIT_NV 0x00800000
FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000
FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000
FONT_UNDERLINE_POSITION_BIT_NV 0x04000000
FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000
FONT_HAS_KERNING_BIT_NV 0x10000000
Accepted by the parameter of GetPathSpacingNV:
ACCUM_ADJACENT_PAIRS_NV 0x90AD
ADJACENT_PAIRS_NV 0x90AE
FIRST_TO_REST_NV 0x90AF
Accepted by the parameter of GetPathColorGenivNV,
GetPathColorGenfvNV, GetPathTexGenivNV and GetPathTexGenfvNV:
PATH_GEN_MODE_NV 0x90B0
PATH_GEN_COEFF_NV 0x90B1
PATH_GEN_COLOR_FORMAT_NV 0x90B2
PATH_GEN_COMPONENTS_NV 0x90B3
Additions to Chapter 2 of the OpenGL 3.2 (unabridged) Specification
(OpenGL Operation)
None
Additions to Chapter 3 of the OpenGL 3.2 (unabridged) Specification
(Rasterization)
None
Additions to Chapter 4 of the OpenGL 3.2 (unabridged) Specification
(Per-Fragment Operations and the Frame Buffer)
None
Additions to Chapter 5 of the OpenGL 3.2 (unabridged) Specification
(Special Functions)
-- Insert section 5.X "Path Rendering" after 5.3 "Feedback"
5.X Path Rendering
5.X.1 Path Specification
PATH COMMANDS
Paths are specified as a sequence of path commands; each path command
has an associated sequence of floating-point coordinates with the
number of such coordinates depending on the specific path command.
Coordinates are specified in a sequence independent from the path
command sequence; coordinates from the coordinate sequence are matched
up with (associated with) commands, in the order of the command,
with coordinates extracted from the front of the coordinate sequence.
Valid path commands are listed in table 5.pathCommands. Each path
command is listed with its associated token, description, character
alias, count of associated coordinates.
As an example of how path commands associated with path coordinates,
if the command sequence was MOVE_TO_NV, LINE_TO_NV, CUBIC_CURVE_TO_NV,
CLOSE_PATH_NV and the coordinates were 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, the MOVE_TO_NV command would be matched to coordinates 1 and 2,
LINE_TO_NV would be matched to 3 and 4, CUBIC_CURVE_TO_NV would be
matched to 5, 6, 7, 8, 9, 10, and CLOSE_PATH_NV would be matched to
no coordinates.
Path commands are processed in their sequence order to generate the
path's outline. The outline generation process maintains three 2D
(x,y) state variables for each path processed: the start position
(sp), the current position (cp), and the prior end point (pep);
/sp/, /cp/ and /pep/ are initially (0,0) when a path starts being
processed.
Table 5.pathCommands: Path Commands
Character Coordinate
Token Description alias count
========================== ===================== ========== ==========
MOVE_TO_NV Absolute move 'M' 2
current point
RELATIVE_MOVE_TO_NV Relative move 'm' 2
current point
-------------------------- --------------------- ---------- ----------
CLOSE_PATH_NV Close path 'Z' or 'z' 0
RESTART_PATH_NV Reset the path - 0
-------------------------- --------------------- ---------- ----------
LINE_TO_NV Absolute line 'L' 2
RELATIVE_LINE_TO_NV Relative line 'l' 2
-------------------------- --------------------- ---------- ----------
HORIZONTAL_LINE_TO_NV Absolute horizontal 'H' 1
line
RELATIVE_HORIZONTAL- Relative horizontal 'h' 1
_LINE_TO_NV line
VERTICAL_LINE_TO_NV Absolute vertical 'V' 1
line
RELATIVE_VERTICAL- Relative vertical 'v' 1
_LINE_TO_NV line
-------------------------- --------------------- ---------- ----------
QUADRATIC_CURVE_TO_NV Absolute quadratic 'Q' 4
Bezier segment
RELATIVE- Relative quadratic 'q' 4
_QUADRATIC_CURVE_TO_NV- Bezier segment
CUBIC_CURVE_TO_NV Absolute cubic 'C' 6
Bezier segment
RELATIVE_CUBIC_CURVE_TO_NV Relative cubic 'c' 6
Bezier segment
-------------------------- --------------------- ---------- ----------
SMOOTH- Absolute smooth 'T' 2
_QUADRATIC_CURVE_TO_NV quadratic Bezier
segment
RELATIVE_SMOOTH- Relative smooth 't' 2
_QUADRATIC_CURVE_TO_NV quadratic Bezier
segment
-------------------------- --------------------- ---------- ----------
SMOOTH- Absolute smooth 'S' 4
_CUBIC_CURVE_TO_NV cubic Bezier segment
RELATIVE_SMOOTH- Relative smooth 's' 4
_CUBIC_CURVE_TO_NV cubic Bezier segment
-------------------------- --------------------- ---------- ----------
SMALL_CCW_ARC_TO_NV Absolute small-sweep - 5
counterclockwise
partial elliptical
arc segment
RELATIVE- Relative small-sweep - 5
_SMALL_CCW_ARC_TO_NV counterclockwise
partial elliptical
arc segment
SMALL_CW_ARC_TO_NV Absolute small-sweep - 5
clockwise partial
elliptical arc
segment
RELATIVE- Relative small-sweep - 5
_SMALL_CW_ARC_TO_NV clockwise partial
elliptical arc
segment
LARGE_CCW_ARC_TO_NV Absolute large-sweep - 5
counterclockwise
partial elliptical
arc segment
RELATIVE- Relative large-sweep - 5
_LARGE_CCW_ARC_TO_NV counterclockwise
partial elliptical
arc segment
LARGE_CW_ARC_TO_NV Absolute large-sweep - 5
clockwise partial
elliptical arc
segment
RELATIVE- Relative large-sweep - 5
_SMALL_CW_ARC_TO_NV clockwise partial
elliptical arc
segment
-------------------------- --------------------- ---------- ----------
DUP_FIRST_- Absolute cubic Bezier - 4
CUBIC_CURVE_TO_NV segment, duplicating
first control point
DUP_LAST_CUBIC_CURVE_TO_NV Absolute cubic Bezier - 4
segment, duplicating
last control point
RECT_NV Closed rectangle - 4
-------------------------- --------------------- ---------- ----------
CIRCULAR_CCW_ARC_TO_NV Absolute - 5
counterclockwise
circular arc segment
CIRCULAR_CW_ARC_TO_NV Absolute clockwise - 5
circular arc segment
CIRCULAR_TANGENT_ARC_TO_NV Absolute circular - 5
tangential
arc segment
-------------------------- --------------------- ---------- ----------
ARC_TO_NV Absolute general 'A' 7
elliptical arc
RELATIVE_ARC_TO_NV Relative general 'a' 7
elliptical arc
-------------------------- --------------------- ---------- ----------
Table 5.pathEquations provides for each path command, as relevant,
the command's path segment parametric equation, equations for the
updated current point (ncp) and equations for the updated prior
end point (npep). After each command in a path is processed in the
sequence, the new current point, prior end point, and start point
(if changed) update the current point, prior end point, and start
point for the next path command to be processed in the sequence. So:
cp = ncp
pep = npep
Each path segment parametric equation is parameterized by a variable
/t/ ranging from 0.0 to 1.0. So the outline is traced by evaluating
each path command's path segment parametric equation continuously
as /t/ varies from 0.0 to 1.0.
With the exception of the MOVE_TO_NV, RESTART_PATH_NV,
RELATIVE_MOVE_TO_NV, RECT_NV, CIRCULAR_CCW_ARC_TO_NV, and
CIRCULAR_CW_ARC_TO_NV commands, the commands are specified such that
C0 continuity of the outline is guaranteed at path command segment
end-points.
The MOVE_TO_NV, RELATIVE_MOVE_TO_NV, RESTART_PATH_NV, RECT_NV,
CIRCULAR_CCW_ARC_TO_NV, and CIRCULAR_CW_ARC_TO_NV commands update the
start position (sp) to the value of these command's new current point
(ncp).
The MOVE_TO_NV, RELATIVE_MOVE_TO_NV, and RECT_NV commands
unconditionally change the start position (sp) to value of these
command's new current point (ncp) so:
sp = ncp
The CIRCULAR_CCW_ARC_TO_NV and CIRCULAR_CW_ARC_TO_NV commands
conditionally change sp to the command's ncp but only the sp has not
been specified by a prior command in the path's command sequence since
the beginning of the path's command sequence or last RESTART_PATH_NV.
When these circular arc commands change the sp to the command's ncp,
it implies the initial implicit line these commands generate from sp
to ncp will be zero length. (This behavior is to match the semantics
of PostScript.)
Moving of the start position creates a discontinuity in the outline
so starts a new subpath within the path.
Table 5.pathEquations: Path Equations
Path segment new current new prior end
Token parametric equation point equation point equation
========================== ====================================== ================== =====================
MOVE_TO_NV - ncp.x = c[0] npep.x = c[0]
ncp.y = c[1] npep.y = c[1]
RELATIVE_MOVE_TO_NV - ncp.x = cp.x+c[0] npep.x = cp.x+c[0]
ncp.y = cp.y+c[1] npep.y = cp.y+c[1]
-------------------------- -------------------------------------- ------------------ ---------------------
CLOSE_PATH_NV x = (1-t)*cp.x + t*sp.x ncp.x = sp.x npep.x = sp.x
y = (1-t)*cp.y + t*sp.y ncp.y = sp.y npep.y = sp.y
RESTART_PATH_NV - ncp.x = 0 npep.x = 0
ncp.y = 0 npep.y = 0
-------------------------- -------------------------------------- ------------------ ---------------------
LINE_TO_NV x = (1-t)*cp.x + t*c[0] ncp.x = c[0] npep.x = c[0]
y = (1-t)*cp.y + t*c[1] ncp.y = c[1] npep.y = c[1]
RELATIVE_LINE_TO_NV x = (1-t)*cp.x + t*(c[0]+cp.x) ncp.x = cp.x+c[0] npep.x = cp.x+c[0]
y = (1-t)*cp.y + t*(c[1]+cp.y) ncp.y = cp.y+c[1] npep.y = cp.y+c[1]
-------------------------- -------------------------------------- ------------------ ---------------------
HORIZONTAL_LINE_TO_NV x = (1-t)*cp.x + t*sp.x ncp.x = c[0] npep.x = c[0]
y = cp.y ncp.y = cp.y npep.y = cp.y
RELATIVE_HORIZONTAL- x = (1-t)*cp.x + t*(c[0]+cp.x) ncp.x = cp.x+c[0] npep.x = cp.x+c[0]
_LINE_TO_NV y = cp.y ncp.y = cp.y npep.y = cp.y
VERTICAL_LINE_TO_NV x = cp.x ncp.x = cp.x npep.x = cp.x
y = (1-t)*cp.y + t*sp.y ncp.y = c[0] npep.y = c[0]
RELATIVE_VERTICAL- x = cp.x ncp.x = cp.x npep.x = cp.x
_LINE_TO_NV y = (1-t)*cp.y + t*(c[0]+cp.y) ncp.y = cp.y+c[0] npep.y = cp.y+c[0]
-------------------------- -------------------------------------- ------------------ ---------------------
QUADRATIC_CURVE_TO_NV x = (1-t)^2*cp.x + ncp.x = c[2] npep.x = c[0]
2*(1-t)*t*c[0] + ncp.y = c[3] npep.y = c[1]
t^2*c[2]
y = (1-t)^2*cp.y +
2*(1-t)*t*c[1] +
t^2*c[3]
RELATIVE- x = (1-t)^2*cp.x + ncp.x = cp.x+c[2] npep.x = cp.x+c[0]
_QUADRATIC_CURVE_TO_NV 2*(1-t)*t*(c[0]+cp.x) + ncp.y = cp.x+c[3] npep.y = cp.y+c[1]
t^2*(c[2]+cp.x)
y = (1-t)^2*cp.y +
2*(1-t)*t*(c[1]+cp.y) +
t^2*(c[3]+cp.y)
-------------------------- -------------------------------------- ------------------ ---------------------
CUBIC_CURVE_TO_NV x = (1-t)^3*cp.x + ncp.x = c[4] npep.x = c[2]
3*(1-t)^2*t*c[0] + ncp.y = c[5] npep.y = c[3]
3*(1-t)*t^2*c[2] +
t^3*c[4]
y = (1-t)^3*cp.y +
3*(1-t)^2*t*c[1] +
3*(1-t)*t^2*c[3] +
t^3*c[5]
RELATIVE_CUBIC_CURVE_TO_NV x = (1-t)^3*cp.x + ncp.x = cp.x+c[4] npep.x = cp.x+c[2]
3*(1-t)^2*t*(c[0]+cp.x) + ncp.y = cp.y+c[5] npep.y = cp.y+c[3]
3*(1-t)*t^2*(c[2]+cp.x) +
t^3*(c[4]+cp.x)
y = (1-t)^3*cp.y +
3*(1-t)^2*t*(c[1]+cp.y) +
3*(1-t)*t^2*(c[3]+cp.y) +
t^3*(c[5]+cp.y)
-------------------------- -------------------------------------- ------------------ ---------------------
SMOOTH- x = (1-t)^2*cp.x + ncp.x = c[0] npep.x = 2*cp.x-pep.x
_QUADRATIC_CURVE_TO_NV 2*(1-t)*t*(2*cp.x-pep.x) + ncp.y = c[1] npep.y = 2*cp.y-pep.y
t^2*c[0]
y = (1-t)^2*cp.y +
2*(1-t)*t*(2*cp.y-pep.y) +
t^2*c[1]
RELATIVE_SMOOTH- x = (1-t)^2*cp.x + ncp.x = cp.x+c[0] npep.x = 2*cp.x-pep.x
QUADRATIC_CURVE_TO_NV 2*(1-t)*t*(2*cp.x-pep.x) + ncp.y = cp.y+c[1] npep.y = 2*cp.y-pep.y
t^2*(c[0]+cp.x)
y = (1-t)^2*cp.y +
2*(1-t)*t*(2*cp.y-pep.y) +
t^2*(c[1]+cp.y)
SMOOTH- x = (1-t)^3*cp.x + ncp.x = c[2] npep.x = c[0]
_CUBIC_CURVE_TO_NV 3*(1-t)^2*t*(2*cp.x-pep.x) + ncp.y = c[3] npep.y = c[1]
3*(1-t)*t^2*c[0] +
t^3*c[2]
y = (1-t)^3*cp.y +
3*(1-t)^2*t*(2*cp.y-pep.y) +
3*(1-t)*t^2*c[1] +
t^3*c[3]
RELATIVE_SMOOTH- x = (1-t)^3*cp.x + ncp.x = cp.x+c[2] npep.x = cp.x+c[0]
_CUBIC_CURVE_TO_NV 3*(1-t)^2*t*(2*cp.x-pep.x) + ncp.y = cp.y+c[3] npep.y = cp.y+c[1]
3*(1-t)*t^2*(c[0]+cp.x) +
t^3*(c[2]+cp.x)
y = (1-t)^3*cp.y +
3*(1-t)^2*t*(2*cp.y-pep.y) +
3*(1-t)*t^2*(c[1]+cp.y) +
t^3*(c[3]+cp.y)
-------------------------- -------------------------------------- ------------------ ---------------------
SMALL_CCW_ARC_TO_NV x = arc_x(c,rv,rh,phi, ncp.x = c[3] npep.x = c[3]
theta1,dtheta,t) ncp.y = c[4] npep.y = c[4]
y = arc_y(c,rv,rh,phi,
theta1,dtheta,t)
RELATIVE- x = arc_x(c,rv,rh,phi, ncp.x = c[3] npep.x = cp.x+c[3]
_SMALL_CCW_ARC_TO_NV theta1,dtheta,t) ncp.y = c[4] npep.y = cp.y+c[4]
y = arc_y(c,rv,rh,phi,
theta1,dtheta,t)
SMALL_CW_ARC_TO_NV x = arc_x(c,rv,rh,phi, ncp.x = c[3] npep.x = c[3]
theta1,dtheta,t) ncp.y = c[4] npep.y = c[4]
y = arc_y(c,rv,rh,phi,
theta1,dtheta,t)
RELATIVE- x = arc_x(c,rv,rh,phi, ncp.x = c[3] npep.x = cp.x+c[3]
_SMALL_CW_ARC_TO_NV theta1,dtheta,t) ncp.y = c[4] npep.y = cp.y+c[4]
y = arc_y(c,rv,rh,phi,
theta1,dtheta,t)
LARGE_CCW_ARC_TO_NV x = arc_x(c,rv,rh,phi, ncp.x = c[3] npep.x = c[3]
theta1,dtheta,t) ncp.y = c[4] npep.y = c[4]
y = arc_y(c,rv,rh,phi,
theta1,dtheta,t)
RELATIVE- x = arc_x(c,rv,rh,phi, ncp.x = c[3] npep.x = cp.x+c[3]
_LARGE_CCW_ARC_TO_NV theta1,dtheta,t) ncp.y = c[4] npep.y = cp.y+c[4]
y = arc_y(c,rv,rh,phi,
theta1,dtheta,t)
LARGE_CW_ARC_TO_NV x = arc_x(c,rv,rh,phi, ncp.x = c[3] npep.x = c[3]
theta1,dtheta,t) ncp.y = c[4] npep.y = c[4]
y = arc_y(c,rv,rh,phi,
theta1,dtheta,t)
RELATIVE- x = arc_x(c,rv,rh,phi, ncp.x = c[3] npep.x = cp.x+c[3]
_SMALL_CW_ARC_TO_NV theta1,dtheta,t) ncp.y = c[4] npep.y = cp.y+c[4]
y = arc_y(c,rv,rh,phi,
theta1,dtheta,t)
-------------------------- -------------------------------------- ------------------ ---------------------
DUP_FIRST- x = (1-t)^3*cp.x + ncp.x = c[2] npep.x = c[0]
CUBIC_CURVE_TO_NV 3*(1-t)^2*t*cp.x + ncp.y = c[3] npep.y = c[1]
3*(1-t)*t^2*c[0] +
t^3*c[2]
y = (1-t)^3*cp.y +
3*(1-t)^2*t*cp.y +
3*(1-t)*t^2*c[1] +
t^3*c[3]
DUP_LAST_CUBIC_CURVE_TO_NV x = (1-t)^3*cp.x + ncp.x = c[2] npep.x = c[2]
3*(1-t)^2*t*c[0] + ncp.y = c[3] npep.y = c[3]
3*(1-t)*t^2*c[2] +
t^3*c[2]
y = (1-t)^3*cp.y +
3*(1-t)^2*t*c[1] +
3*(1-t)*t^2*c[3] +
t^3*c[3]
RECT_NV / (1-4*t)*c[0] + ncp.x = c[0] npep.x = c[0]
| 4*t*(c[0]+c[2]), t<=0.25 ncp.y = c[1] npep.y = c[1]+c[3]
x = < c[0]+c[2], 0.25=0.5
/ (1-2*t)*cp.y + 2*t*A.y, t<=0.5
y = {
\ arc_y(c,rv,rh,phi,theta1,
\ dtheta,t*2-1), t>=0.5
CIRCULAR_CW_ARC_TO_NV / (1-2*t)*cp.x + 2*t*A.x, t<=0.5 ncp.x = B.x npep.x = B.x
x = { ncp.y = B.y npep.x = B.y
\ arc_x(c,rv,rh,phi,theta1,
\ dtheta,t*2-1) t>=0.5
/ (1-2*t)*cp.y + 2*t*A.y, t<=0.5
y = {
\ arc_y(c,rv,rh,phi,theta1,
\ dtheta,t*2-1), t>=0.5
CIRCULAR_TANGENT_ARC_TO_NV / (1-2*t)*cp.x + 2*t*C.x, t<=0.5 ncp.x = D.x npep.x = D.x
x = { ncp.y = D.y npep.x = D.y
\ arc_x(c,rv,rh,phi,theta1,
\ dtheta,t*2-1), t>=0.5
/ (1-2*t)*cp.y + 2*t*C.y, t<=0.5
y = {
\ arc_y(c,rv,rh,phi,theta1,
\ dtheta,t*2-1), t>=0.5
-------------------------- -------------------------------------- ------------------ ---------------------
ARC_TO_NV x = arc_x(c,rv,rh,phi, ncp.x = c[5] npep.x = c[5]
theta1,dtheta,t) ncp.y = c[6] npep.y = c[6]
y = arc_y(c,rv,rh,phi,
theta1,dtheta,t)
RELATIVE_ARC_TO_NV x = arc_x(c,rv,rh,phi, ncp.x = cp.x+c[5] npep.x = cp.x+c[5]
theta1,dtheta,t) ncp.y = cp.y+c[6] npep.y = cp.y+c[6]
y = arc_y(c,rv,rh,phi,
theta1,dtheta,t)
-------------------------- -------------------------------------- ------------------ ---------------------
In the equations in Table 5.pathEquations, c[i] is the /i/th (base
zero) coordinate of the coordinate sequence for the command; /cp/
is the 2D (x,y) current position from the prior command (for the
first command of a path object, /cp/ is (0,0)); /sp/ is the 2D (x,y)
start position for the current contour (for the first command of a
path object, /sp/ is (0,0)); /pep/ is the 2D (x,y) prior end position
from the prior end position (for the first command of a path object,
/pep/ is (0,0)); and /ncp/ is the 2D (x,y) "new" current position
that will become the current position for the subsequent command;
/npep/ is the 2D (x,y) "new" prior end position for the subsequent
command. The values /c/, /theta1/, /dtheta/ are explained in the
discussion of partial elliptical arc commands below. The values
of /rv/, /rh/, /phi/ come from Table 5.arcParameterSpecialization.
The values of /A/, /B/, /C/, and /D/ are discussed in the context
of Table 5.arcParameterSpecialization.
If a value specified for a coordinate (however the coordinate is
specified) or a value computed from these coordinates (as specified
in the discussion that follows) exceeds the implementation's maximum
representable value for a single-precision floating-point number,
the rendering behavior (discussed in section 5.X.2) of the specified
path and the value of said coordinate if queried (section 6.X.2)
is undefined. This is relevant because coordinates can be specified
explicitly but also relatively (by RELATIVE_* path commands) or
encoded in a string of otherwise arbitrary precision and range or
computed by weighting.
PARTIAL ELLIPTICAL ARC COMMAND DETAILS
In all the arc-based path commands, the parametric segment path
equations in Table 5.pathEquations are expressed in terms of the
functions /arc_x/ and /arc_y/.
Equation 5.generalParametricArc
arc_x(c,rv,rh,phi,theta1,dtheta,t) = cos(phi)*rh*cos(theta1+t*dtheta) -
sin(phi)*ry*sin(theta1+t*dtheta) + c.x
arc_y(c,rv,rh,phi,theta1,dtheta,t) = sin(phi)*rh*cos(theta1+t*dtheta) +
cos(phi)*ry*sin(theta1+t*dtheta) + c.y
This general form of a parametric partial elliptical arc computes
(x,y) 2D positions on the arc as /t/ ranges from 0.0 to 1.0 inclusive.
In addition to the varying /t/ parameter, these functions depend on
a 2D (x,y) center position /c/, a horizontal ellipse radius /rh/,
a vertical ellipse radius /ry/, a counterclockwise angle (in radians)
of an ellipse with respect to the x-axis /phi/, /theta1/ is the angle
(in radians) of the initial point on the partial arc, and /dtheta/
is the difference between the angle (in radians) of the terminal
point on the partial arc and /theta1/. The larger of /rh/ and /ry/
is the complete ellipse's major axis while the smaller of the two
is the complete ellipse's minor axis.
How these additional dependent parameters for /arc_x/ and /arc_y/
are determined depends on the specific arc path command as
detailed in Table 5.arcParameterSpecialization. Before explaining
how specific arc commands determine these dependent parameters,
the following discussion develops a general scheme for converting
general end-point representations of arcs to the partial elliptical
arc segment representation of Equation 5.generalParametricArc.
All the arc commands supported are specializations of this general
end-point representation. The general scheme is developed, specific
arc commands are specified as special cases of the general end-point
representation scheme for arcs.
In general, consider seven scalar values (/x1/, /y1/, /x2/,
/y2/, /phi/, /fA/, and /fS/) fully parameterizing a given partial
elliptical arc:
* a 2D position (x1,y1) at the start of a partial elliptical
arc segment
* a 2D position (x2,y2) at the end of a partial elliptical
arc segment
* /phi/ is the angle (in radians) from the x-axis of the path
space coordinate system to the x-axis of the axis-aligned ellipse
* /fA/ is a boolean (the "large arc" flag) that is true when
the arc spans greater than 180 degrees; and otherwise false
if the arc sweeps 180 degrees or less
* /fS/ is a boolean (the "sweep" flag) that is true when the
arc sweeps in a counterclockwise direction in path space
(so sweeps with increasing angles); and otherwise false
when the arc sweeps in a clockwise direction (so sweeps with
decreasing angles)
Given this parameterization, the procedure below computes the /c/,
/rv/, /rh/, /phi/, /theta1/, and /dtheta/ parameters to represent
this same arc in the general parametric form of Equation
5.generalParametricArc.
Step 1:
x1p = cos(phi)*(x1-x2)/2 + sin(phi)*(y1-y2)/2
y1p = -sin(phi)*(x1-x2)/2 + cos(phi)*(y1-y2)/2
If /rh/, /rv/, and /phi/ are such that there is no solution
(basically, the ellipse is not big enough to reach from (x1,y1)
to (x2,y2), then the ellipse is scaled up uniformly until there
is exactly one solution (until the ellipse is just big enough)
in this manner:
lambda = (x1p/rh)^2 + (y1p/rv)^2
/ rh, lambda<=1
rp.x = {
\ rh*sqrt(lambda), lambda>1
/ rv, lambda<=1
rp.y = {
\ rv*sqrt(lambda), lambda>1
Step 2:
cp.x = fsgn*sqrt((rp.x^2*rp.y^2 - rp.x^2*y1p^2 - rp.y^2*x1p^2) /
(rp.x^2*y1p^2 + rp.y^2*x1p^2)
) * rp.x*y1p/rp.y
cp.y = fsgn*sqrt((rp.x^2*rp.y^2 - rp.x^2*y1p^2 - rp.y^2*x1p^2) /
(rp.x^2*y1p^2 + rp.y^2*x1p^2)
) * -rp.y*x1p/rp.x
where
/ +1, fA != fS
fsgn = {
\ -1, fA = fS
Step 3:
c.x = cos(phi)*cp.x - sin(phi)*cyp + (x1+x2)/2
c.y = sin(phi)*cp.x + cos(phi)*cyp + (y1+y2)/2
In general, the angle between two vectors (u.x, u.y) and (v.x, v.y)
can be computed as
/ arcos(dot(u,v)/sqrt(dot(u,u))*sqrt(dot(v,v))), u.x*v.y-u.y*v.x>=0
angle(u,v) = {
\ -arcos(dot(u,v)/sqrt(dot(u,u))*sqrt(dot(v,v))), u.x*v.y-u.y*v.x<0
Step 4:
theta1 = angle([1,0],
[(x1p-cp.x)/r.x,(y1p-cp.y)/r.y])
dangle = angle([(x1p-cp.x)/r.x,(y1p-cp.y)/r.y],
[(-x1p-cp.x)/r.x,(-y1p-cp.y)/r.y])
/ dangle - 2*Pi, fS=false AND d>0
/
/ dangle, fS=false AND d<=0
dtheta = {
\ dangle, fS=true AND d>=0
\
\ dangle + 2*Pi, fS=true AND d<0
The arc path commands allow arbitrary numeric values so when these
values result in invalid or out-of-range parameters when the above
steps are applied, the following further steps are taken to ensure
well-defined behavior.
If (x1,y1) and (x2,y2) are identical, then this is equivalent to
omitting the arc segment entirely.
If either of /rh/ or /rv/ is zero, the arc is treated as a straight
line segment from (x1,y1) to (x2,y2).
Table 5.arcParameterSpecialization now maps the coordinate values
for each arc path command to the parameters of the arc end-point
parametization above from which the arc's parametric representation
can be obtained.
Table 5.arcParameterSpecialization: Arc Path Command
Token (x1,y1) rh rv phi (x2,y2) fA fS
---------------------------- ---------- --------- --------- ----------- ------------------- --------------- -------
SMALL_CCW_ARC_TO_NV cp.x,cp.y abs(c[0]) abs(c[1]) c[2]*Pi/180 c[3],c[4] false true
RELATIVE_SMALL_CCW_ARC_TO_NV cp.x,cp.y abs(c[0]) abs(c[1]) c[2]*Pi/180 cp.x+c[3],cp.y+c[4] false true
SMALL_CW_ARC_TO_NV cp.x,cp.y abs(c[0]) abs(c[1]) c[2]*Pi/180 c[3],c[4] false false
RELATIVE_SMALL_CW_ARC_TO_NV cp.x,cp.y abs(c[0]) abs(c[1]) c[2]*Pi/180 cp.x+c[3],cp.y+c[4] false false
LARGE_CCW_ARC_TO_NV cp.x,cp.y abs(c[0]) abs(c[1]) c[2]*Pi/180 c[3],c[4] true true
RELATIVE_LARGE_CCW_ARC_TO_NV cp.x,cp.y abs(c[0]) abs(c[1]) c[2]*Pi/180 cp.x+c[3],cp.y+c[4] true true
LARGE_CW_ARC_TO_NV cp.x,cp.y abs(c[0]) abs(c[1]) c[2]*Pi/180 c[3],c[4] true false
RELATIVE_SMALL_CW_ARC_TO_NV cp.x,cp.y abs(c[0]) abs(c[1]) c[2]*Pi/180 cp.x+c[3],cp.y+c[4] true false
CIRCULAR_CCW_ARC_TO_NV A.x,A.y abs(c[2]) abs(c[2]) 0 B.x,B.y (c[4]-c[3])>180 true
CIRCULAR_CW_ARC_TO_NV A.x,A.y abs(c[2]) abs(c[2]) 0 B.x,B.y (c[4]-c[3])>180 false
CIRCULAR_TANGENT_ARC_TO_NV C.x,C.y abs(c[4]) abs(c[4]) 0 D.x,D.y false num>=0
ARC_TO_NV cp.x,cp.y abs(c[0]) abs(c[1]) c[2]*Pi/180 c[5],c[6] c[3]!=0 c[4]!=0
RELATIVE_ARC_TO_NV cp.x,cp.y abs(c[0]) abs(c[1]) c[2]*Pi/180 cp.x+c[5],cp.y+c[6] c[3]!=0 c[4]!=0
where, for CIRCULAR_CCW_ARC_TO_NV and CIRCULAR_CW_ARC_TO_NV,
A = (c[0]+c[2]*cos(c[3]*Pi/180),
c[1]+c[2]*sin(c[3]*Pi/180))
B = (c[0]+c[2]*cos(c[4]*Pi/180),
c[1]+c[2]*sin(c[4]*Pi/180))
and C, D, and num, for CIRCULAR_TANGENT_ARC_TO_NV, are computed
through the following steps:
Step 1: Compute two tangent vectors:
d0.x = cp.x - c[0]
d0.y = cp.y - c[1]
d2.x = c[2] - c[0]
d2.y = c[3] - c[1]
Step 2: Compute scaling factors for tangent vectors:
num = d0.y*d2.x - d2.y*d0.x
denom = sqrt(dot(d0,d0)*dot(d2,d2)) - dot(d0,d2)
dist = abs(c[4] * num/denom)
l0 = dist/sqrt(dot(d0,d0)) * c[4]/abs(c[4])
l2 = dist/sqrt(dot(d2,d2)) * c[4]/abs(c[4])
Step 3: Add scaled directions to the tangent vector intersection
point:
/ (c[0],c[1]) + d0 * l0, denom!=0 AND c[4]!=0
C = {
\ (c[0],c[1]), denom==0 OR c[4]==0
/ (c[0],c[1]) + d2 * l2, denom!=0 AND c[4]!=0
D = {
\ (c[0],c[1]), denom==0 OR c[4]==0
PATH OBJECT SPECIFICATION
Path objects can be specified in one of four ways:
1) explicitly from an array of commands and corresponding
coordinates,
2) from a string conforming to one of two supported grammars to
specify a string,
3) from a glyph within a font face from a system font or font file,
or
4) by linearly combining one or more existing path objects with
mutually consistent command sequences to form a new path.
In any situation where a path object is specified or re-specified,
the command's parameters are re-initialized as discussed in section
5.X.1.5 unless otherwise specified. However modification of path
commands and coordinates (section 5.X.1.4) does not modify path
parameters.
5.X.1.1 Explicit Path Specification
The command
void PathCommandsNV(uint path,
sizei numCommands, const ubyte *commands,
sizei numCoords, enum coordType,
const void *coords);
specifies a new path object named /path/ where /numCommands/
indicates the number of path commands, read from the array
/commands/, with which to initialize that path's command sequence.
These path commands reference coordinates read sequentially from the
/coords/ array. The type of the coordinates read from the /coords/
array is determined by the /coordType/ parameter which must be
one of BYTE, UNSIGNED_BYTE, SHORT, UNSIGNED_SHORT, or FLOAT.
The /numCommands/ elements of the /commands/ array must be tokens
or character in Table 5.pathCommands. The command sequence matches
the element order of the /commands/ array. Each command references
a number of coordinates specified by "Coordinate count" column of
Table 5.pathCommands, starting with the first (zero) element of
the /coords/ array and advancing by the coordinate count for each
command. If any of these /numCommands/ command values are not
listed in the "Token" or "Character aliases" columns of Table
5.pathCommands, the INVALID_ENUM error is generated.
The INVALID_OPERATION error is generated if /numCoords/ does not
equal the number of coordinates referenced by the command sequence
specified by /numCommands/ and /commands/ (so /numCoords/ provides a
sanity check that the /coords/ array is being interpreted properly).
The error INVALID_VALUE is generated if either /numCommands/ or
/numCoords/ is negative.
If the PathCommandsNV command results in an error, the path object
named /path/ is not changed; if there is no error, the prior contents
of /path/, if /path/ was an existent path object, are lost and the
path object name /path/ becomes used.
5.X.1.2 String Path Specification
The command
void PathStringNV(uint path, enum format,
sizei length, const void *pathString);
specifies a new path object named /path/ where /format/ must be
either PATH_FORMAT_SVG_NV or PATH_FORMAT_PS_NV, in which case the
/length/ and /pathString/ are interpreted according to grammars
specified in sections 5.X.1.2.1 and 5.X.1.2.2 respectively.
The INVALID_VALUE error is generated if /length/ is negative.
If the PathStringNV command results in an error, the path object
named /path/ is not changed; if there is no error, the prior contents
of /path/, if /path/ was an existent path object, are lost and the
path object name /path/ becomes used.
5.X.1.2.1 Scalable Vector Graphics Path Grammar
If the /format/ parameter of PathStringNV is PATH_FORMAT_SVG_NV,
the /pathString/ parameter is interpreted as a string of ubyte ASCII
characters with /length/ elements.
This string must satisfy the "svg-path" production in the path
grammar below. This grammar is taken directly from the Scalable
Vector Graphics (SVG) 1.1 (April 30, 2009) specification.
The following notation is used in the Backus-Naur Form (BNF)
description of the grammar for an SVG path string:
* *: 0 or more
* +: 1 or more
* ?: 0 or 1
* (): grouping
* ()^n: grouping with n repetitions where n is explained subsequently
* |: separates alternatives
* double quotes surround literals
* #x: prefixes an ASCII character value followed by hexadecimal
digits
* ..: means any of an inclusive range of ASCII characters, so
'0'..'9' means any digit character
The following is the grammar for SVG paths.
svg-path:
wsp* moveto-drawto-command-groups? wsp*
moveto-drawto-command-groups:
moveto-drawto-command-group
| moveto-drawto-command-group wsp* moveto-drawto-command-groups
moveto-drawto-command-group:
moveto wsp* drawto-commands?
drawto-commands:
drawto-command
| drawto-command wsp* drawto-commands
drawto-command:
closepath
| lineto
| horizontal-lineto
| vertical-lineto
| curveto
| smooth-curveto
| quadratic-bezier-curveto
| smooth-quadratic-bezier-curveto
| elliptical-arc
moveto:
( "M" | "m" ) wsp* moveto-argument-sequence
moveto-argument-sequence:
coordinate-pair
| coordinate-pair comma-wsp? lineto-argument-sequence
closepath:
("Z" | "z")
lineto:
( "L" | "l" ) wsp* lineto-argument-sequence
lineto-argument-sequence:
coordinate-pair
| coordinate-pair comma-wsp? lineto-argument-sequence
horizontal-lineto:
( "H" | "h" ) wsp* horizontal-lineto-argument-sequence
horizontal-lineto-argument-sequence:
coordinate
| coordinate comma-wsp? horizontal-lineto-argument-sequence
vertical-lineto:
( "V" | "v" ) wsp* vertical-lineto-argument-sequence
vertical-lineto-argument-sequence:
coordinate
| coordinate comma-wsp? vertical-lineto-argument-sequence
curveto:
( "C" | "c" ) wsp* curveto-argument-sequence
curveto-argument-sequence:
curveto-argument
| curveto-argument comma-wsp? curveto-argument-sequence
curveto-argument:
coordinate-pair comma-wsp? coordinate-pair comma-wsp? coordinate-pair
smooth-curveto:
( "S" | "s" ) wsp* smooth-curveto-argument-sequence
smooth-curveto-argument-sequence:
smooth-curveto-argument
| smooth-curveto-argument comma-wsp? smooth-curveto-argument-sequence
smooth-curveto-argument:
coordinate-pair comma-wsp? coordinate-pair
quadratic-bezier-curveto:
( "Q" | "q" ) wsp* quadratic-bezier-curveto-argument-sequence
quadratic-bezier-curveto-argument-sequence:
quadratic-bezier-curveto-argument
| quadratic-bezier-curveto-argument comma-wsp?
quadratic-bezier-curveto-argument-sequence
quadratic-bezier-curveto-argument:
coordinate-pair comma-wsp? coordinate-pair
smooth-quadratic-bezier-curveto:
( "T" | "t" ) wsp* smooth-quadratic-bezier-curveto-argument-sequence
smooth-quadratic-bezier-curveto-argument-sequence:
coordinate-pair
| coordinate-pair comma-wsp? smooth-quadratic-bezier-curveto-argument-sequence
elliptical-arc:
( "A" | "a" ) wsp* elliptical-arc-argument-sequence
elliptical-arc-argument-sequence:
elliptical-arc-argument
| elliptical-arc-argument comma-wsp? elliptical-arc-argument-sequence
elliptical-arc-argument:
nonnegative-number comma-wsp? nonnegative-number comma-wsp?
number comma-wsp flag comma-wsp flag comma-wsp coordinate-pair
coordinate-pair:
coordinate comma-wsp? coordinate
coordinate:
number
nonnegative-number:
integer-constant
| floating-point-constant
number:
sign? integer-constant
| sign? floating-point-constant
flag:
"0" | "1"
comma-wsp:
(wsp+ comma? wsp*) | (comma wsp*)
comma:
","
integer-constant:
digit-sequence
floating-point-constant:
fractional-constant exponent?
| digit-sequence exponent
fractional-constant:
digit-sequence? "." digit-sequence
| digit-sequence "."
exponent:
( "e" | "E" ) sign? digit-sequence
sign:
"+" | "-"
digit-sequence:
digit
| digit digit-sequence
digit:
"0".."9"
wsp:
(#x20 | #x9 | #xD | #xA)
The processing of the BNF must consume as much of a given BNF
production as possible, stopping at the point when a character
is encountered which no longer satisfies the production. Thus,
in the string "M 100-200", the first coordinate for the "moveto"
consumes the characters "100" and stops upon encountering the minus
sign because the minus sign cannot follow a digit in the production
of a "coordinate". The result is that the first coordinate will be
"100" and the second coordinate will be "-200".
Similarly, for the string "M 0.6.5", the first coordinate of the
"moveto" consumes the characters "0.6" and stops upon encountering
the second decimal point because the production of a "coordinate"
only allows one decimal point. The result is that the first coordinate
will be "0.6" and the second coordinate will be ".5".
The grammar allows the string to be empty (zero length). This is
not an error, instead specifies a path with no commands.
Table 5.svgCommands maps productions in the grammar above to the
path commands in Table 5.pathCommands; each such path command, with
its corresponding coordinates, is added to the path command sequence
of the path object. Each production listed in Table 5.svgCommands
consumes a number of coordinates consistent with the path command
token's coordinate count listed in Table 5.pathCommands. The
"coordinate" and "nonnegative-number" productions convert to a numeric
coordinate value in the obvious way. The "flag" production converts
"0" and "1" to numeric coordinate values zero and one respectively.
Table 5.svgCommands: SVG Grammar Commands to Path Command Tokens
Grammar's prior
Production command character Path command token
------------------------------------------------- ----------------- -------------------------------------
moveto-argument-sequence "M" MOVE_TO_NV
"m" RELATIVE_MOVE_TO_NV
closepath "Z" or "z" CLOSE_PATH_NV
lineto-argument-sequence "L" LINE_TO_NV
"l" RELATIVE_LINE_TO_NV
horizontal-lineto-argument-sequence "H" HORIZONTAL_LINE_TO_NV
"h" RELATIVE_HORIZONTAL_LINE_TO_NV
vertical-lineto-argument-sequence "V" VERTICAL_LINE_TO_NV
"v" RELATIVE_VERTICAL_LINE_TO_NV
quadratic-bezier-curveto-argument "Q" QUADRATIC_CURVE_TO_NV
"q" RELATIVE_QUADRATIC_CURVE_TO_NV
smooth-quadratic-bezier-curveto-argument-sequence "T" SMOOTH_QUADRATIC_CURVE_TO_NV
"t" RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV
curveto-argument "C" CUBIC_CURVE_TO_NV
"c" RELATIVE_CUBIC_CURVE_TO_NV
smooth-curveto-argument "S" SMOOTH_CUBIC_CURVE_TO_NV
"s" RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV
elliptical-arc-argument "A" ARC_TO_NV
"a" RELATIVE_ARC_TO_NV
If the string fails to satisfy the svg-path production, the path
object named /path/ is not changed. The production may not be
satisfied for one of two reasons: either the grammar cannot be not
satisfied by the string, or the grammar is satisfied but there still
remain a non-zero number of characters in the string. Neither
failure to satisfy the production generates an error; instead the
PATH_ERROR_POSITION_NV state is set to the character offset where the
grammar was first not satisfied or where the grammar was exhausted.
If the string was parsed successfully and the command did not generate
an error, the PATH_ERROR_POSITION_NV state is set to negative one
to indicate success.
5.X.1.2.2 PostScript Path Grammar
If the /format/ parameter of PathStringNV is PATH_FORMAT_PS_NV,
the /pathString/ parameter is interpreted as a string of ubyte ASCII
characters with /length/ elements.
This string must satisfy the "ps-path" production in the path
grammar below. This grammar is parses path specified in PostScript's
subgrammar for user paths specified by "PostScript Language Reference
Manual" 3rd edition.
The following is the grammar (using the same notation as section
5.X.1.2.1) for PS paths with special support for binary encoding modes
(as explained below):
ps-path:
ps-wsp* user-path? ps-wsp*
| ps-wsp* encoded-path ps-wsp*
user-path:
user-path-cmd
| user-path-cmd ps-wsp+ user-path
user-path-cmd:
setbbox
| ps-moveto
| rmoveto
| ps-lineto
| rlineto
| ps-curveto
| rcurveto
| arc
| arcn
| arct
| ps-closepath
| ucache
setbbox:
numeric-value numeric-value numeric-value numeric-value setbbox-cmd
setbbox-cmd:
"setbbox"
| #x92 #x8F
ps-moveto:
numeric-value numeric-value moveto-cmd
moveto-cmd:
"moveto"
| #x92 #x6B
rmoveto:
numeric-value numeric-value rmoveto-cmd
rmoveto-cmd:
"rmoveto"
| #x92 #x86
ps-lineto:
numeric-value numeric-value lineto-cmd
lineto-cmd:
"lineto"
| #x92 #x63
rlineto:
numeric-value numeric-value rlineto-cmd
rlineto-cmd:
"rlineto"
| #x92 #x85
ps-curveto:
numeric-value numeric-value numeric-value numeric-value numeric-value numeric-value curveto-cmd
curveto-cmd:
"curveto"
| #x92 #x2B
rcurveto:
numeric-value numeric-value numeric-value numeric-value numeric-value numeric-value rcurveto-cmd
rcurveto-cmd:
"rcurveto"
| #x92 #x7A
arc:
numeric-value numeric-value numeric-value numeric-value numeric-value arc-cmd
arc-cmd:
"arc"
| #x92 #x05
arcn:
numeric-value numeric-value numeric-value numeric-value numeric-value arcn-cmd
arcn-cmd:
"arcn"
| #x92 #x06
arct:
numeric-value numeric-value numeric-value numeric-value numeric-value arct-cmd
arct-cmd:
"arct"
| #x92 #x07
ps-closepath:
"closepath"
| #x92 #x16
ucache:
"ucache"
| #x92 #xB1
encoded-path:
data-array ps-wsp* operator-string
data-array:
"{" ps-wsp* numeric-value-sequence? "}"
| homogeneous-number-array
| ascii85-homogeneous-number-array
operator-string:
hexadecimal-binary-string
| ascii85-string
| short-binary-string
| be-long-binary-string
| le-long-binary-string
hexadecimal-binary-string:
"<" ps-wsp-chars* hexadecimal-sequence ps-wsp-chars* ">"
hexadecimal-sequence:
hexadecimal-digit
| hexadecimal-digit ps-wsp-chars* hexadecimal-sequence
hexadecimal-digit:
digit
| "a".."f" |
| "A".."F"
short-binary-string:
#x8E one-byte ( one-byte )^n
/where n is the value of the one-byte production decoded
as an unsigned integer, 0 through 255/
be-long-binary-string:
#x8F two-bytes ( one-byte )^n
/where n is the value of the two-bytes production decoded
as an unsigned integer, 0 through 65535, decoded in
big-endian byte order/
le-long-binary-string:
#x90 two-bytes ( one-byte )^n
/where n is the value of the two-bytes production decoded
as an unsigned integer, 0 through 65535, decoded in
little-endian byte order/
numeric-value-sequence:
numeric-value:
| numeric-value numeric-value-sequence
numeric-value:
number ps-wsp+
| radix-number ps-wsp+
| be-integer-32bit
| le-integer-32bit
| be-integer-16bit
| le-integer-16bit
| le-integer-8bit
| be-fixed-16bit
| le-fixed-16bit
| be-fixed-32bit
| le-fixed-32bit
| be-float-ieee
| le-float-ieee
| native-float-ieee
be-integer-32bit:
#x84 four-bytes
le-integer-32bit:
#x85 four-bytes
be-integer-16bit:
#x86 two-bytes
le-integer-16bit:
#x87 two-bytes
le-integer-8bit:
#x88 one-byte
be-fixed-32bit:
#x89 #x0..#x1F four-bytes
le-fixed-32bit:
#x89 #x80..#x9F four-bytes
be-fixed-16bit:
#x89 #x20..#x2F two-bytes
le-fixed-16bit:
#x89 #xA0..#xAF two-bytes
be-float-ieee:
#x8A four-bytes
le-float-ieee:
#x8B four-bytes
native-float-ieee:
#x8C four-bytes
radix-number:
base "#" base-number
base:
digit-sequence
base-number:
base-digit-sequence
base-digit-sequence:
base-digit
| base-digit base-digit-sequence
base-digit:
digit
| "a".."z"
| "A".."Z"
homogeneous-number-array:
be-fixed-32bit-array
| be-fixed-16bit-array
| be-float-ieee-array
| native-float-ieee-array
| le-fixed-32bit-array
| le-fixed-16bit-array
| le-float-ieee-array
be-fixed-32bit-array:
#x95 #x0..#x1F two-bytes ( four-bytes )^n
/where n is the value of the two-bytes production decoded
as an unsigned integer, 0 through 65535, decoded in
big-endian byte order/
be-fixed-16bit-array:
#x95 #x20..#x2F two-bytes ( two-bytes )^n
/where n is the value of the two-bytes production decoded
as an unsigned integer, 0 through 65535, decoded in
big-endian byte order/
be-float-ieee-array:
#x95 #x30 two-bytes ( four-bytes )^n
/where n is the value of the two-bytes production decoded
as an unsigned integer, 0 through 65535, decoded in
big-endian byte order/
le-fixed-32bit-array:
#x95 #x80..#x9F two-bytes ( four-bytes )^n
/where n is the value of the two-bytes production decoded
as an unsigned integer, 0 through 65535, decoded in
little-endian byte order/
le-fixed-16bit-array:
#x95 #xA0..#xAF two-bytes ( two-bytes )^n
/where n is the value of the two-bytes production decoded
as an unsigned integer, 0 through 65535, decoded in
little-endian byte order/
le-float-ieee-array:
#x95 #xB0 two-bytes ( four-bytes )^n
/where n is the value of the two-bytes production decoded
as an unsigned integer, 0 through 65535, decoded in
little-endian byte order/
native-float-ieee-array:
#x95 ( #x31 | #xB1 ) two-bytes ( four-bytes )^n
/where n is the value of the two-bytes production decoded
as an unsigned integer, 0 through 65535, decoded in
the native byte order/
ascii85-string:
"<~" (#x21..#x75 | "z" | psp-wsp )* "~>"
ascii85-homogeneous-number-array:
"<~" (#x21..#x75 | "z" | psp-wsp )* "~>"
one-byte:
#x0..#xFF
two-bytes:
#x0..#xFF #x0..#xFF
four-bytes:
#x0..#xFF #x0..#xFF #x0..#xFF #x0..#xFF
ps-wsp:
ps-wsp-chars
| ps-comment
ps-wsp-chars:
( #x20 | #x9 | #xA | #xC | #xD | #x0 )
ps-comment:
"%" ( #0..#9 | #xB..#xC | #xE..#xFF )* ( #xD | #xA )
This grammar is not technically a pure BNF because it uses binary
encoded data to encode how many characters should be as part of
several productions (short-binary-string, native-float-ieee-array,
etc.).
The processing of the BNF must consume as much of a given BNF
production as possible, stopping at the point when a character
is encountered which no longer satisfies the production.
The grammar allows the string to be empty (zero length). This
is not an error, instead specifies a path with no commands.
Table 5.psCommands maps productions in the grammar above to the path
commands in Table 5.pathCommands; each such path command, with its
corresponding coordinates, is added to the path command sequence
of the path object. Each production listed in Table 5.svgCommands
consumes a quantity of values, matched by the "number" production,
consistent with the path command token's coordinate count listed
in Table 5.pathCommands. The "setbbox" and "ucache" products are
matched but do not result in path commands.
Table 5.psCommands: PS Grammar Commands to Path Command Tokens
Production Path command token
------------ --------------------------
arc CIRCULAR_CCW_ARC_TO_NV
arcn CIRCULAR_CW_ARC_TO_NV
arct CIRCULAR_TANGENT_ARC_TO_NV
ps-closepath CLOSE_PATH_NV
ps-curveto CUBIC_CURVE_TO_NV
ps-lineto LINE_TO_NV
ps-moveto MOVE_TO_NV
rcurveto RELATIVE_CUBIC_CURVE_TO_NV
rlineto RELATIVE_LINE_TO_NV
rmoveto RELATIVE_MOVE_TO_NV
setbbox -
ucache -
The "number" production converts to a numeric coordinate value
in the obvious way. The "radix-number" production converts the
base-n integer conversion of its "base-number" production using
the base indicated by the base-10 integer conversion of its "base"
production where the base /n/ must be within the range 2 to 26.
The "base-number" is interpreted in base /n/; the "base-number"
production must contain digits ranging from 0 to /n/-1; digits greater
than 9 are represented by the letters A through Z (or a through z)
for the values 10 through 35 respectively.
The "encoded-path" production provides a compact and precise way
to encode paths with the commands and coordinates decoupled.
The "data-array" subproductions provide a sequence of coordinate
values for the encoded path's commands. The "data-array"
subproduction provides a sequence of numbers that is used by the
following "operator-string" production.
The "operator-string" subproduction is interpreted as a sequence
of encoded path commands, one command per byte generated by
"operator-string"'s "binary-string" production.
Each hexadecimal character in the "hexadecimal-binary-string"
production is a nibble (a 4-bit quantity). Each pair of characters
is two nibbles and they form a byte with the first nibble
representing the most signification bits of the byte. If the
"hexadecimal-binary-string" production contains an odd number of
hexadecimal characters, "0" is assumed to be suffixed to make an
even number of characters (so "A7C" would encode the bytes 167 for
"A7" followed by 192 for "C" which is treated as "C0" for 192).
Table 5.encodedPathOpcodes maps the values contained in the operator
string to path commands. Each command consumes from the coordinate
array supplied by the "data-array" production a number of values
for the command's coordinates equal to the path command token's
coordinate count listed in Table 5.pathCommands. If the value for
an element of the operator string is between 12 and 32 inclusive,
the grammar fails to parse at this point. If the value /n/ of an
element of the operator string is between 32 and 255, then this value
/n/-32 is treated as a repetition count and is treated as if /n/-32
repetitions of the next command are contained in the operator string
instead and the appropriate number of coordinates are consumed from
the associated sequence of coordinate values.
Table 5.encodedPathOpcodes
Opcode Name
------ ---------
0 setbbox
1 moveto
2 rmoveto
3 lineto
4 rlineto
5 curveto
6 rcurveto
7 arc
8 arcn
9 arct
10 closepath
11 ucache
The ASCII characters in the "ascii85-binary-string" production
consists of a sequence of printable ASCII characters between the "<~"
and "~>" delimiters. This represents arbitrary binary data using
an encoding technique that products a 4:5 expansion as opposed to
the 1:2 expansion for the "hexadecimal-binary-string" production.
This encoding is known as ASCII base-85.
Binary data in the ASCII base-85 encoding are encoded in 4-tuples
(groups of 4) each 4-tuple is used to produce a 5-type of ASCII
characters. If the binary 4-tuple is (b1,b2,b3,b4) and the encoded
5-tuple is (c1,c2,c3,c4,c5), then the relation between them is:
(b1 * 256^3) + (b2 * 256^2) + (b3 * 256^1) + b4 =
(c1 * 256^4) + (c2 * 256^3) + (c3 * 256^2) + (c4 * 256^3) + c5
The four bytes of binary data are interpreted as a base-256 number and
then converted into a base-85 number. The five "digits" of this number,
(c1,c2,c3,c4,c5), are then converted into ASCII characters by adding 33,
which is the ASCII code for '!', to each. ASCII characters in the
range '!' to 'u' are used, where '!' represented the value 0 and 'u'
represents the value 84. As a special case, if all five digits are
zero, they must be represented by either a single 'z' instead of by
'!!!!'.
If the encoded sequence ends with a sequence of characters that is
not an even multiple of 4, the last 1, 2, or 3 characters to produce
a special final partial 5-tuple. Given n (1, 2, or 3) bytes of final
binary data, an encoder must first append 4-n zero bytes to make
a complete 4-tuple. Then, the encoder must encode the 4-tuple in
the usual way, but without applying the 'z' special case. Finally,
the encoder must write the first n+1 bytes of the resulting 5-tuple.
Those bytes are immediately followed by the "~>" terminal marker.
This encoding scheme is reversible and the GL is responsible for
converting the ASCII base-85 string into its corresponding binary
data. White space within an ASCII base-85 encoded string is ignored.
The following conditions constitute encoding violations of the ASCII
base-85 scheme:
* The value represented by a 5-tuple is greater than 2^32-1
* The 'z' value occurs in the middle of a 5-tuple.
* A final partial 5-tuple contains only one character.
Any such encoding violation is a parsing error.
Once the ASCII base-85 string is decoded, this sequence of bytes
is treated as operator elements in the identical manner as the
elements for the "hexadecimal-string" subproduction. This means
invalid opcodes are possible and are treated as parsing errors, and
Valid opcodes and counts consume coordinates from the "data-array"
production to generate path commands with associated coordinates.
The "short-binary-string", "be-long-binary-string", and
"le-long-binary-string" subproductions of "operator-string" are
binary encodings of a sequence of operator string elements.
The "short-binary-string" has a count from 0 to 255 supplied by its
"one-byte" subproduction which indicates how many bytes follow.
These remaining (unsigned) bytes generate the sequence of operator
string elements.
The "be-long-binary-string" has a count from 0 to 65535 supplied by
its "two-byte" subproduction which indicates how many bytes follow.
These remaining (unsigned) bytes generate the sequence of operator
string elements. The "two-byte" subproduction is converted to a
count by multiplying the first unsigned byte by 256 and adding it
to the second unsigned byte.
The "le-long-binary-string" has a count from 0 to 65535 supplied by
its "two-byte" subproduction which indicates how many bytes follow.
These remaining (unsigned) bytes generate the sequence of operator
string elements. The "two-byte" subproduction is converted to a
count by multiplying the second unsigned byte by 256 and adding it
to the first unsigned byte.
The "encoded-path" fails to parse if invalid opcodes are detected
in the operator string or the sequence of numbers for coordinates
is exhausted prematurely.
If the string fails to satisfy the ps-path production, the path
object named /path/ is not changed. The production may not be
satisfied for one of three reasons: the grammar cannot be not
satisfied by the string, the string has invalid sequences (such
as ASCII base-85 violations, exhausting the coordinate data in the
"data-array" production, or invalid opcodes encountered in the
"operator-string" production), or the grammar is satisfied but
there still remain a non-zero number of characters in the string.
None of these failures to satisfy the grammar generates an error;
instead the PATH_ERROR_POSITION_NV state is set to the character
offset where the grammar was first not satisfied, violated
semantically, or where the grammar was exhausted. If the string
was parsed successfully and the command did not generate an error,
the PATH_ERROR_POSITION_NV state is set to negative one to indicate
success.
If a parsing error occurs, the exact value assigned to the
PATH_ERROR_POSITION_NV state variable is implementation-dependent
(because the specifics of error position determination is difficult
to specify) though the determined error location should be nearby
the first error.
5.X.1.3 Font Glyph Path Specification
PATH GLYPHS FROM CHARACTER CODE SEQUENCE
The command
void PathGlyphsNV(uint firstPathName,
enum fontTarget,
const void *fontName,
bitfield fontStyle,
sizei numGlyphs, enum type,
const void *charcodes,
enum handleMissingGlyphs,
uint pathParameterTemplate,
float emScale);
creates, if no error occurs, a range of path objects named from
/firstPathName/ to /firstPathName/+/numGlyphs/-1 based on the
font face indicated by /fontTarget/, /fontName/, and /fontStyle/
and the sequence of /numGlyphs/ character codes listed in the
/charcodes/ array, as interpreted based by the /type/ parameter.
However each particular name in the range /firstPathName/ to
/firstPathName/+/numGlyphs/-1 is specified as a new path object only
if that name is not already in use as a path object; if a name is
already in use, that named path object is silently left undisturbed.
A path object name is also left undisturbed if the
/handleMissingGlyphs/ parameter is SKIP_MISSING_GLYPH_NV and the
character code for a given glyph corresponds to the font's missing
glyph or the character code is otherwise not available.
The error INVALID_VALUE is generated if /numGlyphs/ or /emScale/
is negative.
The /fontTarget/ parameter must be one of STANDARD_FONT_NAME_NV,
SYSTEM_FONT_NAME_NV, or FILE_NAME_NV; otherwise the INVALID_ENUM
error is generated.
The /handleMissingGlyphs/ parameter must be one of
SKIP_MISSING_GLYPH_NV or USE_MISSING_GLYPH_NV; otherwise the
INVALID_ENUM error is generated.
If /fontTarget/ is STANDARD_FONT_NAME_NV, then /fontName/ is
interpreted as a nul-terminated 8-bit ASCII character string that
must be one of the following strings: "Serif", "Sans", "Mono",
or "Missing"; otherwise the INVALID_VALUE error is generated.
These "Serif", "Sans", and "Mono" names respectively correspond to
serif, sans-serif, and sans monospaced font faces with the intent
that the font face matches the appearance, metrics, and kerning
of the DejaVu fonts of the same names. All implementations /must/
support these font names for the STANDARD_FONT_NAME_NV target.
For the STANDARD_FONT_NAME_NV targets with "Serif", "Sans", and
"Mono", all implementations /must/ support the first 256 character
codes defined by Unicode and the ISO/IEC 8859-1 (Latin-1 Western
European) character encoding though implementations are strongly
encouraged to support as much of the Unicode character codes as the
system's underlying font and language support provides.
For the STANDARD_FONT_NAME_NV targets with "Missing", the entire
sequence of path objects must be populated with an identical box
outline with metrics matching this box.
If /fontTarget/ is SYSTEM_FONT_NAME_NV, then /fontName/ is interpreted
as a nul-terminated 8-bit ASCII character string that corresponds to a
system-specific font name. These names are intended to correspond to
the fonts names typically used in web content (e.g. Arial, Georgia,
Times Roman, Helvetica). The mapping of the system font character
string to a system font is assumed to be performed by the GL server.
If /fontTarget/ is FILE_NAME_NV, then /fontName/ is interpreted as
a nul-terminated 8-bit ASCII character string that corresponds to
a system-specific file name in a standard outline font format.
The specific interpretation of this name depends on the system
conventions for identifying files by name. This name can be an
absolute or relative path. The name is expected to include the
font name's extension. The mapping of the font file name to a
font is assumed to be performed by the GL client. What font file
formats are supported is system dependent but implementations are
encouraged to support outline font formats standard to the system
(e.g. TrueType for Windows systems, etc.).
If the /fontTarget/ and /fontName/ combination can not be loaded for
any reason (including the file name could not be opened, the font
name is not available on the system, the font file format is not
supported, the font file format is corrupted, etc.) and there is no
other error generated, the command succeeds silently (so no error
is generated) and the range of named path objects is not modified.
If the named path objects did not exist previously, they continue
to not exist.
The /fontStyle/ parameter is a bitfield allowed to have the
bits BOLD_BIT_NV or ITALIC_BIT_NV set; if other bits are set, the
INVALID_VALUE error is generated. The font style is used as a hint to
indicate the style of the font face. Glyphs are generated with the
font's bold or italic style respectively (or combination thereof)
if the BOLD_BIT_NV or ITALIC_BIT_NV bits are set; otherwise, the
value 0 or NONE indicates the default font face style should be used
to generate the requested glyphs. In situations where the bold or
italic style of the font is encoded in the font name or file name,
the /fontStyle/ parameter is ignored.
The generated glyphs for the path objects named /firstPathName/
to /firstPathName/+/numGlyphs/-1 are specified by the /numGlyphs/
character codes listed in the /charcodes/ array where each element of
the array is determined by the /type/ parameter that must be one of
UNSIGNED_BYTE, UNSIGNED_SHORT, UNSIGNED_INT, UTF8_NV, UTF16_NV,
2_BYTES, 3_BYTES, and 4_BYTES with the array accessed in the same
manner as the CallLists command's /type/ and /lists/ parameters
(though not offset by the display list base), but indicating character
codes instead of display list names.
The character codes from the /charcodes/ array are Unicode character
codes if the font in question can map from the Unicode character
set to the font's glyphs. If the font has no meaningful mapping
from Unicode, the font's standard character set is used instead
of Unicode (e.g. a font filled with non-standard symbols). For a
font supporting a character set that can be mapped to the Unicode
character set, a best effort should be made to map the specified
character code from its Unicode character code interpretation to
the closest appropriate glyph in the specified font.
Path objects created from glyphs by PathGlyphsNV have their path
object metric state initialized from the metrics of the glyph from
which they were specified. Section 6.X.3. ("Path Object Glyph
Typographic Queries") explains how these metrics are queried and
what their values mean. While the per-glyph metrics are expected to
vary from glyph to glyph within a font face, the per-font metrics
are expected to be identical for every path object created from a
given font name and font style combination.
Metrics in font space of glyphs are scaled by a value /s/ that is the
ratio of the /emScale/ parameter divided by the font's units per Em;
if the /emScale/ parameter equals zero, treat /emScale/ as if it was
identical to the font's units per Em such that /s/ is exactly 1.0.
Each glyph's outline are also scaled by /s/. The metric values /not/
scaled by /s/ are GLYPH_HAS_KERNING_BIT_NV, FONT_UNITS_PER_EM_BIT_NV,
and FONT_HAS_KERNING_BIT_NV (since these metric values are not
specified in font units).
When unknown or missing character codes in a font face are specified
and the /handleMissingGlyph/ parameter is USE_MISSING_GLYPHS_NV,
this situation should be handled in a manner appropriate to the
character code, font face, and implementation. Typically this
involves using the font's missing glyph for the unknown or missing
character code.
If the /pathParameterTemplate/ parameter names an existing path
object, that path object's current parameters listed in Table
5.pathParameters (excepting PATH_FILL_MODE_NV as explained in
the following paragraph) are used to initialize the respective
parameters of path objects specified by this command; otherwise
if the /pathParameterTemplate/ path object name does not exist,
the initial path parameters are used as specified by table 6.Y
(without generating an error).
Path objects created from glyphs by PathGlyphsNV have their
PATH_FILL_MODE_NV parameter, as explained in Section 5.X.1.5 ("Path
Parameter Specification"), initialized according to the fill
conventions of the font outlines within the font (instead of the
COUNT_UP_NV default for paths specified by means other than glyphs).
This may be one of: COUNT_UP_NV if the font's outline winding
convention is counterclockwise and its outline filling assumes the
non-zero winding rule; COUNT_DOWN_NV if the font's outline winding
convention is clockwise and its outline filling assumes the non-zero
winding rule; or INVERT if the font's outline filling assumes the
even-odd winding rule.
PATH GLYPHS FROM CHARACTER CODE RANGE
The command
void PathGlyphRangeNV(uint firstPathName,
enum fontTarget,
const void *fontName,
bitfield fontStyle,
uint firstGlyph,
sizei numGlyphs,
enum handleMissingGlyphs,
uint pathParameterTemplate,
float emScale);
allows a sequence of character codes in a font face to specify a
sequence of path objects and is equivalent to
int *array = malloc(sizeof(int)*numGlyphs);
if (array) {
for (int i=0; i= 55296) && (pathName <= 57343)) {
// Stop processing the UTF byte sequence early.
return false;
}
if (pathName < 2048) {
return false;
}
p += 3;
} else {
ubyte c3 = p[3];
if ((c3 & 0xC0) != 0x80) {
// Stop processing the UTF byte sequence early.
return false;
}
if ((c0 & 0xF8) == 0xF0) {
// Three continuation (65536 to 1114111)
pathName = pathBase + ((c3 & 0x3F) | (c2 & 0x3F) << 6 |
(c1 & 0x3F) << 12 | (c0 & 0x7) << 18);
if (pathName < 65536 && pathName > 1114111) {
return false;
}
p += 4;
} else {
// Skip invalid or restricted encodings.
// Stop processing the UTF byte sequence early.
return false;
}
}
}
}
paths = p;
return true;
}
case UTF16_NV:
{
const ushort *p = (const ushort*)paths;
ushort s0 = p[0];
if ((s0 < 0xDB00) || (s0 > 0xDFFF)) {
pathName = pathBase + s0;
p += 1;
} else {
if ((s0 >= 0xDB00) && (s0 <= 0xDBFF)) {
ushort s1 = p[1];
if ((s1 >= 0xDC00) && (s1 <= 0xDFFF)) {
pathName = pathBase + (((s0 & 0x3FF) << 10 |
(s1 & 0x3FF)) + 0x10000);
p += 2;
} else {
// Stop processing the UTF byte sequence early.
return false;
}
} else {
return false;
}
}
paths = p;
return true;
}
default: // generate INVALID_ENUM
}
}
The command
void StencilStrokePathInstancedNV(sizei numPaths,
enum pathNameType, const void *paths,
uint pathBase,
int reference, uint mask,
enum transformType,
const float *transformValues);
stencils a sequence of stroked paths and is equivalent to:
const float *v = transformValues;
for (int i = 0; i>
CoverFillPathNV(pathName, cover);
}
MatrixLoaddEXT(MODELVIEW, m); // restore matrix
}
}
assuming these helper functions for applyTransformType and
getPathName defined above as well as:
void renderBoundingBox(enum boundingBoxType,
sizei numPaths,
enum pathNameType,
const void *paths,
uint pathBase,
enum transformType,
const float *transformValues)
{
boolean hasBounds = FALSE;
float boundsUnion[4], bounds[4];
const float *v = transformValues;
for (int i = 0; i bounds[2]) {
float t = bounds[2];
bounds[2] = bounds[0];
bounds[0] = t;
}
if (bounds[1] > bounds[3]) {
float t = bounds[3];
bounds[3] = bounds[1];
bounds[1] = t;
}
if (hasBounds) {
if (bounds[0] < boundsUnion[0]) {
boundsUnion[0] = bounds[0];
}
if (bounds[1] < boundsUnion[1]) {
boundsUnion[1] = bounds[1];
}
if (bounds[2] > boundsUnion[2]) {
boundsUnion[2] = bounds[2];
}
if (bounds[3] > boundsUnion[3]) {
boundsUnion[3] = bounds[3];
}
} else {
for (int i=0; i<4; i++) {
boundsUnion[i] = bounds[i];
}
hasBounds = TRUE;
}
}
}
if (hasBounds) {
boolean polygonSmoothEnable = IsEnabled(POLYGON_SMOOTH);
int polygonModes[2];
GetIntegerv(POLYGON_MODE, polygonModes);
Rectf(bounds[0], bounds[1], bounds[2], bounds[3]);
PolygonMode(FRONT, polygonModes[0]);
PolygonMode(BACK, polygonModes[1]);
if (polygonSmoothEnable) {
Enable(POLYGON_SMOOTH);
} else {
Disable(POLYGON_SMOOTH);
}
}
}
The GetPathParameterfvNV query, used in the code above, is introduced
in section 6.X.1 ("Path Object Parameter Queries").
The command
void CoverStrokePathInstancedNV(sizei numPaths,
enum pathNameType, const void *paths,
uint pathBase,
enum coverMode,
enum transformType,
const float *transformValues);
covers a sequence of stroked paths and is equivalent to:
if (coverage == BOUNDING_BOX_OF_BOUNDING_BOXES_NV) {
renderBoundingBox(PATH_STROKE_BOUNDING_BOX_NV,
numPaths,
pathNameType, paths,
pathBase,
transformType, transformValues);
} else {
const float *v = transformValues;
for (int i = 0; i>
CoverStrokePathNV(pathName, cover);
}
MatrixLoaddEXT(MODELVIEW, m); // restore matrix
}
}
assuming these helper functions for applyTransformType,
getPathName, and renderBoundingBox defined above.
-- Section 5.4 "Display Lists"
Add to the list of commands not compiled into display lists:
"Path objects: GenPathsNV, DeletePathsNV."
Additions to Chapter 6 of the OpenGL 3.2 (unabridged) Specification (State and
State Requests)
-- Insert section 6.X "Path Object Queries" after 6.1.18 "Renderbuffer
Object Queries"
6.X. Path Rendering Queries
6.X.1. Path Object Parameter Queries
The queries
void GetPathParameterivNV(uint path, enum pname, int *value);
void GetPathParameterfvNV(uint path, enum pname, float *value);
obtains the current value of the /param/ path parameter of the path
object named /name/; the error INVALID_OPERATION is generated if
/name/ is not an existing path object. /value/ is a pointer to a
scalar or array of the appropriate type, int for GetPathParameterivNV
and float for GetPathParameterfvNV, in which to place the returned
data.
Table 6.readOnlyPathParameters
Name Type Description
--------------------------- ------- ----------------------------
PATH_COMMAND_COUNT_NV int Length of the path's
command sequence
PATH_COORD_COUNT_NV int Length of the path's
coordinate sequence
PATH_DASH_ARRAY_COUNT_NV int Length of the path's
dash array
PATH_COMPUTED_LENGTH_NV float Computed path-space
length of all the
segments in the path
(see section 6.X.4)
PATH_OBJECT_BOUNDING_BOX_NV 4*float tight path-space bounding
box around the path's
covered fill region
PATH_FILL_BOUNDING_BOX_NV 4*float Conservative path-space
bounding box around the
path's covered fill region
PATH_STROKE_BOUNDING_BOX_NV 4*float Conservative path-space
bounding box around the
path's covered stroke region
/param/ must be one of the tokens listed in Table 5.pathParameters
or Table 6.readOnlyPathParameters; otherwise the INVALID_ENUM
error is generated. The parameters from Table 5.pathParameters
always return a single (scalar) value. The parameters from
Table 6.readOnlyPathParameters a single (scalar) value for all the
parameters but the PATH_*_BOUNDING_BOX_NV parameters; these bounding
box parameters return a vector of 4 values. These four values are
the minimum (x1,y1) corner of the respective path-space bounding
box and the maximum (x2,y2) corner of the respective path-space
orthogonally aligned bounding box, returned in (x1,y1,x2,y2) order.
(This guarantees x1<=x2 and y1<=y2.) Float parameters queried by
GetPathParameterivNV are rounded to the nearest integer (where values
with a floating-point fraction of 0.5 round up).
The PATH_OBJECT_BOUNDING_BOX_NV bounding box is intended to bound
tightly the region of path space containing the path's outline.
The PATH_FILL_BOUNDING_BOX_NV bounding box matches the rectangle
region covered by the CoverFillPathNV command with the BOUNDING_BOX_NV
/coverMode/. With either the PATH_OBJECT_BOUNDING_BOX_NV or
PATH_FILL_BOUNDING_BOX_NV bounding boxes of a path object, a point at
(x,y) such that xx2 or yy2 is guaranteed to /not/
be within the filled outline of the path.
The PATH_STROKE_BOUNDING_BOX_NV bounding box matches the rectangle
region covered by the CoverFillPathNV command with the BOUNDING_BOX_NV
/coverMode/. With the PATH_STROKE_BOUNDING_BOX_NV bounding box of
a path object, a point at (x,y) such that xx2 or yy2 is guaranteed to /not/ be within the stroked region of the path.
6.X.2. Path Object Varying Arrays Queries
Path objects support a variable number of commands, coordinates,
and dash lengths.
The query
void GetPathCommandsNV(uint path, ubyte *commands);
returns the sequence of commands within the path object named /name/
into the array named /commands/; the error INVALID_OPERATION is
generated if /name/ is not an existing path object. The number of
commands returned is identical to the value of the path object's
PATH_COMMAND_COUNT_NV parameter. The application is responsible
for ensuring /commands/ array has sufficient space.
Any path commands specified with a character alias value (from Table
5.pathCommands) is returned as the command's token value instead.
The query
void GetPathCoordsNV(uint path, float *coords);
returns the sequence of coordinates within the path object named
/name/ into the array named /coords/; the error INVALID_OPERATION
is generated if /name/ is not an existing path object. The number
of commands returned is identical to the value of the path object's
PATH_COORD_COUNT_NV parameter. The application is responsible for
ensuring /coords/ array has sufficient space.
Boolean coordinates such as the large/small and sweep flags for arcs
are always returned as 1.0 or 0.0 for true and false respectively.
Other coordinates are returned as they were specified.
The query
void GetPathDashArrayNV(uint path, float *dashArray);
returns the sequence of dash lengths within the path object named
/name/ into the array named /coords/; the error INVALID_OPERATION is
generated if /name/ is not an existing path object. The number of
dash lengths returned is identical to the value of the path object's
PATH_DASH_ARRAY_COUNT_NV parameter. The application is responsible
for ensuring /dashArray/ has sufficient space.
6.X.3. Path Object Glyph Typographic Queries
GLYPH METRIC QUERIES
To facilitate proper text layout, the command
void GetPathMetricsNV(bitfield metricQueryMask,
sizei numPaths,
enum pathNameType, const void *paths,
uint pathBase,
sizei stride,
float *metrics);
queries glyph metrics associated with a sequence of path objects
specified by the /glyphBase/, /count/, /pathNameType/, and /paths/
parameters. Metrics are associated with path objects specified by
PathGlyphsNV or PathGlyphRangeNV (see section 5.X.1.3).
There are two kinds of metrics:
* Per-glyph metrics that are typically different for each glyph.
* Per-font face metrics that are identical for all glyphs belonging
to a given font face.
Per-font face metrics are aggregate metrics such as the maximum
ascender or descender for all the glyphs in the font face.
/metricQueryMask/ is a bitfield constructed from the bits listed
in Table 6.perGlyphMetrics and Table 6.perGlyphMetrics. If a bit
is set in /metricQueryMask/ not listed in these tables, the error
INVALID_VALUE is generated.
/stride/ is the byte (machine units) offset separating each group of
returned metrics for a given path object. If /stride/ is negative
or /stride/ is not a multiple of the size of float in bytes (machine
units), the INVALID_VALUE error is generated. The INVALID_OPERATION
error is generated if /stride/ divided by the size of float in bytes
is not either zero or else greater than or equal to the number of
metrics specified for querying in the metricQueryMask (based on the
number of specified bits specified in the mask) times the size of
float in bytes. A /stride/ of zero is specially handled; the value
zero is interpreted to indicate the number of bytes (machine units)
such that the all the metrics are written in a tightly packed array,
so the size of float in bytes times the number of specified bits in
the /metricQueryMask/ bitfield.
For path objects not created with either PathGlyphsNV or
PathGlyphRangeNV or non-existent, all glyph metrics return -1.
This metric information for a path object is /not/ updated if
the commands or coordinates or parameters of that path object are
changed.
Figure 6.horizontalGlyphMetrics: Horizontal Glyph Metrics
^
| xMin xMax
| | |
| | width |
| |<---------->|
| | |
| +============+ - - - - - - - - - - - yMax
| I I ^ ^
| I I | hBearingY |
| I I | |
hBearingX |---->I GLYPH I | height |
| I OUTLINE I | |
----O-----I------------I------*---> |
/| I HERE I | |
/ | I I | v
origin | +============+ - - -|- - - - - - - - yMin
| |
|------------------------>|
| hAdvance |
Figure 6.verticalGlyphMetrics: Vertical Glyph Metrics
vBearingX
|<---------| origin
| | /
| |/
---------------------O----------------------------->
| | | |
| | vBearingY | |
| | v |
yMax - - +================+ - - - - - - - - |
I | I ^ |
I | I | |
I GLYPH | I | |
I OUTLINE I | height |
I HERE| I | |
I | I | |
I | I | |
I | I v | vAdvance
yMin - - +================+ - - - |
| | | v
| * - - - - - - - - - - - -
| | |
xMin v xMax
Table 6.perGlyphMetrics
Bit number
Glyph from LSB
Bit field name metric tag in bitmask Description (units in path space)
--------------------------------------- ---------- ---------- -------------------------------------------
GLYPH_WIDTH_BIT_NV width 0 Glyph's width
GLYPH_HEIGHT_BIT_NV height 1 Glyph's height
GLYPH_HORIZONTAL_BEARING_X_BIT_NV hBearingX 2 Left side bearing for horizontal layout
GLYPH_HORIZONTAL_BEARING_Y_BIT_NV hBearingY 3 Top side bearing for horizontal layout
GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV hAdvance 4 Advance width for horizontal layout
GLYPH_VERTICAL_BEARING_X_BIT_NV vBearingX 5 Left side bearing for vertical layout
GLYPH_VERTICAL_BEARING_Y_BIT_NV vBearingY 6 Top side bearing for vertical layout
GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV vAdvance 7 Advance height for vertical layout
GLYPH_HAS_KERNING_BIT_NV - 8 True if glyph has a kerning table.
Table 6.perFontFaceMetrics
Bit number
from LSB
Bit field name in bitmask Description (units in path space)
--------------------------------------- ---------- ---------------------------------------------------
FONT_X_MIN_BOUNDS_BIT_NV 16 Horizontal minimum (left-most) of the font bounding
box. The font bounding box (this metric and the
next 3) is large enough to contain any glyph from
the font face.
FONT_Y_MIN_BOUNDS_BIT_NV 17 Vertical minimum (bottom-most) of the font bounding
box.
FONT_X_MAX_BOUNDS_BIT_NV 18 Horizontal maximum (right-most) of the font
bounding box.
FONT_Y_MAX_BOUNDS_BIT_NV 19 Vertical maximum (top-most) of the font bounding
box.
FONT_UNITS_PER_EM_BIT_NV 20 Number of units in path space (font units) per
Em square for this font face. This is typically
2048 for TrueType fonts, and 1000 for PostScript
fonts.
FONT_ASCENDER_BIT_NV 21 Typographic ascender of the font face. For font
formats not supplying this information, this value
is the same as FONT_Y_MAX_BOUNDS_BIT_NV.
FONT_DESCENDER_BIT_NV 22 Typographic descender of the font face (always a
positive value). For font formats not supplying
this information, this value is the same as
FONT_Y_MIN_BOUNDS_BIT_NV.
FONT_HEIGHT_BIT_NV 23 Vertical distance between two consecutive baselines
in the font face (always a positive value).
FONT_MAX_ADVANCE_WIDTH_BIT_NV 24 Maximal advance width for all glyphs in this font
face. (Intended to make word wrapping computations
easier.)
FONT_MAX_ADVANCE_HEIGHT_BIT_NV 25 Maximal advance height for all glyphs in this
font face for vertical layout. For font formats
not supplying this information, this value is the
same as FONT_HEIGHT_BIT_NV.
FONT_UNDERLINE_POSITION_BIT_NV 26 Position of the underline line for this font face.
This position is the center of the underling stem.
FONT_UNDERLINE_THICKNESS_BIT_NV 27 Thickness of the underline of this font face.
FONT_HAS_KERNING_BIT_NV 28 True if font face provides a kerning table
consulted by the GetPathSpacingNV command discussed in Section 6.X.6
("Querying Spacing Information").
The query
void GetPathMetricRangeNV(bitfield metricQueryMask,
uint firstPathName,
sizei numPaths,
sizei stride,
float *metrics);
is equivalent to
int *array = malloc(sizeof(int)*numGlyphs);
if (array) {
for (int i=0; i, are supported.
Strings for ASCII base-85 encoded data, that is strings enclosed
in <~ and ~>, are supported for the data-array and operator-string
production
Also the short-binary-string, be-long-binary-string, and
le-long-binary-string productions allow very compact and precise
encoding of operator strings through binary encoding.
Strings for literal text, that is strings enclosed in ( and ),
are NOT supported.
The rationale for not supporting literal text is this format
is awkward for encoding the operator-string production (though
PostScript does technically allow it) and is not compact.
31. Should the PostScript grammar support Binary Object Sequences?
RESOLVED: No.
Binary Object Sequences are intended to support complex
(potentially nested) data structures and are over-kill for
user paths.
32. Why are the binary tokens in the PS grammar assigned the values
they are assigned?
RESOLVED: These values are from the "Binary Tokens" section of
the PostScript Language Reference Manual.
33. Why are the binary encodings for the path commands in the PS
grammar assigned the specified values?
RESOLVED: These values match PostScript's system name table
values. These are documented in the "System Name Encodings"
appendix of the PostScript Language Reference Manual.
Specifically (in decimal):
Index Name
----- ---------
22 closepath
99 lineto
107 moveto
133 rlineto
134 rmoveto
143 setbbox
43 curveto
122 rcurveto
5 arc
6 arcn
7 arct
177 ucache
34. Why do glGetPathCommandsNV, glGetPathCoordsNV, and
glGetPathDashArrayNV have their own queries? Could there not
simply be a token for glGetPathParameteriv/glGetPathParameterfvNV
to return this state?
RESOLVED: These queries for path commands, coordinates, and
the path's dash array return a variable payload of data so are
more like glGetTexImage than glGetIntegerv/glGetFloatv which
return a static amount of data.
APIs that return variable amounts of data are prone to buffer
overflows. It is somewhat more obvious these commands return
a variable amount of data if they have their own API calls, than
simply having certain token values to a multi-purpose glGet* call
that mysteriously returns varying amounts of data for these token
values while all the other tokens return static amounts of data.
This resolution follows the existing precedent from
core OpenGL where glGetColorTable is distinct from
glGetColorTableParameter{fv,iv}. Same with glGetConvolutionFilter
and glGetHistogram relative to glGetConvolutionParameter{fv,iv}
and glGetHistogramParameter{fv,iv}.
(There is a poor precedent for having an OpenGL query return both
static and varying amounts of data based on a pname parameter.
glGetMap{dv,fv,iv} returns varying data when GL_COEFF is queried
while GL_ORDER and GL_DOMAIN return n and 2*n values respectively
where n is the dimensionality of the map target. This isn't a
good precedent and is obscure.)
35. How should the GL_PATH_*_BOUNDING_BOX_NV path parameters be
returned?
RESOLVED: In (x1,y1,x2,y2) order where (x1,y1) is the minimum
bounds of the bounding box and (x2,y2) is the maximum bounds.
This is contrary to the precedent of GL_SCISSOR_BOX query
which returns the scissor as an (x,y,width,height) 4-tuple.
While that makes sense for a scissor box, particularly given how
the scissor is specified with glScissor, it is not a convenient
way to specify a bounding box.
The (x1,y1,x2,y2) format also makes the
glCover{Fill|Stroke}PathInstancedNV pseudo-code work nicely
with glRectf. See the renderBoundingBox pseudo-code.
The (x1,y1,x2,y2) format is also consistent with the way
FreeType2 provides per-font face bounds information through
the GL_FONT_X_MIN_BOUNDS_BIT_NV, GL_FONT_Y_MIN_BOUNDS_BIT_NV,
GL_FONT_X_MAX_BOUNDS_BIT_NV, and GL_FONT_Y_MAX_BOUNDS_BIT_NV
metric queries.
36. Why is font loading part of this extension? Shouldn't OpenGL
stick with just rendering and not involved itself with fonts?
RESOLVED: An explicit goal of this extension is to provide
GPU-accelerated path rendering that INCLUDES excellent support
for glyphs and their associated metrics.
The fact is all the major existing standards for path rendering
(PostScript, SVG, OpenVG, Java 2D, Quartz 2D, Flash) include
first-class font and glyph support.
Not including font and glyph support would be a glaring omission
that would make this extension much less useful to simple OpenGL
applications that don't want to incorporate large font libraries.
Additionally font loading is notoriously platform dependent.
This extension provides a simple platform-independent mechanism to
rendezvous with standard font names. However an implementation of
this extension can make use of whatever platform-specific font
services the platform provides (such as through DirectWrite,
etc.).
Fonts, particularly for Asian languages or designed to support a
large portion of Unicode, are large. Populating their complete
outlines can consume substantial amounts of system and video
memory. Many applications on a system are likely to access
the same collections of fonts. Having fonts loaded by name
allows GL implementations to coordinate the efficient sharing
of font outline data among multiple GL application instances.
This font sharing can have a substantial reduction in the total
system resources devoted to font data which is not possible if
the GL is unable to be aware of duplicated font outline data
within the system.
Font formats change and evolve over time. Building font format
knowledge into applications will ultimately be limiting long-term.
Fonts are really properly thought of as system resources.
They represent intellectual property that is typically licensed
on a per-system basis. Building font access into the GL promotes
use of the system's properly licensed fonts. Most applications
do not want to be encumbered by licensing issues associated with
fonts so to the extent that the API makes access to system fonts
easier, that promotes properly licensed use of fonts.
37. What is the typographical philosophy for this extension?
RESOLVED: This extension relies on other standards to provide
its typographic backbone and philosophy.
The character set supported depends on the Unicode standard.
Specific font formats supported depend on the system but the
expectation is that standard TrueType, PostScript, and OpenType
fonts can be used through this extension. The metrics from such
fonts will generally be "passed through" the glGetPathMetricsNV
query.
The naming of fonts is consistent with the underlying system
with the expectation that the system's naming is consistent with
modern web standards for identifying fonts in web content.
While the specific set of supported fonts may vary from system to
system based on the available installed fonts, the expectation is
that standard TrueType fonts such as Arial, New Courier, Georgia,
etc. will be available on systems that support this extension.
For applications that demand a set of glyphs that are guaranteed
to be available, the GL_STANDARD_FONT_NAME font target is
available for the names "Sans", "Serif", and "Mono" and these
fonts are understood to match a set of glyphs consistent with the
DejaVu font set populated with at least the Latin-1 character set.
The underlying font engine is likely to be FreeType2 or the
system's native font engine (such as DirectWrite for newer
Windows versions).
38. What is the path rendering philosophy for this extension?
RESOLVED: Two-step stencil-based GPU-acceleration + broad-tent
support for the accepted functionality of path rendering.
This extension assumes that the two-step "stencil, then cover"
stencil-based approach to GPU-accelerating path rendering.
Both stenciling and stroking are supported. Strokes are
first-class representations and not treated as fills that
approximate the stroked region. For pragmatic reasons, cubic
Bezier and partial elliptical (non-circular) arcs path segments
are assumed to be approximated by a sequence of quadratic Bezier
path segments that guarantee G1 continuity.
The contrapositive of this approach is an avoidance of schemes
based on tessellation of path outlines.
Paths are defined using both cubic and quadratic Bezier curves.
This broadly allows path content from TrueType (based on quadratic
Bezier curves) and PostScript and its font families (based on
cubic Bezier curves) to be supported.
Arcs are drawn consistent with both SVG (partial elliptical arcs)
and PostScript (circular arcs and circular tangent arcs).
The set of stroking options is a union of the stroking features
of OpenVG, SVG, XML Paper Specification (XPS), PostScript, and
other standards. For example, XPS supports dash caps that other
standards lack.
The path queries support the key path queries supported by OpenVG.
39. Should there be an API for assigning path metric information to
a path object?
RESOLVED: No.
Path metrics are available when a path object is created with
glPathGlyphsNV or glPathGlyphRangeNV. In these cases, the font
supplies the metric data for these path objects.
It might be useful to allow these metrics to be specified for an
arbitrary path object. This way user-defined path objects could
appear to have metrics available as if they had been specified
by glPathGlyphsNV or glPathGlyphRangeNV.
Supporting the specification of path metrics would require new
API. Something like glPathMetricsNV perhaps? Or having parameter
names for the font metrics supported by glPathParameter{f,i}v?
The later approach would probably require new tokens and would
mean glGetPathParameter{f,i}v should support these tokens too.
Since the metrics are for information purposes only, meaning
the rendering functionality for paths never involves the metrics
(unlike other path parameters), it seems odd to allow information
to be specified just so it can be queried by the application.
This doesn't feel like essential functionality though its
absence may be missed by library developers that want to "fake"
font loaders.
40. What happens when an input path object to glWeightPathsNV,
glInterpolatePathsNV contains an arc command when there are two
or more path objects involved?
RESOLVED: An INVALID_OPERATION error is generated.
In general, arc commands are not "closed" under linear
combination. Said another way, the linear combination of two
or more arcs is not, in general, itself an arc of the same form.
glCopyPathNV copies outlines for path objects containing any
valid commands including arc commands.
41. When a path object is created from other existing path objects
through the glWeightPathsNV, glInterpolatePathsNV, or glCopyPathNV
commands, where does the new path's parameters come from?
RESOLVED: While the path commands are interpolated on a
command-by-command basis with these commands, the path parameters
should be copied from the first path object specified.
So for glWeightPathsNV, glInterpolatePathsNV, and glCopyPathNV,
the path parameters from the path[0], pathA, and srcPath
parameters respectively.
42. How is the glyph metric and kerning information specified for
a path object created from other existing path objects through the
glWeightPathsNV, glInterpolatePathsNV, or glCopyPathNV commands,
where does the new path's parameters come from?
RESOLVED: The path metric information is set to negative one
for glWeightPathsNV and glInterpolatePathsNV.
There's no reasonable way to weight the metric information.
Metric information is tuned to a particular glyph.
More explicitly, the path metric information from the first path
object to be combined is NOT copied (as the parameters are).
However glCopyPathNV does copy the glyph metric and kerning
information (since only one path object is involved so there's
no combination of outlines).
43. Should there be a way to specify different stroking parameters
(stroke width, end caps, etc.) within the command sequence of
a path?
RESOLVED: No.
Existing path rendering standards keep the stroking parameters
constant for a given path's outline. For example, there's not
support for a dashed stroked segment of width 5.0 as well as a
non-dashed stroked segment with width 9.4 in the same path.
This wouldn't be impossible to support; commands that changed
stroking parameters could be supported within the command
sequence. However it would complicate the meaning of the path
parameters for stroking; these parameters could be considered
defaults for stroking parameters if stroking parameters are not
otherwise specified. There's also the complication of when
new stroking parameters would latch into place. Would it be
immediately (mid path?) or not latch until the next "moveto"
command? And how would such commands be weighted/interpolated?
Attempting to support changing stroking parameters within a path
appears to open up a complicated can of worms.
The same rendering effect can be achieved with the
gl{Stencil,Cover}StrokePathInstancedNV commands using multiple
path object, each with the appropriate stroking parameters for
the appropriate path segments.
44. What should the query token for the path color and texture
coordinate generation coefficients be named?
RESOLVED: GL_PATH_GEN_COEFF_NV.
Alternatively this could be GL_PATH_GEN_COEFFS_NV (plural),
but that doesn't match the precedent set by GL_COEFF used by
glGetMap{f,d}v. These existing queries return a plurality of
coefficients too.
45. What should the number of coefficients returned when querying the
path color and texture coordinate generation coefficients depend
on the current path color or texture coordinate generation mode or
should a fixed maximum number of coefficients always be returned?
RESOLVED: A fixed maximum of 16 coefficients should always
be returned.
It is error-prone and likely to result in obscure buffer
overflow cases if the number of coefficients returned depends
on the respective current path generation mode. It is better
to simply always return 16 values. Unused coefficients by the
current generation mode should always be returned as zero.
46. How does glGetPathLengthNV compare to OpenVG's vgPathLength?
RESOLVED: glGetPathLengthNV and vgPathLength compute
essentially the same result except glGetPathLengthNV returns
0 when /numSegments/ is 0 whereas vgPathLength considers this
case an error.
47. Where does all the discussion of partial elliptical arc
parameterization come from?
RESOLVED: This discussion is based on and fully consistent with:
http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
48. Where does the parameterization of the
GL_CIRCULAR_TANGENT_ARC_TO_NV come from?
RESOLVED: The GL_CIRCULAR_TANGENT_ARC_TO_NV is based on the
PostScript arct command (which is based on arcto) for user paths.
See the gs_arcto routine in:
http://svn.ghostscript.com/ghostscript/trunk/gs/base/gspath1.c
49. How should fog coordinate generation work for path rendering?
RESOLVED: The glPathFogGenNV command controls how generation
of the fog coordinate operates for path rendering commands.
The GL_FOG enable is tricky because it controls both per-vertex and
per-fragment processing state (unlike per-vertex lighting and texture
coordinate generation).
Simply using the existing fixed-function fog coordinate state is
undesirable because that 1) entangles fog coordinate generation
with conventional vertex processing and path vertex processing,
and 2) the NV_fog_distance extension allows a non-linear fog
coordinate to be generated through the GL_EYE_RADIAL_NV mode.
The fog coordinate generation for path rendering can either
use the fog coordinate "as is" for the entire covered path or
have the fog coordinate be the negated perspective-divided
eye-space Z component (which can vary, but only linearly).
50. What should glyph metrics return for path objects not specified
by glPathGlyphsNV or glPathGlyphRangeNV?
RESOLVED: All queried metrics should return the value -1.
Negative values are out-of-range for many of the metric values so
negative values provide a reliable indicator that a path object
was not specified from a glyph.
51. How should the fill mode state of path objects created from
glyphs be initialized?
RESOLVED: The initial GL_PATH_FILL_MODE_NV for path objects
created from glyphs depends on the source font's convention.
Typically TrueType and newer (all?) PostScript fonts depend on the
non-zero fill rule. TrueType fonts assume a clockwise outline
winding (hence will use GL_COUNT_DOWN_NV) while PostScript
fonts assume a counterclockwise outline winding (hence will
use GL_COUNT_UP_NV).
It's unlikely an actual font will use GL_INVERT as its
GL_PATH_FILL_MODE_NV but the possibility is allowed.
52. Should other path object parameters other than the fill mode be
initialized specially when path objects are specified from glyphs?
RESOLVED: No.
In theory, other path parameters such as stroke width, join style,
etc. could all be specified from the font. In practice, most
font forms don't provide such parameters.
At least one font format, Bitstream's PFA format, does provide
such information though how applicable these parameters are to
a path object is unclear. The availability of these parameters
appears to be intended as a way to bold or otherwise dilate the
glyph's outline rather than being intended for stroking.
SVG supports stroking of fonts but the stroke-width tag is
specified in the current user coordinate system rather than
depending on the particular font or its glyphs.
53. How should the integers passed to glPathGlyphsNV and
glPathGlyphRangeNV be mapped to actual glyph outlines for a font?
RESOLVED: The integers that come from the charcode array or
the firstGlyph to firstGlyph+numGlyphs-1 range are treated as
Unicode character codes if the font has a meaningful mapping of
Unicode to its glyphs.
The existence of a meaningful mapping from Unicode to glyph
outlines is the expected situation. For fonts without a
meaningful mapping to Unicode character codes (such as custom
symbol fonts), the font's standard mapping of character codes to
glyphs should be used. This situation should be rare, probably
due to a font that is poorly authored, very old, or custom built.
54. How are typographical situations such as ligatures, composite
characters, glyph substitution, and language-dependent character
sequence conversion handled?
RESOLVED: If a particular behavior is desired for how such
situations are handled, that is up to the application software
using this extension.
For example, in the case of ligatures, multiple Unicode characters
may map to a single ligature glyph. Support for ligatures is
a stylistic typographic decision and the application is free to
handle this in any of a number of ways; this extension neither
forces nor precludes specific approaches to handle ligatures.
The application can overlap existing glyphs to create the
appearance of a path object by rendering the individual multiple
Unicode characters overlapped; a ligature character that is
part of the Unicode character set could be selected; or the
application could create its own custom path object in this
situation and render it.
For composite characters, the underlying font engine used to
implement this extension may construct composite characters.
Or this may be a situation where, due to limitations of the
font or font engine, possibly in combination, this is treated as
an unknown or missing character where implementation-dependent
handling is possible. Such a situation could also exist for a
ligature character specified by Unicode.
In general, higher level details of text presentation such
as ligatures, composite characters, glyph substitution, and
language-dependent character sequence conversion are beyond the
scope of this extension.
See the Unicode FAQ on "Ligatures, Digraphs and Presentation
Forms":
http://www.unicode.org/faq/ligature_digraph.html
In complicated typographical situations, the assumption is that
the application will construct the appropriate inter-glyph
transformation values (the transformValues and transformType
for glStencilFillPathInstancedNV and glCoverFillPathInstancedNV)
and build digraphs or other presentation forms.
55. Are relative path commands converted to absolute commands upon
path specification?
RESOLVED: No, relative commands are first-class and are
maintained as relative commands.
This includes when relative commands are created by copying,
interpolating, or weighting existing path objects. Relative path
commands must match identical relative path commands and their
relatively control points are weighted as relative position
offsets.
Another implication if this is that if an application modifying
the control points with glPathSubCoordsNV, those edits can effect
the outline of subsequent relative commands that depend on the
modified coordinates.
The same applies to changing commands. Editing commands with
glPathSubCommandsNV can change how coordinates are interpreted
for the edited commands and subsequent relative commands.
In other words, if a path object is modified or edited, the
outline of the path is the same as if the path object had been
specified from scratch with the same command and coordinate
sequences.
56. What does this extension do with so-called "hinting" in outline
font glyphs?
RESOLVED: When a path object is specified from the glyph of a
font, the path object's outline is specified from the "ideal"
resolution-independent form of the glyph.
This is because a path object is rendered (stenciled or covered)
from a resolution-independent form. There is an implicit
assumption in the specified transformation and rendering process
that the process is unaware of the device coordinate grid.
This means there's not the knowledge of device coordinate space
necessary to apply hinting information.
In TrueType terms, this amounts to the path object's outline for
a TrueType glyph being the glyph's "master outline". This means
the TrueType instructions associated with the glyph are ignored
and not executed.
While it is beyond the scope of this extension, there's nothing
in this extension that keeps an application in decoding itself the
TrueType master outline of a glyph and performing the grid-fitted
outline generation at a given arbitrary device resolution.
Then this fitted outline could be specified for a path object.
The key observation is that doing so makes the resulting outline
resolution-dependent which obviates much of the advantage of
this extension's ability to render from a resolution-independent
outline.
Rather than relying on hinting for legibility, applications using
this extension are likely to rely on multisampling or multiple
jittered rendering passes for antialiasing and assume a certain
amount of grayscale appearance as a consequence.
57. If a font format has bitmap font data, is that used?
RESOLVED: No, only resolution-independent outline data is used;
bitmap data is ignored. Bitmap-only font formats won't be loaded.
In the FreeType 2 API, the information available is comparable
to calling FT_Load_Glyph with the FT_LOAD_NO_SCALE and
FT_LOAD_NO_BITMAP flags specified.
58. How is antialiasing of path object rendering accomplished?
RESOLVED: Multisampling is the expected way that antialiasing
will be accomplished when rendering path objects.
Recall in multisampling that the stencil buffer is maintained
at per-sample resolution. This means the coverage determined
by stenciling path objects should be accurate to the sample
resolution.
If a multisampled framebuffer provides N samples per pixel, that
means that there are N+1 possible coverage weightings of a given
path with respect to that pixel, assuming a single "stencil, then
cover step", equal weighting of samples in the final pixel color,
and the samples for a given pixel belonging to a single pixel.
One explicit goal of this extension is to maintain a separation
between coverage and opacity. The two concepts are often
conflated treating both as percentages and then modulating
opacity with coverage. Conflating the two leads to coverage
bleeding at what should be sharp, though transparent, edges and
corners.
In this extension, the stencil buffer maintains coverage and
the alpha channel for RGBA colors, which is per-sample when the
framebuffer format supports multisampling, maintains opacity.
Philosophically this extension provides a robust and accurate
mechanism for determining point-sampled coverage for arbitrary
filled and stroked paths. The extension does not rely on, nor
does it even attempt, to compute or approximate a path's area
coverage with respect to a pixel. For practical reasons, such
analytical computations are inevitably approximations for
arbitrary paths and are difficult to make robust.
Point sampling of path object rasterization can offer more
robustness and precision. Point sampling also allows this
extension's rendering results to seamlessly co-exist with OpenGL's
conventional point, line, and polygon rasterization approaches
which are point-sampled.
The implication of this observation is path rendered content can
be mixed with arbitrary OpenGL 3D content, whether rendered with
depth testing or not. This provides the very powerful ability to
mix path rendered and 3D rendered content in the same framebuffer
in predictable ways with negligible overhead for doing so.
Keep in mind that 2D path rendered content is transformed by the
projective modelview-projection transform, just like other OpenGL
rendering primitives, so fragments generated with path rendering
have varying depth values that can be depth tested, fogged, etc.
Point sampling is prone to missing coverage but avoids indicating
coverage where no actual coverage exists.
This extension implicitly assumes that GPUs have some maximum
sample location precision while rasterizing. This is an artifact
of subpixel precision. This concept is built into OpenGL; see
the GL_SUBPIXEL_BITS implementation-dependent limit. Developers
should not expect any additional sampling precision beyond this
limit. To get beyond this limit, applications would be expected
to render at a larger framebuffer resolution and downsample to
the appropriate resolution or render in some tiled fashion.
If multisampling provides insufficient antialiasing, further
antialiasing is possible by rendering with multiple passes.
For example, applications can use accumulation buffer techniques
with sub-pixel jittered re-rendering of the entire scene
to improve the overall quality. This provides full-scene
antialiasing.
Alternatively, a path object itself needing extra antialiasing,
perhaps because the application has determined the path object
maps to a small region of the framebuffer in window space, can
be rendered multiple times, each time with subpixel jittering.
By writing just into the non-visible alpha component of the
framebuffer, a coverage percentage at each color sample can
be accumulated. Then a final cover operation can blend this
coverage information into the visible RGB color channels.
Despite the multiple passes involved, this approach can still
be several times faster than CPU path rendering methods because
of the rendering rate possible through GPU acceleration.
59. How do the multisample fragment operations interact with path
rendering?
RESOLVED: They are ignored for the "stencil" path rendering
operations (since only the stencil buffer is updated), and they
work as specified for the "cover" path rendering operation.
The coverage determination made during the "cover" path
rendering operation doesn't reflect the path itself but rather
the conservative coverage provide by the covering operation.
For this reason, the coverage mask is conservative, meaning
samples may be covered that don't actually belong to the filled
or stenciled region of the path being covered. And exactly how
conservative this coverage is depends on the implementation.
Still the coverage is available and can be used as specified in
section 4.1.3 ("Multisample Fragment Operations").
The GL_SAMPLE_COVERAGE mode would be more useful if the stencil
testing was performed prior to the shading of the covered geometry
and the covered sample mask reflected any discards performed by
the stencil (or depth) tests.
The NV_explicit_multisample extension and its
ARB_texture_multisample functionality (standard with OpenGL 3.2)
provide explicit control of the multisample mask. This mask is
respected for path rendering.
60. Does creating multiple instances of path objects from the same
glyph in the same font face "waste memory"? What about copies
of objects created with glCopyPathNV?
RESOLVED: This is an implementation issue, but it is reasonable
to expect that copies of path objects created with glCopyPathNV
will share their outline data on a copy-on-write basis. This is
true even if a path object is copied and its path parameters
are modified (but not the path commands and coordinates).
It is also reasonable to expect that path objects created with
glPathGlyphsNV may use copies if there are replicated character
codes. While glPathGlyphRangeNV isn't subject to replicated
character codes, if two or more character codes share the same
glyph, it would be reasonable to expect the implementation might
share the outline data.
It's always possible to use glPathSubCommandsNV or
glPathSubCoordsNV to modify the path commands and/or coordinate
data so then sharing will have to be broken.
61. Why does glPathGlyphsNV (and hence glPathGlyphRangeNV as well)
not disturb path objects that already exist in the range of path
objects to be created?
RESOLVED: This facilitates a strategy for supporting multiple
font names specified in preferential order.
An application can do something like:
GLint firstPathName = glGenPathsNV(256);
const GLfloat emScale = 2048;
glPathGlyphRangeNV(firstPathName, GL_SYSTEM_FONT_NAME_NV,
"Helvetica", GL_NONE, 0, 256, emScale);
glPathGlyphRangeNV(firstPathName, GL_SYSTEM_FONT_NAME_NV,
"Arial", GL_NONE, 0, 256, emScale);
glPathGlyphRangeNV(firstPathName, GL_STANDARD_FONT_NAME_NV,
"Sans", GL_NONE, 0, 256, emScale);
This ensures that path object names /firstPathName/ through
/firstPathName/+255 will be loaded with the glyphs from Helvetica,
Arial, or the guaranteed-present Sans font face, in that order
of preference.
This is consistent with the CSS font-family property used in
web standards, including SVG.
62. Why are the angles for the arc path commands specified with
degrees (instead of radians)?
RESOLVED: Using degrees is consistent with OpenGL's existing
glRotatef, glRotated, and gluPerspective commands.
Using degrees for angles is also consistent with the conventions
of the PostScript, SVG, and OpenVG commands upon which the arc
path commands are based.
Using degrees (90 degrees, 30 degrees, 45 degrees) also allows
important angles be represented exactly with integer values.
This is relevant for compact coordinate formats and paths defined
by strings.
63. Should UTF-8 and UTF-16 be supported for arrays of path names?
RESOLVED: Yes.
64. What order should the arguments be listed when a array of
path objects with typed elements and a base are specified?
RESOLVED:
1) sizei count,
2) enum pathNameType,
3) const void *paths,
4) uint pathBase
The standard OpenGL parameter pattern is count/type/array.
Examples of this are glDrawElements and glCallLists.
(More generally the pattern is count/format/type/array.)
Having the pathBase parameter last matches the precedent set by.
glDrawElementsBaseVertex where the base vertex value follows
the list of element indices. Hence the pattern
count/type/array/base.
The basevertex parameter to glDrawElementsBaseVertex is typed
GLint; the pathBase parameter is typed GLuint. GLuint makes
sense to avoid useless signed/unsigned mismatch warnings from
C compilers when most values passed to pathBase parameters are
likely to be from GLuint variables. When GLuint and GLint are
both 32-bit data types, the choice is not consequential.
Commands that use this order are glStencilFillPathInstancedNV,
glStencilStrokePathInstancedNV, glCoverFillPathInstancedNV,
glCoverStrokePathInstancedNV, glGetPathMetricsNV, and
glGetPathSpacingNV.
65. What order should the arguments be listed when a range of
path objects is specified?
RESOLVED:
1) uint firstPath,
2) sizei count
The glDeletePathsNV command and GetPathMetricRangeNV query use
this order.
glDeleteLists uses this same order.
66. Where does the UTF-8 and UTF-16 specification language come from?
See the RFC "UTF-8, a transformation format of ISO 10646":
http://tools.ietf.org/html/rfc3629
See the RFC "UTF-16, an encoding of ISO 10646":
http://tools.ietf.org/html/rfc2781
The intent of the specification language is to match these RFCs.
67. How does the GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV cover mode
work for glCoverFillPathInstancedNV and glCoverStrokePathInstancedNV?
RESOLVED: The command computes the bounding box of all the
path's bounding boxes. (This can be too conservative for an
arbitrarily arranged collection of path objects but works well
enough for glyphs in line of text.)
This bounding box has a consistent counterclockwise winding
order no matter what path objects are listed. This property
is a combination of how glRectf works and how the parameters to
glRectf are computed.
The object-space z (depth) is always zero. (This behavior is
a consequence of the primitive being emitted by glRectf.) The
matrix elements in the Z row (if such a row exists) of the
transforms specified for glCoverFillPathInstancedNV and
glCoverStrokePathInstancedNV is ignored.
Programmers are cautioned that this could result in the
covering geometry being view-frustum culled if the programmer
is not careful when using 3D transformTypes (GL_TRANSLATE_3D_NV,
GL_AFFINE_3D_NV, GL_TRANSPOSE_AFFINE_3D_NV). To guard against
this mishap, consider something such as the following:
glMatrixPushEXT(GL_PROJECTION);
glScalef(1,1,0);
glCoverFillPathInstancedNV(...);
glMatrixPopEXT(GL_PROJECTION);
This essentially forces the clip-space Z to be zero which will
never be clipped by the near or far view-frustum clip planes.
If depth testing is desired, perform the depth testing during the
"stenciling" step so that depth testing is unnecessary during the
"covering" step done by the glCoverFillPathInstancedNV command.
68. What happens when the radius of a circular arc command is
negative?
UNRESOLVED: The intent is to match the behavior of the PostScript
circular arc commands (arc, arcn, arct). Unfortunately the
PostScript specification is not entirely clear about how negative
radius is handled.
Table 5.arcParameterSpecialization has absolute values (abs)
computed for the rv and rh columns.
However, the points A and B (used for arc and arcn) are computed
with c[2] directly (without an absolute value).
This computation looks consistent with Ghostscript's behavior
for arct:
dist = abs(c[4] * num/denom)
l0 = dist/sqrt(dot(d0,d0)) * c[4]/abs(c[4])
l2 = dist/sqrt(dot(d2,d2)) * c[4]/abs(c[4])
Could this simply be:
dist = c[4] * num/denom
l0 = dist/sqrt(dot(d0,d0))
l2 = dist/sqrt(dot(d2,d2))
Probably.
This really needs testing and comparison with a PostScript
implementation to make sure the specified equations really match
PostScript's implemented behavior.
69. What happens when the two angles (c[2] and c[3]) for a circular
arc command (GL_CIRCULAR_CCW_ARC_TO_NV or
GL_CIRCULAR_CW_ARC_TO_NV) create 1 or more full revolutions?
UNRESOLVED: The intent is to match the behavior of the PostScript
circular arc commands (arc and arcn).
PostScript specifies that "If angle2 is less than angle1, it is
increased by multiples of 360 [degrees] until it becomes greater
than or equal to angle1. No other adjustments are made to the
two angles. In particular, if the difference angle2-angle1
exceeds 360 [degrees], the resulting path will trace portions
of the circle more than once."
The current equations based on an end-point partial elliptical arc
parameterization achieve this. Extra parametric behavior would be
necessary to trace a circle multiple times. The current equations
in Table 5.pathEquations do not capture this (but should).
This needs to be thought through carefully to make sure stroking,
particularly when dashed, is handled correctly.
70. PostScript generates a limitcheck error when numbers are
encountered that exceed the implementation limit for real numbers.
Should the PostScript grammar treat such situations as a parsing
error?
RESOLVED: No, it's not a parsing error, but the results in
such a situation are likely to be undefined.
This paragraph in Section 5.X.1 ("Path Specification") applies
which begins "If a value specified for a coordinate (however
the coordinate is specified) or a value computed from these
coordinates (as specified in the discussion that follows)
exceeds the implementation's maximum representable value for a
single-precision floating-point number, ..."
The PostScript's notion of a limitcheck error doesn't nicely
correspond to a parsing error. And PostScript's notion of "the
implementation limit for real numbers" (likely double precision)
might not correspond to the GL's notion of floating-point
(typically single precision).
The PostScript notion of a limitcheck on numeric range is
particularly hard to enforce with relative commands where the
limitcheck might not occur until all the relative offsets are
applied, something which isn't really part of parsing.
What an actual implementation does may vary but a likely
implementation approach is generate an IEEE infinity value when
single-precision floating-point range is exceeded. This will
generate undefined rendering behavior.
SVG doesn't offer guidance in its specification when coordinate
values exceed the representable range of floating-point.
Presumably such range overflows result in implementation-dependent
undefined rendering behavior too.
71. What happens when the radius of a OpenVG-style partial elliptical
arc commands is negative?
RESOLVED: The absolute value of the radius is used for
the OpenVG-style arc commands GL_SMALL_CCW_ARC_TO_NV,
GL_RELATIVE_SMALL_CCW_ARC_TO_NV, GL_SMALL_CW_ARC_TO_NV,
GL_RELATIVE_SMALL_CW_ARC_TO_NV, GL_LARGE_CCW_ARC_TO_NV,
GL_RELATIVE_LARGE_CCW_ARC_TO_NV, GL_LARGE_CW_ARC_TO_NV, and
GL_RELATIVE_SMALL_CW_ARC_TO_NV.
Table 5.arcParameterSpecialization specifies an absolute value
(abs) in the rh and rv entries of all these commands.
The OpenVG specification is clear on this point in section 8.4
("Elliptical Arcs") saying "Negative values of [radii] rh and
rv are replaced with their absolute values."
72. What should happen for a stroked subpath that is zero length?
UNRESOLVED: Not sure yet.
SVG gives this advice:
http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
Probably need to check what other path renders, particularly
PostScript do in this situation. Requires testing actual
implementations because the specifications are not clear.
73. Why have the GL_PATH_CLIENT_LENGTH_NV path parameter?
RESOLVED: This supports SVG's pathLength attribute used to
calibrate distance-along-a-path computations.
This applies to dashing a stroked segment, but does NOT
apply to the lengths returned by the glGetPathLengthNV and
glPointAlongPathNV queries.
The client length just applies to dashing because having a
client length that is different from the GL's computed length
for a path may greatly affect the dashing pattern. The client
knows the path's client length, but the GL doesn't unless the
client state is available to the GL when dashing a stroked path.
It's better to have the client send the client path length
unconditionally than require the client to query the GL's computed
path length ahead of any sending of a rescaled version of the
dash offset or dash array.
For the queries, presumably the client can perform the necessary
scaling by the client length itself if that's desirable.
74. Should there be a query for GL_PATH_END_CAPS_NV and
GL_PATH_DASH_CAPS_NV?
RESOLVED: No. You have to query GL_PATH_INTIAL_END_CAP_NV or
GL_PATH_TERMINAL_END_CAP_NV for the each respective end cap; or
query GL_PATH_INITIAL_DASH_CAP_NV or GL_PATH_TERMINAL_DASH_CAP_NV
for each respective dash cap.
GL_PATH_END_CAPS_NV and GL_PATH_DASH_CAPS_NV are convenient
for most path rendering systems that have identical initial
and terminal end and dash caps, but are NOT supported by
glGetPathParameteriv or glGetPathParameterfv.
75. What should the path format tokens for SVG and PostScript tokens
be named?
RESOLVED: Use the abbreviated names SVG and PS respectively:
GL_PATH_FORMAT_SVG_NV and GL_PATH_FORMAT_PS_NV. These names
are shorter and avoid putting an Adobe trademark in a token name.
Future extensions might want to add version numbers to these
abbreviated names (another reason to stick with short abbreviated
names).
76. In what content (GL client or GL server) are font file names
and system font names interpreted?
RESOLVED: The GL_STANDARD_FONT_NAME_NV and GL_SYSTEM_FONT_NAME_NV
font targets map their respective font names to a font within
the GL server. The GL_FILE_NAME_NV font target does the file
reading in the GL client; for GLX, there needs to be GLX protocol
to transfer glyphs including their kerning and metric data to
the GL server.
77. How can the glPathSubCommandsNV command be used to append to
the end of an existing path object?
RESOLVED: If you set the /commandStart/ parameter to
glPathSubCommandsNV to be sufficiently large (greater or equal
to the number of path commands in the path object suffices),
that works to append commands.
78. Does depth offset (a.k.a. polygon offset) work when using the
"stencil" and "cover" path operations?
RESOLVED: Yes with caveats.
The "stencil" path operations use the
GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV and
GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV state set by
glPathStencilDepthOffsetNV. There is no specific enable; instead
set the scale and units to zero if no depth offset is desired.
The "cover" path operations use the polygon depth offset state if
the GL_POLYGON_OFFSET_FILL enable is enabled, using the polygon
offset factor and units specified for glPolygonOffset. This is
because the "cover" operation (unlike the stencil operation)
does rasterize a polygon primitive.
Depth offset is useful when a path rendered decal is applied
on depth tested 3D geometry and the path rendered geometry has
to be biased forward (negative bias) by polygon offset to avoid
depth ambiguities. See issue #120 for details.
This is also useful when putting path rendered primitives into
shadow maps with a positive depth bias to avoid shadow acne
issues.
There is NOT a guarantee that the depth offset computed for a
"stencil" operation will exactly match the depth offset for a
"cover" operation given identical path object and transformations.
The two offsets will be close but not generally exact for all
generated samples.
79. Can fragment shaders access the facingness state during a cover
operation?
RESOLVED: Yes, the gl_FrontFacing special variable in GLSL
is available. So is the fragment.facing fragment attribute
binding in NV_fragment_program2 and subsequent NVIDIA shader
assembly extensions.
In cases where the path rendered primitive is "very edge" on the
facingness fragment state may be ambiguous in extreme situations.
80. When are various computed path parameters re-computed?
RESOLVED: If the computed parameter parameters
(PATH_COMMAND_COUNT_NV, PATH_COORD_COUNT_NV,
PATH_DASH_ARRAY_COUNT_NV, PATH_COMPUTED_LENGTH_NV,
PATH_OBJECT_BOUNDING_BOX_NV, PATH_FILL_BOUNDING_BOX_NV, and
PATH_STROKE_BOUNDING_BOX_NV) are queried, the values returned
always reflect the most up-to-date state of the path object.
This also includes when path object parameters are used in
contexts such as instanced "cover" operations.
81. Should projective 2D path coordinates be supported?
RESOLVED: No. Major path rendering standards don't support
projective 2D path coordinates.
Moreover, projective 2D path coordinates create technical
problems because the projective transformation of projective
2D path coordinates for cubic Bezier curves do not necessarily
retain their topology (serpentine, cusp, or loop).
82. Should a non-dashed stroked path's coverage be the same
independent of how its control points are specified?
RESOLVED: Yes, this is a symmetry rule mandated by the OpenXML
Paper Specification. This applies to lines and Bezier curves.
So a cubic Bezier curve defined by control points cp0, cp1, cp2,
and cp3 should generate the same stroked coverage (assuming the
same stroke parameters and requiring the dash array count to be
zero) as a cubic Bezier curve with control points cp3, cp2, cp1,
and cp0 (so the reversed control point order).
XXX Unresolved if it applies to arcs.
83. Should character aliases used to specify path commands be returned
as their character alias values or remapped to the actual token
name of the command?
RESOLVED: Remapped. Any path commands specified with a
character alias value (from Table 5.pathCommands) is returned
as the command's token value instead.
This avoids applications calling glGetPathCommandsNV from having
bugs where they handle token names but not character aliases.
This also makes it simpler to say "identical" when saying command
sequences must match for glWeightPathsNV. Character aliases
remapped to command token values makes it unambiguous that
GL_LINE_TO and 'L" are the identical command.
84. Is there a way to use this extension to trade-off rendering performance
for more effective samples per pixel to improve coverage quality?
RESOLVED: Yes.
This code demonstrates how multiple passes could accumulate
coverage information in the alpha channel of the framebuffer and
then a final cover pass could blend the incoming color with the
accumulated coverage from the framebuffer's alpha channel.
// INITIALIZATION
// assume stencil is cleared to zero and framebuffer alpha is clear to zero
const int coveragePassesToAccumulate = 4;
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NOT_EQUAL, 0x80, 0x7F);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // tricky: zero 0x7F mask stencil on covers, but set 0x80
glColorMask(0,0,0,1); // just update alpha
// M STENCIL+COVER PASSES to accumulate jittered path coverage into framebuffer's alpha channel
glStencilFillPathNV(path, GL_COUNT_UP_NV, 0x7F);
glCoverFillPathNV(path, GL_PATH_FILL_COVER_MODE_NV);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE); // sum up alpha
glColor4f(0,0,0, 1.0/coveragePassesToAccumulate );
static const GLfloat jitters[4][2] = { {0,0}, /* various small subpixel jitter X & Y values */ };
for (i=1; iMatrixMultfEXT
11 10/21/13 mjk parameter for PathGlyphsNV
and PathGlyphRangeNV
12 11/05/13 dsn Use consistent argument names