Name NV_path_rendering Name Strings GL_NV_path_rendering Contact Mark Kilgard, NVIDIA (mjk 'at' nvidia.com) Status Released in NVIDIA Driver Release 275.33 (June 2011). Version Last Modified Date: June 14, 2011 Version: 1 Number XXX 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-existant 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_NV|GL_FONT_Y_MAX_BOUNDS_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, glyphBase, GL_UNSIGNED_BYTE, "OpenGL", GL_PATH_FILL_MODE_NV, 0xFF, GL_TRANSLATE_2D_NV, xtranslate); glCoverFillPathInstancedNV(6, glyphBase, GL_UNSIGNED_BYTE, "OpenGL", 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, int 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 name, 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 name, enum param, int *value); void GetPathParameterfvNV(uint name, enum param, float *value); void GetPathCommandsNV(uint name, ubyte *commands); void GetPathCoordsNV(uint name, float *coords); void GetPathDashArrayNV(uint name, float *dashArray); void GetPathMetricsNV(bitfield metricQueryMask, sizei numPaths, enum pathNameType, const void *paths, uint pathBase, sizei stride, float *metrics); void GetPathMetricRangeNV(bitfield metricQueryMask, uint fistPathName, 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 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 CallLists, 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_STENCIL_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 PathStringNV: 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_NV 0x100 // per-font face metrics FONT_X_MIN_BOUNDS_NV 0x00010000 FONT_Y_MIN_BOUNDS_NV 0x00020000 FONT_X_MAX_BOUNDS_NV 0x00040000 FONT_Y_MAX_BOUNDS_NV 0x00080000 FONT_UNITS_PER_EM_NV 0x00100000 FONT_ASCENDER_NV 0x00200000 FONT_DESCENDER_NV 0x00400000 FONT_HEIGHT_NV 0x00800000 FONT_MAX_ADVANCE_WIDTH_NV 0x01000000 FONT_MAX_ADVANCE_HEIGHT_NV 0x02000000 FONT_UNDERLINE_POSITION_NV 0x04000000 FONT_UNDERLINE_THICKNESS_NV 0x08000000 FONT_HAS_KERNING_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 -------------------------- --------------------- ---------- ---------- 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 -------------------------- --------------------- ---------- ---------- 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, 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, 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. 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.x npep.y = sp.y -------------------------- -------------------------------------- ------------------ --------------------- 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) -------------------------- -------------------------------------- ------------------ --------------------- CIRCULAR_CCW_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_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 an 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 an 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 an 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_NV, FONT_UNITS_PER_EM_NV, and FONT_HAS_KERNING_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 an 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= 0x2) { pathName = pathBase + ((c1 & 0x3F) | (c0 & 0x1F) << 6); } else { // Skip overlong encoding: start of a 2-byte sequence, // but code point <= 127. // Stop processing the UTF byte sequence early. return 0; } p += 2; } else { ubyte c2 = p[2]; if ((c0 & 0xF0) == 0xE0) { pathName = pathBase + ((c2 & 0x3F) | (c1 & 0x3F) << 6 | (c0 & 0xF) << 12); p += 3; } else { ubyte c3 = p[3]; if ((c0 & 0xF8) == 0xF0) { pathName = pathBase + ((c3 & 0x3F) | (c2 & 0x3F) << 6 | (c1 & 0x3F) << 12 | (c0 & 0x7) << 18); p += 4; } else { // Skip invalid or restricted encodings. // Stop processing the UTF byte sequence early. return 0; } } } } paths = p; return 1; } 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)) { // Stop processing the UTF byte sequence early. return 0; } else { 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 0; } } } paths = p; return 1; } 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" Update the paragraph specifying CallLists: After the discussion of 2_BYTES, 3_BYTES, and 4_BYTES, but before the statement "If n=0, CallLists does nothing." insert: "Further /type/ may be either UTF8_NV or UTF16_NV, indicating the array is a variable-length 8-bit Unicode Transformation Format (UTF-8) encoding or 16-bit Unicode Transformation Format (UTF-16) encoding. In the case of the UTF-8 token (UTF8_NV), each integer offset is constructed according to the following algorithm where listBase the value specified by ListBase (discussed next): const ubyte *p = (const ubyte *)lists; for (int i=0; i= 0x2) { uint list = (c1 & 0x3F) | (c0 & 0x1F) << 6; CallList(list + listBase); } else { // Skip overlong encoding: start of a 2-byte sequence, // but code point <= 127. // Stop processing the UTF byte sequence early. break; } p += 2; } else { ubyte c2 = p[2]; if ((c0 & 0xF0) == 0xE0) { uint list = (c2 & 0x3F) | (c1 & 0x3F) << 6 | (c0 & 0xF) << 12; CallList(list + listBase); p += 3; } else { ubyte c3 = p[3]; if ((c0 & 0xF8) == 0xF0) { uint list = (c3 & 0x3F) | (c2 & 0x3F) << 6 | (c1 & 0x3F) << 12 | (c0 & 0x7) << 18; CallList(list + listBase); p += 4; } else { // Skip invalid or restricted encodings. // Stop processing the UTF byte sequence early. break; } } } } } In the case of the UTF-16 token (UTF16_NV), each integer offset is constructed according to the following algorithm: const ushort *p = (const ushort *)lists; for (int i=0; i 0xDFFF)) { uint list = s0; CallList(list + listBase); p += 1; } else { if ((s0 >= 0xDB00) && (s0 <= 0xDBFF)) { // Stop processing the UTF byte sequence early. break; } else { ushort s1 = p[1]; if ((s1 >= 0xDC00) && (s1 <= 0xDFFF)) uint list = ((s0 & 0x3FF) << 10 | (s1 & 0x3FF)) + 0x10000; CallList(list + listBase); p += 2; } else { // Stop processing the UTF byte sequence early. break; } } } } 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 name, enum param, int *value); void GetPathParameterfvNV(uint name, enum param, 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 name, 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 name, 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 name, 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_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_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_NV 17 Vertical minimum (bottom-most) of the font bounding box. FONT_X_MAX_BOUNDS_NV 18 Horizontal maximum (right-most) of the font bounding box. FONT_Y_MAX_BOUNDS_NV 19 Vertical maximum (top-most) of the font bounding box. FONT_UNITS_PER_EM_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_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_NV. FONT_DESCENDER_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_NV. FONT_HEIGHT_NV 23 Vertical distance between two consecutive baselines in the font face (always a positive value). FONT_MAX_ADVANCE_WIDTH_NV 24 Maximal advance width for all glyphs in this font face. (Intended to make word wrapping computations easier.) FONT_MAX_ADVANCE_HEIGHT_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_NV. FONT_UNDERLINE_POSITION_NV 26 Position of the underline line for this font face. This position is the center of the underling stem. FONT_UNDERLINE_THICKNESS_NV 27 Thickness of the underline of this font face. FONT_HAS_KERNING_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_NV, GL_FONT_Y_MIN_BOUNDS_NV, GL_FONT_X_MAX_BOUNDS_NV, and GL_FONT_Y_MAX_BOUNDS_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. glCallLists should also be extended to take UTF type data. This allows complex Unicode text to be rendered by glCallLists. 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; i