skia / external / github.com / KhronosGroup / OpenGL-Registry / 108-cleanup-xml / . / extensions / NV / NV_blend_equation_advanced.txt

Name | |

NV_blend_equation_advanced | |

Name Strings | |

GL_NV_blend_equation_advanced | |

GL_NV_blend_equation_advanced_coherent | |

Contact | |

Pat Brown, NVIDIA Corporation (pbrown 'at' nvidia.com) | |

Contributors | |

Jeff Bolz, NVIDIA Corporation | |

Mathias Heyer, NVIDIA Corporation | |

Mark Kilgard, NVIDIA Corporation | |

Daniel Koch, NVIDIA Corporation | |

Rik Cabanier, Adobe | |

Status | |

NV_blend_equation_advanced is released in NVIDIA Driver Release | |

326.xx (June 2013). | |

Version | |

Last Modified Date: September 30, 2014 | |

NVIDIA Revision: 9 | |

Number | |

OpenGL Extension #433 | |

OpenGL ES Extension #163 | |

Dependencies | |

This extension is written against the OpenGL 4.1 Specification | |

(Compatibility Profile). | |

OpenGL 2.0 is required (for Desktop). | |

OpenGL ES 2.0 is required (for mobile). | |

EXT_blend_minmax is required (for mobile). | |

This extension interacts with OpenGL 4.0. | |

This extension interacts with OpenGL 4.1 (Core Profile). | |

This extension interacts with OpenGL 4.3 or later. | |

This extension interacts with OpenGL ES 2.0. | |

This extension interacts with OpenGL ES 3.0. | |

This extension interacts with NV_path_rendering. | |

Overview | |

This extension adds a number of "advanced" blending equations that can be | |

used to perform new color blending operations, many of which are more | |

complex than the standard blend modes provided by unextended OpenGL. This | |

extension provides two different extension string entries: | |

- NV_blend_equation_advanced: Provides the new blending equations, but | |

guarantees defined results only if each sample is touched no more than | |

once in any single rendering pass. The command BlendBarrierNV() is | |

provided to indicate a boundary between passes. | |

- NV_blend_equation_advanced_coherent: Provides the new blending | |

equations, and guarantees that blending is done coherently and in API | |

primitive ordering. An enable is provided to allow implementations to | |

opt out of fully coherent blending and instead behave as though only | |

NV_blend_equation_advanced were supported. | |

Some implementations may support NV_blend_equation_advanced without | |

supporting NV_blend_equation_advanced_coherent. | |

In unextended OpenGL, the set of blending equations is limited, and can be | |

expressed very simply. The MIN and MAX blend equations simply compute | |

component-wise minimums or maximums of source and destination color | |

components. The FUNC_ADD, FUNC_SUBTRACT, and FUNC_REVERSE_SUBTRACT | |

multiply the source and destination colors by source and destination | |

factors and either add the two products together or subtract one from the | |

other. This limited set of operations supports many common blending | |

operations but precludes the use of more sophisticated transparency and | |

blending operations commonly available in many dedicated imaging APIs. | |

This extension provides a number of new "advanced" blending equations. | |

Unlike traditional blending operations using the FUNC_ADD equation, these | |

blending equations do not use source and destination factors specified by | |

BlendFunc. Instead, each blend equation specifies a complete equation | |

based on the source and destination colors. These new blend equations are | |

used for both RGB and alpha components; they may not be used to perform | |

separate RGB and alpha blending (via functions like | |

BlendEquationSeparate). | |

These blending operations are performed using premultiplied colors, where | |

RGB colors stored in the framebuffer are considered to be multiplied by | |

alpha (coverage). The fragment color may be considered premultiplied or | |

non-premultiplied, according the BLEND_PREMULTIPLIED_SRC_NV blending | |

parameter (as specified by the new BlendParameteriNV function). If | |

fragment color is considered non-premultiplied, the (R,G,B) color | |

components are multiplied by the alpha component prior to blending. For | |

non-premultiplied color components in the range [0,1], the corresponding | |

premultiplied color component would have values in the range [0*A,1*A]. | |

Many of these advanced blending equations are formulated where the result | |

of blending source and destination colors with partial coverage have three | |

separate contributions: from the portions covered by both the source and | |

the destination, from the portion covered only by the source, and from the | |

portion covered only by the destination. The blend parameter | |

BLEND_OVERLAP_NV can be used to specify a correlation between source and | |

destination pixel coverage. If set to CONJOINT_NV, the source and | |

destination are considered to have maximal overlap, as would be the case | |

if drawing two objects on top of each other. If set to DISJOINT_NV, the | |

source and destination are considered to have minimal overlap, as would be | |

the case when rendering a complex polygon tessellated into individual | |

non-intersecting triangles. If set to UNCORRELATED_NV (default), the | |

source and destination coverage are assumed to have no spatial correlation | |

within the pixel. | |

In addition to the coherency issues on implementations not supporting | |

NV_blend_equation_advanced_coherent, this extension has several | |

limitations worth noting. First, the new blend equations are not | |

supported while rendering to more than one color buffer at once; an | |

INVALID_OPERATION will be generated if an application attempts to render | |

any primitives in this unsupported configuration. Additionally, blending | |

precision may be limited to 16-bit floating-point, which could result in a | |

loss of precision and dynamic range for framebuffer formats with 32-bit | |

floating-point components, and in a loss of precision for formats with 12- | |

and 16-bit signed or unsigned normalized integer components. | |

New Procedures and Functions | |

void BlendParameteriNV(enum pname, int value); | |

void BlendBarrierNV(void); | |

New Tokens | |

Accepted by the <cap> parameter of Disable, Enable, and IsEnabled, and by | |

the <pname> parameter of GetIntegerv, GetBooleanv, GetFloatv, GetDoublev | |

and GetInteger64v: | |

BLEND_ADVANCED_COHERENT_NV 0x9285 | |

Note: The BLEND_ADVANCED_COHERENT_NV enable is provided if and only if | |

the NV_blend_equation_advanced_coherent extension is supported. On | |

implementations supporting only NV_blend_equation_advanced, this enable is | |

considered not to exist. | |

Accepted by the <pname> parameter of BlendParameteriNV, GetBooleanv, | |

GetIntegerv, GetInteger64v, GetFloatv, and GetDoublev: | |

BLEND_PREMULTIPLIED_SRC_NV 0x9280 | |

BLEND_OVERLAP_NV 0x9281 | |

Accepted by the <value> parameter of BlendParameteriNV when <pname> is | |

BLEND_PREMULTIPLIED_SRC_NV: | |

TRUE | |

FALSE | |

Accepted by the <value> parameter of BlendParameteriNV when <pname> is | |

BLEND_OVERLAP_NV: | |

UNCORRELATED_NV 0x9282 | |

DISJOINT_NV 0x9283 | |

CONJOINT_NV 0x9284 | |

Accepted by the <mode> parameter of BlendEquation and BlendEquationi: | |

ZERO // reused from core | |

SRC_NV 0x9286 | |

DST_NV 0x9287 | |

SRC_OVER_NV 0x9288 | |

DST_OVER_NV 0x9289 | |

SRC_IN_NV 0x928A | |

DST_IN_NV 0x928B | |

SRC_OUT_NV 0x928C | |

DST_OUT_NV 0x928D | |

SRC_ATOP_NV 0x928E | |

DST_ATOP_NV 0x928F | |

XOR_NV 0x1506 | |

MULTIPLY_NV 0x9294 | |

SCREEN_NV 0x9295 | |

OVERLAY_NV 0x9296 | |

DARKEN_NV 0x9297 | |

LIGHTEN_NV 0x9298 | |

COLORDODGE_NV 0x9299 | |

COLORBURN_NV 0x929A | |

HARDLIGHT_NV 0x929B | |

SOFTLIGHT_NV 0x929C | |

DIFFERENCE_NV 0x929E | |

EXCLUSION_NV 0x92A0 | |

INVERT // reused from core | |

INVERT_RGB_NV 0x92A3 | |

LINEARDODGE_NV 0x92A4 | |

LINEARBURN_NV 0x92A5 | |

VIVIDLIGHT_NV 0x92A6 | |

LINEARLIGHT_NV 0x92A7 | |

PINLIGHT_NV 0x92A8 | |

HARDMIX_NV 0x92A9 | |

HSL_HUE_NV 0x92AD | |

HSL_SATURATION_NV 0x92AE | |

HSL_COLOR_NV 0x92AF | |

HSL_LUMINOSITY_NV 0x92B0 | |

PLUS_NV 0x9291 | |

PLUS_CLAMPED_NV 0x92B1 | |

PLUS_CLAMPED_ALPHA_NV 0x92B2 | |

PLUS_DARKER_NV 0x9292 | |

MINUS_NV 0x929F | |

MINUS_CLAMPED_NV 0x92B3 | |

CONTRAST_NV 0x92A1 | |

INVERT_OVG_NV 0x92B4 | |

RED_NV 0x1903 | |

GREEN_NV 0x1904 | |

BLUE_NV 0x1905 | |

NOTE: These enums are not accepted by the <modeRGB> or <modeAlpha> | |

parameters of BlendEquationSeparate or BlendEquationSeparatei. | |

NOTE: The tokens XOR_NV, RED_NV, GREEN_NV, and BLUE_NV have the same | |

values as core OpenGL API enumerants with names without the "_NV" | |

suffixes. Either #define can be used, but the non-suffixed #defines are | |

not available in OpenGL ES 2.0 and XOR is not available in OpenGL ES 3.0. | |

Additions to Chapter 2 of the OpenGL 4.1 Specification (OpenGL Operation) | |

None. | |

Additions to Chapter 3 of the OpenGL 4.1 Specification (Rasterization) | |

None. | |

Additions to Chapter 4 of the OpenGL 4.1 Specification (Per-Fragment | |

Operations and the Frame Buffer) | |

Modify Section 4.1.8, Blending (p. 359). | |

(modify the first paragraph, p. 361, allowing for new values in the | |

<mode> parameter) ... <modeRGB> and <modeAlpha> must be one of | |

FUNC_ADD, FUNC_SUBTRACT, FUNC_REVERSE_SUBTRACT, MIN, or MAX as listed | |

in Table 4.1. <mode> must be one of the values in Table 4.1, | |

or one of the blend equations listed in tables X.2, X.3, and X.4. ... | |

(modify the third paragraph, p. 361, specifying minimum precision and | |

dynamic range for blend operations) ... Blending computations are treated | |

as if carried out in floating-point. For the equations in table 4.1, | |

blending computations will be performed with a precision and dynamic range | |

no lower than that used to represent destination components. For the | |

equations in table X.2, X.3, and X.4, blending computations will be | |

performed with a precision and dynamic range no lower than the smaller of | |

that used to represent destination components or that used to represent | |

16-bit floating-point values as described in section 2.1.1. | |

(add unnumbered subsection prior to "Dual Source Blending and Multiple | |

Draw Buffers", p. 363) | |

Advanced Blend Equations | |

The advanced blend equations are those listed in tables X.2, X.3, and X.4. | |

Parameters related to the advanced blend equations can be set by calling | |

void BlendParameteriNV(enum pname, int param); | |

with <pname> set to BLEND_PREMULTIPLIED_SRC_NV or BLEND_OVERLAP_NV. When | |

<pname> is BLEND_PREMULTIPLIED_SRC_NV, the valid values for <param> are | |

TRUE or FALSE. When <pname> is BLEND_OVERLAP_NV, the valid values for | |

<param> are UNCORRELATED_NV, CONJOINT_NV, and DISJOINT_NV. An | |

INVALID_ENUM error is generated if <pname> is not | |

BLEND_PREMULTIPLIED_SRC_NV or BLEND_OVERLAP_NV, or if <param> is not a | |

legal value for <pname>. | |

When using one of the equations in table X.2 or X.3, blending is performed | |

according to the following equations: | |

R = f(Rs',Rd')*p0(As,Ad) + Y*Rs'*p1(As,Ad) + Z*Rd'*p2(As,Ad) | |

G = f(Gs',Gd')*p0(As,Ad) + Y*Gs'*p1(As,Ad) + Z*Gd'*p2(As,Ad) | |

B = f(Bs',Bd')*p0(As,Ad) + Y*Bs'*p1(As,Ad) + Z*Bd'*p2(As,Ad) | |

A = X*p0(As,Ad) + Y*p1(As,Ad) + Z*p2(As,Ad) | |

where the function f and terms X, Y, and Z are specified in the table. | |

The R, G, and B components of the source color used for blending are | |

derived according to the premultiplied source color blending parameter, | |

which is set by calling BlendParameteriNV with <pname> set to | |

BLEND_PREMULTIPLIED_SRC_NV, and <param> set to TRUE or FALSE. | |

If the parameter is set to TRUE, the fragment color components are | |

considered to have been premultiplied by the A component prior to | |

blending. The base source color (Rs',Gs',Bs') is obtained by dividing | |

through by the A component: | |

(Rs', Gs', Bs') = | |

(0, 0, 0), if As == 0 | |

(Rs/As, Gs/As, Bs/As), otherwise | |

If the premultiplied source color parameter is FALSE, the fragment color | |

components are used as the base color: | |

(Rs', Gs', Bs') = (Rs, Gs, Bs) | |

The destination color components are always considered to have been | |

premultiplied by the destination A component and the base destination | |

color (Rd', Gd', Bd') is obtained by dividing through by the A component: | |

(Rd', Gd', Bd') = | |

(0, 0, 0), if Ad == 0 | |

(Rd/Ad, Gd/Ad, Bd/Ad), otherwise | |

When blending using advanced blend equations, we expect that the R, G, and | |

B components of premultiplied source and destination color inputs be | |

stored as the product of non-premultiplied R, G, and B component values | |

and the A component of the color. If any R, G, or B component of a | |

premultiplied input color is non-zero and the A component is zero, the | |

color is considered ill-formed, and the corresponding component of the | |

blend result will be undefined. | |

The weighting functions p0, p1, and p2 are defined in table X.1. In these | |

functions, the A components of the source and destination colors are taken | |

to indicate the portion of the pixel covered by the fragment (source) and | |

the fragments previously accumulated in the pixel (destination). The | |

functions p0, p1, and p2 approximate the relative portion of the pixel | |

covered by the intersection of the source and destination, covered only by | |

the source, and covered only by the destination, respectively. These | |

functions are specified by the blend overlap parameter, which can be set | |

by calling BlendParameteriNV with <pname> set to BLEND_OVERLAP_NV. <param> | |

can be one of UNCORRELATED_NV (default), CONJOINT_NV, and DISJOINT_NV. | |

UNCORRELATED_NV indicates that there is no correlation between the source | |

and destination coverage. CONJOINT_NV and DISJOINT_NV indicate that the | |

source and destination coverage are considered to have maximal or minimal | |

overlap, respectively. | |

Overlap Mode Weighting Equations | |

--------------- -------------------------- | |

UNCORRELATED_NV p0(As,Ad) = As*Ad | |

p1(As,Ad) = As*(1-Ad) | |

p2(As,Ad) = Ad*(1-As) | |

CONJOINT_NV p0(As,Ad) = min(As,Ad) | |

p1(As,Ad) = max(As-Ad,0) | |

p2(As,Ad) = max(Ad-As,0) | |

DISJOINT_NV p0(As,Ad) = max(As+Ad-1,0) | |

p1(As,Ad) = min(As,1-Ad) | |

p2(As,Ad) = min(Ad,1-As) | |

Table X.1, Advanced Blend Overlap Modes | |

Mode Blend Coefficients | |

-------------------- ----------------------------------- | |

ZERO (X,Y,Z) = (0,0,0) | |

f(Cs,Cd) = 0 | |

SRC_NV (X,Y,Z) = (1,1,0) | |

f(Cs,Cd) = Cs | |

DST_NV (X,Y,Z) = (1,0,1) | |

f(Cs,Cd) = Cd | |

SRC_OVER_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = Cs | |

DST_OVER_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = Cd | |

SRC_IN_NV (X,Y,Z) = (1,0,0) | |

f(Cs,Cd) = Cs | |

DST_IN_NV (X,Y,Z) = (1,0,0) | |

f(Cs,Cd) = Cd | |

SRC_OUT_NV (X,Y,Z) = (0,1,0) | |

f(Cs,Cd) = 0 | |

DST_OUT_NV (X,Y,Z) = (0,0,1) | |

f(Cs,Cd) = 0 | |

SRC_ATOP_NV (X,Y,Z) = (1,0,1) | |

f(Cs,Cd) = Cs | |

DST_ATOP_NV (X,Y,Z) = (1,1,0) | |

f(Cs,Cd) = Cd | |

XOR_NV (X,Y,Z) = (0,1,1) | |

f(Cs,Cd) = 0 | |

MULTIPLY_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = Cs*Cd | |

SCREEN_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = Cs+Cd-Cs*Cd | |

OVERLAY_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = 2*Cs*Cd, if Cd <= 0.5 | |

1-2*(1-Cs)*(1-Cd), otherwise | |

DARKEN_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = min(Cs,Cd) | |

LIGHTEN_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = max(Cs,Cd) | |

COLORDODGE_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = | |

0, if Cd <= 0 | |

min(1,Cd/(1-Cs)), if Cd > 0 and Cs < 1 | |

1, if Cd > 0 and Cs >= 1 | |

COLORBURN_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = | |

1, if Cd >= 1 | |

1 - min(1,(1-Cd)/Cs), if Cd < 1 and Cs > 0 | |

0, if Cd < 1 and Cs <= 0 | |

HARDLIGHT_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = 2*Cs*Cd, if Cs <= 0.5 | |

1-2*(1-Cs)*(1-Cd), otherwise | |

SOFTLIGHT_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = | |

Cd-(1-2*Cs)*Cd*(1-Cd), | |

if Cs <= 0.5 | |

Cd+(2*Cs-1)*Cd*((16*Cd-12)*Cd+3), | |

if Cs > 0.5 and Cd <= 0.25 | |

Cd+(2*Cs-1)*(sqrt(Cd)-Cd), | |

if Cs > 0.5 and Cd > 0.25 | |

DIFFERENCE_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = abs(Cd-Cs) | |

EXCLUSION_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = Cs+Cd-2*Cs*Cd | |

INVERT (X,Y,Z) = (1,0,1) | |

f(Cs,Cd) = 1-Cd | |

INVERT_RGB_NV (X,Y,Z) = (1,0,1) | |

f(Cs,Cd) = Cs*(1-Cd) | |

LINEARDODGE_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = | |

Cs+Cd, if Cs+Cd<=1 | |

1, otherwise | |

LINEARBURN_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = | |

Cs+Cd-1, if Cs+Cd>1 | |

0, otherwise | |

VIVIDLIGHT_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = | |

1-min(1,(1-Cd)/(2*Cs)), if 0 < Cs < 0.5 | |

0, if Cs <= 0 | |

min(1,Cd/(2*(1-Cs))), if 0.5 <= Cs < 1 | |

1, if Cs >= 1 | |

LINEARLIGHT_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = | |

1, if 2*Cs+Cd>2 | |

2*Cs+Cd-1, if 1 < 2*Cs+Cd <= 2 | |

0, if 2*Cs+Cd<=1 | |

PINLIGHT_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = | |

0, if 2*Cs-1>Cd and Cs<0.5 | |

2*Cs-1, if 2*Cs-1>Cd and Cs>=0.5 | |

2*Cs, if 2*Cs-1<=Cd and Cs<0.5*Cd | |

Cd, if 2*Cs-1<=Cd and Cs>=0.5*Cd | |

??? | |

HARDMIX_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = 0, if Cs+Cd<1 | |

1, otherwise | |

Table X.2, Advanced Blend Equations | |

When using one of the HSL blend equations in table X.3 as the blend | |

equation, the RGB color components produced by the function f() are | |

effectively obtained by converting both the non-premultiplied source and | |

destination colors to the HSL (hue, saturation, luminosity) color space, | |

generating a new HSL color by selecting H, S, and L components from the | |

source or destination according to the blend equation, and then converting | |

the result back to RGB. In the equations below, a blended RGB color is | |

produced according to the following pseudocode: | |

float minv3(vec3 c) { | |

return min(min(c.r, c.g), c.b); | |

} | |

float maxv3(vec3 c) { | |

return max(max(c.r, c.g), c.b); | |

} | |

float lumv3(vec3 c) { | |

return dot(c, vec3(0.30, 0.59, 0.11)); | |

} | |

float satv3(vec3 c) { | |

return maxv3(c) - minv3(c); | |

} | |

// If any color components are outside [0,1], adjust the color to | |

// get the components in range. | |

vec3 ClipColor(vec3 color) { | |

float lum = lumv3(color); | |

float mincol = minv3(color); | |

float maxcol = maxv3(color); | |

if (mincol < 0.0) { | |

color = lum + ((color-lum)*lum) / (lum-mincol); | |

} | |

if (maxcol > 1.0) { | |

color = lum + ((color-lum)*lum) / (maxcol-lum); | |

} | |

return color; | |

} | |

// Take the base RGB color <cbase> and override its luminosity | |

// with that of the RGB color <clum>. | |

vec3 SetLum(vec3 cbase, vec3 clum) { | |

float lbase = lumv3(cbase); | |

float llum = lumv3(clum); | |

float ldiff = llum - lbase; | |

vec3 color = cbase + vec3(ldiff); | |

return ClipColor(color); | |

} | |

// Take the base RGB color <cbase> and override its saturation with | |

// that of the RGB color <csat>. The override the luminosity of the | |

// result with that of the RGB color <clum>. | |

vec3 SetLumSat(vec3 cbase, vec3 csat, vec3 clum) | |

{ | |

float minbase = minv3(cbase); | |

float sbase = satv3(cbase); | |

float ssat = satv3(csat); | |

vec3 color; | |

if (sbase > 0) { | |

// Equivalent (modulo rounding errors) to setting the | |

// smallest (R,G,B) component to 0, the largest to <ssat>, | |

// and interpolating the "middle" component based on its | |

// original value relative to the smallest/largest. | |

color = (cbase - minbase) * ssat / sbase; | |

} else { | |

color = vec3(0.0); | |

} | |

return SetLum(color, clum); | |

} | |

Mode Result | |

-------------------- ---------------------------------------- | |

HSL_HUE_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = SetLumSat(Cs,Cd,Cd); | |

HSL_SATURATION_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = SetLumSat(Cd,Cs,Cd); | |

HSL_COLOR_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = SetLum(Cs,Cd); | |

HSL_LUMINOSITY_NV (X,Y,Z) = (1,1,1) | |

f(Cs,Cd) = SetLum(Cd,Cs); | |

Table X.3, Hue-Saturation-Luminosity Advanced Blend Equations | |

When using one of the equations in table X.4 as the blend equation, the | |

source color used by these blending equations is interpreted according to | |

the BLEND_PREMULTIPLIED_SRC_NV blending parameter. The blending equations | |

below are evaluated where the RGB source and destination color components | |

are both considered to have been premultiplied by the corresponding A | |

component. | |

(Rs', Gs', Bs') = | |

(Rs, Gs, Bs), if BLEND_PREMULTIPLIED_SRC_NV is TRUE | |

(Rs*As, Gs*As, Bs*As), if BLEND_PREMULTIPLIED_SRC_NV is FALSE | |

Mode Result | |

-------------------- ---------------------------------------- | |

PLUS_NV (R,G,B,A) = (Rs'+Rd, Gs'+Gd, Bs'+Bd,As'+Ad) | |

PLUS_CLAMPED_NV (R,G,B,A) = | |

(min(1,Rs'+Rd), min(1,Gs'+Gd), | |

min(1,Bs'+Bd), min(1,As+Ad)) | |

PLUS_CLAMPED_ALPHA_NV (R,G,B,A) = | |

(min(min(1,As+Ad),Rs'+Rd), | |

min(min(1,As+Ad),Gs'+Gd), | |

min(min(1,As+Ad),Bs'+Bd), min(1,As+Ad)) | |

PLUS_DARKER_NV (R,G,B,A) = | |

(max(0,min(1,As+Ad)-((As-Rs')+(Ad-Rd))), | |

max(0,min(1,As+Ad)-((As-Gs')+(Ad-Gd))), | |

max(0,min(1,As+Ad)-((As-Bs')+(Ad-Bd))), | |

min(1,As+Ad)) | |

MINUS_NV (R,G,B,A) = (Rd-Rs', Gd-Gs', Bd-Bs', Ad-As) | |

MINUS_CLAMPED_NV (R,G,B,A) = | |

(max(0,Rd-Rs'), max(0,Gd-Gs'), | |

max(0,Bd-Bs'), max(0,Ad-As)) | |

CONTRAST_NV (R,G,B,A) = | |

(Ad/2 + 2*(Rd-Ad/2)*(Rs'-As/2), | |

Ad/2 + 2*(Gd-Ad/2)*(Gs'-As/2), | |

Ad/2 + 2*(Bd-Ad/2)*(Bs'-As/2), | |

Ad) | |

INVERT_OVG_NV (R,G,B,A) = | |

(As*(1-Rd)+(1-As)*Rd, | |

As*(1-Gd)+(1-As)*Gd, | |

As*(1-Bd)+(1-As)*Bd, | |

As+Ad-As*Ad) | |

RED_NV (R,G,B,A) = (Rs', Gd, Bd, Ad) | |

GREEN_NV (R,G,B,A) = (Rd, Gs', Bd, Ad) | |

BLUE_NV (R,G,B,A) = (Rd, Gd, Bs', Ad) | |

Table X.4, Additional RGB Blend Equations | |

Advanced blending equations are supported only when rendering to a single | |

color buffer using fragment color zero. If any non-NONE draw buffer uses | |

a blend equation found in table X.2, X.3, or X.4, the error | |

INVALID_OPERATION is generated by [[Compatibility Profile: Begin or any | |

operation that implicitly calls Begin (such as DrawElements)]] [[Core | |

Profile: DrawArrays and the other drawing commands defined in section | |

2.8.3]] if: | |

* the draw buffer for color output zero selects multiple color buffers | |

(e.g., FRONT_AND_BACK in the default framebuffer); or | |

* the draw buffer for any other color output is not NONE. | |

[[ The following paragraph applies to NV_blend_equation_advanced only. ]] | |

When using advanced blending equations, applications should split their | |

rendering into a collection of blending passes, none of which touch an | |

individual sample more than once. The results of blending are undefined | |

if the sample being blended has been touched previously in the same pass. | |

The command | |

void BlendBarrierNV(void); | |

specifies a boundary between passes when using advanced blend equations. | |

Any command that causes the value of a sample to be modified is considered | |

to touch the sample, including clears, blended or unblended primitives, | |

BlitFramebuffer copies, and direct updates by commands such as | |

TexSubImage2D. | |

[[ The following paragraph applies to NV_blend_equation_advanced_coherent | |

only. ]] | |

When using advanced blending equations, blending is typically done | |

coherently and in primitive order. When an individual sample is covered | |

by multiple primitives, blending for that sample is performed sequentially | |

in the order in which the primitives were submitted. This coherent | |

blending is enabled by default, but can be enabled or disabled by calling | |

Enable or Disable with the symbolic constant BLEND_ADVANCED_COHERENT_NV. | |

If coherent blending is disabled, applications should split their | |

rendering into a collection of blending passes, none of which touch an | |

individual sample more than once. When coherent blending is disabled, the | |

results of blending are undefined if the sample being blended has been | |

touched previously in the same pass. The command | |

void BlendBarrierNV(void); | |

specifies a boundary between passes when using advanced blend equations. | |

Any command that causes the value of a sample to be modified is considered | |

to touch the sample, including clears, blended or unblended primitives, | |

BlitFramebuffer copies, and direct updates by commands such as | |

TexSubImage2D. | |

Additions to Chapter 5 of the OpenGL 4.1 Specification (Special Functions) | |

None. | |

Additions to Chapter 6 of the OpenGL 4.1 Specification (State and | |

State Requests) | |

None. | |

Additions to Appendix A of the OpenGL 4.1 Specification (Invariance) | |

None. | |

Additions to the AGL/GLX/WGL/EGL Specifications | |

None. | |

GLX Protocol | |

!!! TBD | |

Dependencies on OpenGL 4.0 | |

If OpenGL 4.0 is not supported, references to the BlendEquationi API should | |

be removed. | |

Dependencies on OpenGL 4.1 (Core Profile) | |

This extension throws an INVALID_OPERATION when Begin is called if advanced | |

blend equations are used in conjunction with multiple draw buffers. For | |

the core profile of OpenGL 4.1 (and other versions of OpenGL), there is no | |

Begin command; instead, the error is thrown by other rendering commands | |

such as DrawArrays. The language in this specification documenting the | |

error has separate versions for the core and compatibility profiles. | |

Dependencies on OpenGL 4.3 or later (any Profile) | |

References to Chapter 4 are replaced with references to Chapter 17 (Writing | |

Fragments and Samples to the Framebuffer). | |

References to section 4.1.8 are replaced with references to section 17.3.8. | |

References to Table 4.1 are replace with references to Table 17.1. | |

References to section 2.1.1 are replaced with references to section 2.3.3. | |

Dependencies on OpenGL ES 2.0 | |

If unextended OpenGL ES 2.0 is supported, references to BlendEquationi, | |

BlendEquationSeparatei, GetInteger64v, and GetDoublev should be ignored. | |

Ignore any references to multiple draw buffers if EXT_draw_buffers or | |

NV_draw_buffers is not supported. | |

Dependencies on EXT_blend_minmax | |

Requires EXT_blend_minmax on OpenGL ES 2.0 implementations and references | |

to MIN and MAX should be replace by references to MIN_EXT and MAX_EXT as | |

introduced by that extension. | |

Dependencies on OpenGL ES 3.0 | |

If unextended OpenGL ES 3.0 is supported, references to BlendEquationi, | |

BlendEquationSeparatei, and GetDoublev should be ignored. | |

Dependencies on NV_path_rendering | |

When NV_path_rendering is supported, covering geometry generated by the | |

commands CoverFillPathNV, CoverFillPathInstancedNV, CoverStrokePathNV, and | |

CoverStrokePathInstancedNV will automatically be blended coherently | |

relative to previous geometry when using the blend equations in this | |

extension. This guarantee is provided even on implementations supporting | |

only NV_blend_equation_advanced. | |

Insert the following language after the discussion of the BlendBarrierNV() | |

command for both extensions: | |

[[ For NV_blend_equation_advanced only: ]] | |

The commands CoverFillPathNV, CoverFillPathInstancedNV, | |

CoverStrokePathNV, and CoverStrokePathInstancedNV are considered to | |

start a new blending pass, as though BlendBarrierNV were called prior to | |

the cover operation. If a cover primitive is followed by subsequent | |

non-cover primitives using advanced blend equations and touching the | |

same samples, applications must call BlendBarrierNV after the cover | |

primitives to ensure defined blending results. | |

[[ For NV_blend_equation_advanced_coherent, the language immediately | |

above should be used, but the first sentence should be prefixed with | |

"When coherent blending is disabled, ...". ]] | |

Errors | |

If any non-NONE draw buffer uses a blend equation found in table X.2, X.3, | |

or X.4, the error INVALID_OPERATION is generated by Begin or any operation | |

that implicitly calls Begin (such as DrawElements) if: | |

* the draw buffer for color output zero selects multiple color buffers | |

(e.g., FRONT_AND_BACK in the default framebuffer); or | |

* the draw buffer for any other color output is not NONE. | |

If BlendParameteriNV is called and <pname> is not | |

BLEND_PREMULTIPLIED_SRC_NV or BLEND_OVERLAP_NV the error INVALID_ENUM is | |

generated. | |

If BlendParameteriNV is called with <pname> set to | |

BLEND_PREMULTIPLIED_SRC_NV the error INVALID_ENUM is generated if <param> | |

is not TRUE or FALSE. | |

If BlendParameteriNV is called with <pname> set to BLEND_OVERLAP_NV the | |

error INVALID_ENUM is generated if <param> is not one of UNCORRELATED_NV, | |

DISJOINT_NV, or CONJOINT_NV. | |

New State | |

Initial | |

Get Value Type Get Command Value Description Sec Attribute | |

-------------------- ---- ------------ ------------ ------------------------ ----- ------------ | |

BLEND_ADVANCED_ B IsEnabled TRUE are advanced blending 4.1.8 color-buffer | |

COHERENT_NV equations guaranteed to | |

be evaluated coherently? | |

BLEND_PREMULTIPLIED_ B GetBooleanv TRUE use premultiplied src 4.1.8 color-buffer | |

SRC_NV colors with advanced | |

blend equations | |

BLEND_OVERLAP_NV Z3 GetIntegerv UNCORRELATED correlation of src/dst 4.1.8 color-buffer | |

_NV coverage within a pixel | |

Note: The BLEND_ADVANCED_COHERENT_NV enable is provided if and only if | |

the NV_blend_equation_advanced_coherent extension is supported. On | |

implementations supporting only NV_blend_equation_advanced, this enable is | |

considered not to exist. | |

New Implementation Dependent State | |

None. | |

NVIDIA Implementation Details | |

Older versions of this extension specification and early shipping | |

implementations supported the COLORDODGE_NV and COLORBURN_NV equations | |

without the special case discussed in issue (34). This should be fixed for | |

newer driver releases. | |

Issues | |

(1) How should these new blending operations be supported? | |

RESOLVED: Provide a separate blend equation for each of the various | |

blending operations. | |

(2) Many of these blending operations involve complicated computations on | |

the RGB color components, but corresponding alpha operations are | |

typically very simple. How should blending on the alpha channel work? | |

RESOLVED: Each new blend equation provides one equation for color and | |

another for alpha. In this extension, separate advanced blend equations | |

for color and alpha are not supported; BlendEquationSeparate does not | |

accept these enums. | |

This extension contemplated separate blend equations for RGB and alpha, | |

perhaps with only basic equations for alpha, but we chose to tie RGB and | |

alpha blending together for simplicity. | |

(3) Should we provide explicit support for premultiplied colors? | |

RESOLVED: Yes. Many of the imaging APIs supporting similar blend | |

equations use premultiplied colors, some exclusively. Additionally, | |

many equations are simpler to express and compute with premultiplied | |

colors. | |

In this extension, we choose to treat the destination colors and the | |

blend result as premultiplied. We considered providing a blend | |

parameter supporting non-premultiplied destinations, but chose to | |

support only premultiplied destinations for mathematical simplicity. | |

(4) Should we support blending where some, but not all, colors are | |

premultiplied? For example, there may be cases where the source | |

fragment colors are not premultiplied, but where the destination | |

colors are premultiplied. | |

RESOLVED: Yes, we will provide support for non-premultiplied fragment | |

colors (via a blending parameter), in which case the RGB color | |

components are multiplied by alpha prior to blending. | |

We considered requiring premultiplication in the fragment shader, but | |

opted to provide a fixed-function premultiply operation for cases where | |

it was inconvenient to modify the fragment shader to perform the | |

multiplication, or where no fragment shader is executed (e.g., | |

fixed-function fragment processing, blits via the NV_draw_texture | |

extension). | |

(5) Should we support different types of correlation between source and | |

destination coverage in partially covered pixels? If so, how? | |

RESOLVED: We will provide a blend parameter allowing for multiple | |

versions of many blending equations based on the "correlation" between | |

source and destination coverage. For pixels with partial opacity, there | |

might be three different blend cases: (a) where the portions of the | |

pixel covered by the primitives are considered to have minimal overlap | |

(e.g., abutting primitives in a mesh), (b) where the portions of the | |

pixel covered by the primitives are considered to have maximal overlap | |

(e.g., overlapping geometry), (c) where the portions of the pixel | |

covered by the primitives are considered uncorrelated. | |

(6) Should we support swapping source and destination coverage in advanced | |

blends? If so, how? | |

RESOLVED: In the current version, we don't support fully general | |

swapping. We do provide several pairs of blend equations that are | |

equivalent, other than swapping source and destination colors. For | |

example, we provide complementary blend equations SRC_OVER_NV, where the | |

source color is considered to be "over" the destination, and | |

DST_OVER_NV, where the destination color is considered to be "over the | |

source. Having pairs of equations such as "SRC_OVER" and "DST_OVER" | |

seems to be common practice in various imaging APIs. | |

Alternately, we could provide a blend parameter that simply swaps source | |

and destination for arbitrary blend equations. In the example above, we | |

could provide a single blend equation OVER_NV, where the source color is | |

considered "over" when unswapped and the destination color is considered | |

"over" when swapped. | |

(7) Should we generalize the blending operation, replacing the notions of | |

"source" and "destination" colors with more generic "A" and "B" | |

parameters, which might be obtained from a variety of sources | |

(fragment color, one of <N> color attachment points, some additional | |

source of textures/images)? | |

RESOLVED: Not in this extension; the only blending operation we support | |

takes a fragment color (which could be obtained from an arbitrary | |

source, either through a fragment shader, fixed function fragment | |

processing, or an imaging API such as NV_draw_texture) and a destination | |

color, performs a blend, and stores the result in the buffer from which | |

the destination color was extracted. | |

(8) How should we expose the various combinations of blending modes? | |

RESOLVED: The base blending equation is specified by the same | |

BlendEquation() API supported for regular OpenGL blending. Additional | |

parameters (such as pre-multiplied source colors, overlap mode, source | |

destination swapping, input selection) can be specified via the | |

BlendParameteriNV() API. | |

We could provide for a "general" blend equation API specifying multiple | |

parameters at once, such as: | |

void BlendEquationGeneralNV(enum blend, enum overlap, | |

boolean swapSrcDst); | |

but that API would require applications to pass parameters that are | |

always the same (e.g., overlap as UNCORRELATED_NV) and wouldn't be | |

easily extensible. Note that there are several features that we've | |

chosen not to include but might be usefully added as blend parameters in | |

the future -- see issues (3), (6), and (7), for example. | |

(9) What limitations apply to the new blend modes? | |

RESOLVED: In the current implementation, these blend equations are not | |

supported with more than one color buffer; if this is attempted, a | |

draw-time error is generated. This limitation is similar in nature to | |

one for dual-source blending, which implementations are not required to | |

support in conjunction with multiple color buffers. This limitation may | |

be relaxed in a future version of this extension. | |

(10) What precision is used in the computations for these blending | |

equations? | |

RESOLVED: There are no minimum precision requirements specified in | |

OpenGL 4.1, though one would expect implementations to blend with at | |

least the precision used to store destination color components. This | |

extension provides this as a minimum baseline for existing blending | |

equations. | |

For the new equations, we specify a minimum precision that is the | |

smaller of the precision of the destination buffer or the precision of | |

16-bit floating-point computations. For most formats, this meets the | |

limit for basic blend equations. However, there may be precision loss | |

if these new blending equations are used with 12-bit unsigned normalized | |

components, 16-bit unsigned or signed normalized components, or 32-bit | |

floating-point components. | |

This restriction is specified so that implementations are not required | |

to support the large number of blending equations specified here with | |

full 32-bit floating-point computations. | |

(11) When targeting a fixed-point buffer, are input color components | |

clamped to [-1,1] for signed normalized color buffers or to [0,1] for | |

unsigned normalized color buffers? | |

RESOLVED: We will use the same clamping behavior as for basic blend | |

equations, where fragment color components are clamped to [0,1] prior to | |

blending for unsigned normalized color targets. | |

Note that the OpenGL 4.1 specification, against which this spec is | |

written, had an oversight related signed normalized color buffers. It | |

specifies [0,1] clamping for all "fixed point" targets, which is clearly | |

not desired for signed normalized color buffers. Fragment shader color | |

outputs should be clamped to [-1,+1] in this case; this was fixed in | |

OpenGL 4.2 (bug 6849). | |

(12) What happens when converting a premultiplied color with an alpha of | |

zero to a non-premultiplied color? | |

RESOLVED: We specify that a premultiplied color of (0,0,0,0) should | |

produce non-premultiplied (R,G,B) values of (0,0,0). A premultiplied | |

color with an alpha of zero and a non-zero R, G, or B component is | |

considered to be ill-formed and will produce undefined blending results. | |

For a non-premultiplied color (R',G',B',A'), the corresponding | |

premultiplied color (R,G,B,A) should satisfy the equation: | |

(R,G,B,A) = (R'*A', G'*A', B'*A', A') | |

If the alpha of a non-premultiplied color is zero, the corresponding | |

premultiplied color (R,G,B,A) should be (0,0,0,0). | |

We specify that ill-formed premultiplied colors produce undefined | |

blending results to enable certain performance optimizations. In many | |

of these blending equations, the alpha component used as a denominator | |

to compute the non-premultiplied color ends up being multiplied by the | |

same alpha component in the coverage, resulting in cancellation. For | |

example, implementations may want to substitute a premultiplied | |

destination color into the last term of the basic blend equation: | |

R = f(Rs',Rd')*p0(As,Ad) + Y*Rs'*p1(As,Ad) + Z*Rd'*p2(As,Ad) | |

= ... + Z*Rd'*(Ad*(1-As)) | |

= ... + Z*(Rd'*Ad)*(1-As) | |

= ... + Z* Rd * (1-As) | |

This substitution would be invalid for ill-formed premultiplied | |

destination colors. We choose to specify undefined results for invalid | |

input colors rather than requiring implementations to skip such | |

optimizations or include logic to check for zero alpha values for each | |

input. | |

(13) Should we provide "advanced" RGB blend equations for modes commonly | |

used in dedicated imaging APIs that can already be expressed with | |

current OpenGL blending modes? | |

RESOLVED: Yes; we will provide a number of "advanced" blend equations | |

for basic computations that can be done with existing blend equations. | |

This allows applications wanting to use these advanced modes to use them | |

exclusively, without having to figure out which blends are not supported | |

and generate separate BlendEquation/BlendFunc state for each. | |

(14) How do the advanced RGB blend equations interact with sRGB color | |

buffers? In particular, how does it interact with storing | |

premultiplied color values in the framebuffer? | |

RESOLVED: When targeting an sRGB color buffer with the blend equations | |

in this extension, we will convert the destination colors from sRGB to | |

linear and will convert the linear blend result back to sRGB when | |

writing it to the framebuffer. This approach is no different from | |

regular blends. | |

sRGB conversions will affect premultiplied colors differently than | |

non-premultiplied colors since: | |

linear_to_srgb(C*A) != A * linear_to_srgb(C) | |

When storing an sRGB-encoded value into an sRGB texture or renderbuffer, | |

we expect that the values will be extracted as sRGB colors in a | |

subsequent texturing, blending, or display operation. The fetched sRGB | |

color components will be converted back to linear. Except from rounding | |

errors in converting the color components to fixed-point, converting to | |

and from sRGB will not modify the color, with or without | |

premultiplication. | |

(15) The HSL blend equations are "color surgery" operations where | |

components from the source and destination colors are mixed. Are | |

there any problems using these equations with premultiplied color | |

components? | |

RESOLVED: Like all of the "f/X/Y/Z" blends, the function f() in HSL | |

blend equations are expressed in terms of non-premultiplied colors, | |

which implies a division operation prior to evaluating f(). However, it | |

may be possible to perform some or all of the blending operation using | |

pre-multiplied colors directly. In particular, the luminosity and | |

saturation of a color with components scaled by alpha is equal to alpha | |

times the luminosity or saturation of the un-scaled color: | |

lumv3(C*A) = A * lumv3(C) | |

satv3(C*A) = A * satv3(C) | |

(16) How should we express the blending equations? | |

RESOLVED: In general, we will use the formulation found in the PDF and | |

SVG specifications, which define each blend in terms of four parameters: | |

* a function f(Cs,Cd) specifies the blended color contribution in the | |

portion of the pixel containing both the source and destination; | |

* a constant X specifies whether the region containing both the source | |

and destination contributes to the final alpha; | |

* a constant Y specifies whether the region containing only the source | |

contributes to the final color and alpha; and | |

* a constant Z specifies whether the region containing only the | |

destination contributes to the final color and alpha. | |

This formulation is relatively compact and nicely illustrates the | |

contributions from the three relevant combinations of source and | |

destination coverage; the portion of the pixel covered by neither the | |

source nor the destination contributes nothing to color or alpha. | |

Additionally, we specify three functions p0(As,Ad), p1(As,Ad), and | |

p2(As,Ad) specifying the relative portion of the pixel covered by both | |

the source and destination, just the source, and just the destination, | |

respectively. These functions are defined according to the overlap | |

blend parameter; the most common mode (UNCORRELATED) defines: | |

p0(As,Ad) = As*Ad | |

p1(As,Ad) = As*(1-Ad) | |

p2(As,Ad) = Ad*(1-As) | |

There are certain special-purpose blending equations that don't fit | |

this general model (modes that mix RGB or HSL components from the | |

source and destination). These blends don't fit nicely into the | |

mathematical formulas above and are instead defined separately as a | |

component-by-component operation. | |

(17) How should we express the equations for the HSL blend equations? | |

RESOLVED: The equations used by this specification are loosely adapted | |

from similar code in the version 1.7 of the PDF (Portable Document | |

Format) specification. The equations have been modified to use | |

GLSL-style "vec3" syntax. Additionally, they use vector math in the | |

pseudocode overriding the saturation of a base color instead of using | |

"C_min", "C_mid", and "C_max" syntax effectively defining references to | |

the three components of the base color. | |

Alternately, we could have specified functions for converting (R,G,B) | |

colors to and from an (H,S,L) color space. But we decided not to do | |

that because actual (H,S,L) colors are never used in the pipeline. | |

(18) What issues apply to the PLUS and MINUS equations? | |

RESOLVED: The PLUS and MINUS equations provide arithmetically simple | |

operations; we simply perform a component-wise add or subtract | |

operations. The most interesting question is how and where clamping is | |

performed. The original Porter-Duff compositing specification provided | |

a "plus" equation intended to support blending between two images, | |

effectively performing: | |

weight * image1 + (1-weight) * image2 | |

If the components of <image1>, <image2>, and <weight> are all in [0,1], | |

there is no need for clamping. However, in a general add with no | |

<weight> built in, there is no guarantee that adding components of two | |

images will remain inside the range [0,1]. When using fixed-point | |

unsigned normalized color buffers, the sum will automatically be clamped | |

to [0,1] when stored in the framebuffer. However, there may be cases | |

with floating-point color buffers where not clamping the sum also makes | |

sense. | |

Additionally, when storing premultiplied colors, it may also be | |

desirable to clamp R/G/B components to the range [0,A]. Premultiplied | |

colors effectively store "R*A" in the R channel, where "R" is the | |

non-premultiplied color and A is alpha. Clamping this value to A | |

ensures that the non-premultiplied form of R is in [0,1]. | |

To handle all possible cases, we provide five "PLUS" and "MINUS" | |

equations. | |

PLUS_NV: Add color and alpha components without clamping. | |

PLUS_CLAMPED_NV: Add color and alpha components; clamp each sum to | |

1.0. | |

PLUS_CLAMPED_ALPHA_NV: Add color and alpha components. Clamp the | |

alpha sum to 1.0; clamp the color sums to the alpha result (i.e., the | |

clamped alpha sum). Note that if premultiplied inputs are clamped | |

properly where 0<=R,G,B<=A, this equation isn't needed since the color | |

sums will always be less than the alpha sum. | |

MINUS_NV: Subtract the source color and alpha components from the | |

destination without clamping. | |

MINUS_CLAMPED_NV: Subtract the source color and alpha components from | |

the destination; clamp the difference to 0.0. | |

We don't bother clamping in "unexpected" direction. We don't bother | |

clamping sums to be greater than or equal to zero or differences to be | |

less than or equal to one; either case would require an unclamped input | |

with a negative component. | |

Note that when blending to an unsigned fixed-point buffer, the clamped | |

and non-clamped versions of "PLUS" and "MINUS" produce the same results, | |

since inputs and outputs are both clamped to [0,1]. | |

Note that the LINEARDODGE_NV equation is another form of "PLUS"; in the | |

area of intersection, the source and destination colors are added and | |

clamped to 1.0. | |

(19) Should we provide a blend parameter to clamp the destination color | |

(when read) to [0,1]? What about clamping premultiplied RGB | |

components to [0,a]? | |

RESOLVED: No. We expect the most common use case to involve unsigned | |

normalized color buffers, where components will automatically be clamped | |

to [0,1] by virtue of how they're stored in the framebuffer. It doesn't | |

seem worth the trouble to add a clamp-on-read feature to clamp to [0,a] | |

when it seems easy enough to program colors to stay in range. | |

(20) Should we provide a blend parameter to clamp final color or alpha | |

output components to [0,1]? What about clamping premultiplied RGB | |

outputs to [0,a]? | |

RESOLVED: As above, when writing the blend results to unsigned | |

normalized targets, output components will automatically be clamped to | |

[0,1] by virtue of how they're stored in the framebuffer. It doesn't | |

seem worth the trouble to clamp to [0,a], either. Most of the blend | |

equations supported by this extension will produce outputs with | |

premultiplied color component values in the range [0,a] as long as the | |

inputs also have that property. One exception is PLUS_NV, but we | |

explicitly provide a PLUS_CLAMPED_ALPHA_NV equation to for that case. | |

(21) Should we provide an equation like the VG_BLEND_SOFTLIGHT_SVG_KHR | |

blending equation in the KHR_advanced_blending extension to OpenVG? | |

RESOLVED: No. The KHR_advanced_blending appears to have specified a | |

equation implementing the "soft-light" compositing property in a working | |

draft of a SVG 1.2 specification, as described here: | |

http://www.w3.org/TR/2004/WD-SVG12-20041027/ | |

rendering.html#compositing | |

This version of the specification appears to have been abandoned. The | |

equations for the "soft-light" property in the SVG Compositing | |

Specification at: | |

http://www.w3.org/TR/SVGCompositing/ | |

match the SOFTLIGHT_NV equation provided by this extension and | |

VG_BLEND_SOFTLIGHT_KHR (no "SVG") in KHR_advanced_blending. | |

Additionally, the equations in the SVG 1.2 draft and the | |

KHR_advanced_blending extension both appear to contain clear errors in | |

the first and second cases. Both begin with "(cd*(as-(1-cd/ad)*..." in | |

the KHR spec but should be "(cd*(as+(1-cd/ad)*...". Both of these sign | |

errors are corrected in the "SVG" functions in this extension. With the | |

errors, there is a local minimum at Cs=0.5 (where we switch from the | |

first form to the second or third) and the function has a major | |

discontinuity at Cd=0.125 when Cs>0.5 (where we switch from the second | |

form to the third). For example, when Cs=0.8 and Cd=0.125, the second | |

form of the KHR extension would generate a result of -0.00625 and the | |

third form would generate a result of ~0.26213. Note that the corrected | |

equations still aren't continuous at Cd=0.125; the fixed second and | |

third forms generate 0.25625 and 0.26213, respectively, when Cs=0.8 and | |

Cd=0.125. | |

(22) What issues apply to the INVERT and INVERT_OVG_NV equations? | |

RESOLVED: The INVERT and INVERT_OVG_NV equations were included to | |

provide functionality similar to the same VG_BLEND_INVERT_KHR blend | |

equation provided by the KHR_advanced_blending extension to OpenVG and | |

similar equations in a few other compositing APIs/standards. | |

Unfortunately, the equation specified by the KHR extension has issues. | |

The apparent intent of this blend equation is to use the source alpha to | |

blend between the destination color and an inverted form of the | |

destination color. This description conceptually matches the | |

description in the KHR extension: | |

(1 - asrc) * c'dst + asrc * (1 - c'dst) | |

However, since source and destination colors are premultiplied, the | |

expression "1-c'dst" doesn't correctly invert the destination color. To | |

invert a premultiplied destination color, "adst-c'dst" should be used. | |

For example, if the premultiplied destination color is 50% gray and 50% | |

opaque (adst=0.5), the RGBA destination color will be | |

(0.25,0.25,0.25,0.5). Inverting the color components via "1-c'dst" | |

would yield RGB component values of 0.75, which isn't consistent with an | |

alpha of 0.5. Inverting via "adst-c'dst" would yield correct RGB | |

component values of 0.25. | |

Additionally, the alpha computed for this equation in the KHR extension | |

is the standard "asrc+adst*(1-asrc)", equivalent to X=Y=Z=1 in our | |

normal formulation. However, given that the source color doesn't | |

contribute at all, having "Y=1" doesn't make a whole lot of sense. The | |

INVERT equation used in this extension uses X=Z=1 and Y=0, which means | |

that blending with this equation never changes destination alpha. | |

We provide a separate blend equation INVERT_OVG_NV to provide | |

compatibility with the formulation in the KHR extension. The math in | |

the KHR extension does perform a "valid" blending operation -- it will | |

produce results that remain in [0,1] when inputs are in [0,1], and its | |

results are continuous. It can't be expressed directly via our f/X/Y/Z | |

parameterization, but it does match our general f/X/Y/Z model if you | |

consider all three areas to contribute where: | |

* the intersection area contributes the inverted destination color | |

* the destination-only area contributes the destination color | |

* the source-only area contributes full white | |

Note that INVERT and INVERT_OVG_NV equations are mathematically | |

equivalent when the destination is opaque (i.e., adst=1.0); in this | |

case, "1-c'dst" and "adst-c'dst" are equivalent. In our f/X/Y/Z model, | |

the full destination coverage means there is no "source-only" area in | |

this case. | |

(23) What issues apply to the PLUS_DARKER_NV blend equation? | |

RESOLVED: The PLUS_DARKER_NV equation corresponds to an equation | |

provided in the Quartz 2D API from Apple. The public documentation for | |

this equation specifies the color computed by this operation as: | |

R = MAX(0, 1 - ((1 - D) + (1 - S))) | |

This equation appears to want to invert the source and destination | |

colors, add the two inverted colors, and then invert the result. | |

However, this equation appears to assume opaque source and destination | |

colors. As noted in the discussion for INVERT_OVG_NV, inverting a color | |

via "1-C" doesn't make any sense. We've reformulated the equations to | |

use pre-multipled colors and invert with "A-C" in a manner similar to | |

that described in this email thread: | |

http://www.mail-archive.com/whatwg@lists.whatwg.org/msg06536.html | |

which appears to be the "darker" mode implemented in the Safari browser | |

(at least in 2007). Our formulation is equivalent to the one in the | |

Quartz 2D documentation when As=Ad=1. | |

(24) Should we apply the f/X/Y/Z formulation to blend equations where the | |

equations can be expressed this way only if one or more of X, Y, or | |

Z are neither zero nor one? | |

RESOLVED: No. The f/X/Y/Z model subdivides a pixel into four regions | |

based on the alpha of the source and the destination, three of which | |

(intersection, source only, destination only) either contribute or don't | |

contribute color and coverage based on the given blend equation. The | |

figure below depicts a pixel where the source and destination both have | |

coverage of 0.5 (50%). The picture assigns source coverage to the upper | |

left portion of the pixel and the destination coverage to the upper | |

right, and assumes an UNCORRELATED model. In this case, the pixel is | |

divided into four areas of equal size. The area of intersection is at | |

the top, and its color and coverage are controlled by the f() and X | |

parameters. The source-only and destination-only regions are on the | |

left and right, respectively, and color and coverage are both controlled | |

by the Y and Z parameters. | |

+-----------+ | |

|\_ f/X _/| | |

source | \_ _/ | destination | |

(upper ==>| Y \_/ Z | (upper | |

left) | _/ \_ | <== right) | |

| _/ \_ | | |

|/ \| | |

+-----------+ | |

The PLUS_NV equation could be expressed with f(Cs,Cd) = Cs+Cd, X=2, Y=1, | |

and Z=1. The X=2 term effectively has the source and destination *both* | |

contribute coverage in the area of intersection. The MINUS_NV equation | |

could be expressed with f(Cs,Cd) = Cd-Cs, X=1, Y=-1, and Z=1. The Y=-1 | |

term effectively has the source-only portion of the pixel *remove* | |

coverage. Both of these don't match the physical model, and would yield | |

odd results when combined with conjoint or disjoint overlap modes. | |

(25) Should we provide more specialized versions of CONJOINT_NV and | |

DISJOINT_NV? | |

RESOLVED: Not in this extension. In the future, we could add new | |

overlap modes such as: | |

NON_INTERSECTING_NV: Like DISJOINT_NV, except that it assumes that | |

As+Ad<=1. This might be interesting when rendering polygons with | |

POLYGON_SMOOTH? | |

DST_INSIDE_NV, SRC_INSIDE_NV: Like CONJOINT_NV, except that it | |

assumes that the destination coverage is fully inside the source or | |

vice versa. For DST_INSIDE, the p0/p1/p2 terms would be Ad, As-Ad, | |

and 0, respectively. | |

For all three of these modes, the specialized versions would have | |

simpler and possibly more efficient math. We're not going to add any of | |

these modes in this extension, however. | |

(26) Should the blend equations have a common prefix (e.g., | |

"BLEND_SRC_OVER") or just use forms without a prefix? | |

RESOLVED: Use forms without a prefix where the tokens are only used in | |

the context of blending (i.e., via the BlendEquation API). We will use | |

a "BLEND_" prefix to identify BlendParameter <pname> values because | |

those tokens can also be used in the general GetIntegerv API. Note that | |

in current OpenGL, some parameters have their own Get* API (e.g., | |

TexParameter), while others use the general GetInteger queries (e.g., | |

PointParameter). | |

(27) Should we use standard GL enums for the blend equations that already | |

have these names? | |

RESOLVED: Yes, to minimize the number of new enums. The primary risk | |

is that reusing standard enum definitions would be problematic if a | |

future core version wanted to use these parameters in the same place | |

with a different meaning. However, all such names are in common use in | |

various compositing standards and our semantics are consistent with | |

those standards. | |

(28) What other APIs support blend equations similar to the ones provided | |

here, and how does the feature set compare? | |

RESOLVED: Khronos' OpenVG 1.1 vector graphics library provides a | |

variety of basic blending equations, with additional modes provided by | |

the KHR_advanced_blending extension: | |

* http://www.khronos.org/registry/vg/specs/openvg-1.1.pdf | |

* http://www.khronos.org/registry/vg/extensions/KHR/advanced_blending.txt | |

The World Wide Web Consortium (W3C)'s Scalable Vector Graphics format | |

supports a variety of blending equations in its compositing | |

specification: | |

* http://www.w3.org/TR/SVGCompositing | |

W3C also has a CSS standard (Compositing and Blending Level 1): | |

* http://www.w3.org/TR/compositing-1/ | |

Adobe's widely-used Portable Document Format (PDF) specification | |

provides numerous blending equations in its "Transparency" section: | |

* http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/ | |

pdf_reference_1-7.pdf | |

Adobe's SWF (Flash) File Format Specification | |

* http://www.adobe.com/go/swfspec | |

Various "2D" graphics APIs, including Oracle's JavaFX, Apple's Quartz | |

2D, Qt's QPainter class, and X Window System's X Rendering Extension | |

also support a variety of blending equations: | |

* http://docs.oracle.com/javafx/2/api/javafx/scene/effect/BlendMode.html | |

* http://developer.apple.com/library/ios/#documentation/GraphicsImaging/ | |

Reference/CGContext/Reference/reference.html | |

* http://doc.qt.digia.com/4.7/qpainter.html#composition-modes | |

* http://www.x.org/releases/current/doc/renderproto/renderproto.txt | |

The following table indicates the set of blend equations from this | |

extension that are supported in these various standards or APIs. "X" | |

indicates that the equation is supported. For OpenVG, "E" indicates | |

that the equation is supported by the KHR_advanced_blending extension. | |

"XO" that the indicates that the equation is supported with conjoint and | |

disjoint overlap modes; others support only the uncorrelated overlap | |

mode. | |

Blending Equation OVG SVG PDF SWF JFX Q2D QPT XRE | |

-------------------- ----- ----- ----- ----- ----- ----- ----- ----- | |

ZERO E X - - - X X XO | |

SRC_NV X X - X - X X XO | |

DST_NV E X - - - - X XO | |

SRC_OVER_NV X X X X X X X XO | |

DST_OVER_NV X X - - - X X XO | |

SRC_IN_NV X X - - - X X XO | |

DST_IN_NV X X - X - X X XO | |

SRC_OUT_NV E X - - - X X XO | |

DST_OUT_NV E X - X - X X XO | |

SRC_ATOP_NV E X - - X X X XO | |

DST_ATOP_NV E X - - - X X XO | |

XOR_NV E X - - - X X XO | |

MULTIPLY_NV X X X X X X X X | |

SCREEN_NV X X X X X X X X | |

OVERLAY_NV E X X X X X X X | |

DARKEN_NV X X X X X X X X | |

LIGHTEN_NV X X X X X X X X | |

COLORDODGE_NV E X X - X X X X | |

COLORBURN_NV E X X - X X X X | |

HARDLIGHT_NV E X X X X X X X | |

SOFTLIGHT_NV E X X - X X X X | |

DIFFERENCE_NV E X X X X X X X | |

EXCLUSION_NV E X X - X X X X | |

INVERT - - - X - - - - | |

INVERT_RGB_NV - - - - - - - - | |

LINEARDODGE_NV E - - - - - - - | |

LINEARBURN_NV E - - - - - - - | |

VIVIDLIGHT_NV E - - - - - - - | |

LINEARLIGHT_NV E - - - - - - - | |

PINLIGHT_NV E - - - - - - - | |

HARDMIX_NV E - - - - - - - | |

HSL_HUE_NV - - X - - X - X | |

HSL_SATURATION_NV - - X - - X - X | |

HSL_COLOR_NV - - X - - X - X | |

HSL_LUMINOSITY_NV - - X - - X - X | |

PLUS_NV / | |

PLUS_CLAMPED_NV / X X - X X X X X | |

PLUS_CLAMPED_ALPHA_NV | |

PLUS_DARKER_NV - - - - - X - - | |

MINUS_NV / E - - X - - - - | |

MINUS_CLAMPED_NV | |

CONTRAST_NV - - - - - - - - | |

INVERT_OVG_NV E - - - - - - - | |

RED_NV - - - - X - - - | |

GREEN_NV - - - - X - - - | |

BLUE_NV - - - - X - - - | |

OpenGL COLOR_LOGIC_OP - - - - - - X - | |

(not in this extension) | |

Notes: | |

* The PLUS_NV, PLUS_CLAMPED_NV, and PLUS_CLAMPED_ALPHA_NV equations | |

are very similar and may be indistinguishable when the destination | |

buffer components are stored in normalized [0,1] numeric spaces, as | |

is the case in most of these standards. The MINUS_NV and | |

MINUS_CLAMPED_NV equations behave similarly. | |

* The SWF specification has a mode called "invert", but it's not clear | |

whether the mode is implemented using INVERT, INVERT_OVG_NV, or some | |

other equation. | |

(29) Should we provide an extension that can be supported on | |

implementations that may not provide fully coherent blending when | |

using the new equations? If so, how will this support be provided | |

and what limitations apply? | |

RESOLVED: Yes, this functionality is useful not just for general 3D | |

rendering, but also for 2D rendering operations (where the primitives | |

rendered may be less complex). As indicated in the issue above, the | |

blend equations provided by this extension are already very commonly | |

used in 2D rendering. Accelerating them on a wide range of GPUs, old | |

and new, would be very useful. | |

Older NVIDIA GPUs are able to support these blending equations as long | |

as rendering is split into distinct passes and no pixel is touched more | |

than once in any given pass. For such GPUs, we specify that the results | |

of blending are undefined if a single pixel (or sample) is touched more | |

than once in a pass, and provide the command BlendBarrierNV() to allow | |

applications to delimit boundaries between passes. As long as rendering | |

commands can be split into passes with barriers, advanced blending will | |

work "normally" even on these older GPUs. | |

Since there are two distinct levels of capability, we will advertise two | |

different extension string entries: | |

- GL_NV_blend_equation_advanced: Provides the new blending | |

functionality without support for full coherence (older GPUs). | |

- GL_NV_blend_equation_advanced_coherent: Provides the new blending | |

functionality with full coherence. | |

Since the functionality of these two extensions is nearly identical, we | |

document them in a single extension specification. | |

(30) On implementations that don't support fully coherent blending, should | |

we provide any convenience features so that "2D" applications aren't | |

required to manually sprinkle BlendBarrierNV() throughout the code? | |

RESOLVED: Yes. When using NV_blend_equation_advanced in conjunction | |

with NV_path_rendering commands like CoverFillPathNV and | |

CoverStrokePathNV, the driver will assist in ensuring coherent and | |

properly ordered blending by inserting implicit blend barriers before | |

rendering each cover primitive. | |

(31) When we generate fragments using the automatic coherence guarantees | |

from NV_path_rendering commands like CoverFillPathNV, what happens if | |

a pixel touched by CoverFillPathNV had already been touched by a | |

previous non-NVpr rendering command without an intervening call to | |

BlendBarrierNV? What happens if a pixel touched by CoverFillPathNV | |

is subsequently touched by a subsequent non-NVpr without an | |

intervening call to BlendBarrierNV? | |

RESOLVED: We specify that a blend barrier is inserted prior to each | |

cover primitives, so that cover primitives are blended coherently | |

relative to geometry from previous primitives (cover or otherwise). We | |

do not guarantee that a blend barrier is inserted after each cover | |

primitive, so applications need to call BlendBarrierNV manually if | |

subsequent non-cover primitives are rendered to the same set of pixels. | |

(32) On implementations supporting fully coherent blending, should we | |

provide some mode allowing implementations to opt out of coherent | |

blending? | |

RESOLVED: Yes. We will provide an enable allowing applications to | |

disable coherent blending in case where (a) implementations are able to | |

provide higher-performance implementations if they don't have to worry | |

about full coherence and/or ordering and (b) applications are willing to | |

use BlendBarrierNV() to take advantage of the higher-performance | |

implementation. The enable will be on by default, which means that | |

advanced blending on fully capable implementations will be "safe" unless | |

explicitly disabled. | |

(33) When fully coherent blending is disabled or not supported, | |

BlendBarrierNV() is used to indicate boundaries between passes. | |

Should any other commands in the OpenGL API also implicitly serve as | |

blend barriers? | |

RESOLVED: In general, no. Except for the NV_path_rendering case above, | |

we will require applications manually use BlendBarrierNV(). There may | |

be other operations that indirectly cause blend results to become | |

coherent (in an implementation-dependent way), but we don't attempt to | |

provide any explicit guarantees. Except for path rendering cover | |

primitives (see issues 30 and 31), applications should always call | |

BlendBarrierNV() between possibly overlapping passes. | |

Note that implementations of this extension may use texture mapping | |

hardware to source the framebuffer for blending and may end up caching | |

pre-blended texel values. This can cause subsequent texture fetches to | |

return stale values unless the texture is re-bound, the | |

TextureBarrierNV() command from the NV_texture_barrier extension is | |

used, or some other action is taken to break the "rendering feedback | |

loop". The existing spec already defines that texel fetches produce | |

undefined results when a texture object is bound both as a texture and | |

attached to the current draw framebuffer, with or without advanced blend | |

equations. See the "Rendering Feedback Loops" section (p. 316 in the | |

OpenGL 4.1 Compatibility Profile specification) for more information. | |

(34) How should the blend equations COLORDODGE_NV and COLORBURN_NV be | |

expressed mathematically? | |

RESOLVED: We changed the definition of these equations after the | |

NV_blend_equation_advanced spec was originally published, as discussed | |

below. These changes add new special cases to the COLORDODGE_NV and | |

COLORBURN_NV equations that are found in newer compositing standard | |

specifications and in a number of implementations of old and new | |

standards. We believe that the omission of the special case in other | |

older specifications is a bug. We have no plans to add new blend | |

equation tokens to support "equivalent" modes without the new special | |

case. | |

Note, however, that older versions of this extension and older NVIDIA | |

drivers implementing it will lack these special cases. A driver update | |

may be required to get the new behavior. | |

There is some disagreement in different published specifications about | |

how these two blend equations should be handled. At the time this | |

extension was initially developed, all specifications we found that | |

specified blending equations mathematically (see issue 28) were written | |

the same way. Since then, we discovered that newer working drafts of | |

the W3C Compositing and Blending Level 1 specification (for CSS and SVG) | |

express "color-burn" as follows (translated to our nomenclature): | |

if (Cd == 1) // their Cb (backdrop) is our Cd (destination) | |

f(Cs,Cd) = 1 // their B() is our f() | |

else if (Cs == 0) | |

f(Cs,Cd) = 0 | |

else | |

f(Cs,Cd) = 1 - min(1, (1-Cd)/Cs) | |

http://www.w3.org/TR/2013/WD-compositing-1-20131010/ | |

#blendingcolorburn | |

Earlier versions of the same W3C specification, an older SVG compositing | |

draft specification, the Adobe PDF specification (and the ISO 32000-1 | |

standard), and the KHR_advanced_blending extension to OpenVG all specify | |

the following equation without the initial special case: | |

if (Cs == 0) | |

f(Cs,Cd) = 0 | |

else | |

f(Cs,Cd) = 1 - min(1, (1-Cd)/Cs) | |

http://www.w3.org/TR/2012/WD-compositing-20120816/ | |

#blendingcolorburn | |

http://www.w3.org/TR/2011/WD-SVGCompositing-20110315/ | |

http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/ | |

pdfs/pdf_reference_1-7.pdf | |

http://www.khronos.org/registry/vg/extensions/KHR/ | |

advanced_blending.txt | |

For the Adobe PDF specification, the corrected blend equations are | |

published in an Adobe supplement to ISO 32000-1 and are expected to be | |

accepted in a future version of the standard: | |

http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/ | |

devnet/pdf/pdfs/adobe_supplement_iso32000_1.pdf | |

The author's understanding is that multiple shipping implementations of | |

these blending modes implement the special case for "Cd==1" above, | |

including various Adobe products and the open-source Ghostscript | |

project. | |

We believe that the extra special case in this specification is | |

consistent with the physical model of color burning. Burning is | |

described in | |

http://en.wikipedia.org/wiki/Dodging_and_burning | |

as making a print with normal exposure, and then adding additional | |

exposure to darken the overall image. In the general equation: | |

1 - min(1, (1-Cd)/Cs) | |

Cs operates as a sort of fudge factor where a value of 1.0 implies no | |

additional exposure time and 0.0 implies arbitrarily long additional | |

exposure time, where the initial amount of exposure (1-Cd) is multiplied | |

by 1/Cs and then clamped to maximum exposure by the min() operation. | |

The Cd==1 special case here implies that we get zero exposure in the | |

initial print, since 1-Cd==0. No amount of extra exposure time will | |

generate any additional exposure. This would imply that the final | |

result should have zero exposure and thus a final f() value of 1. This | |

matches the initial special case. Without that special case, we would | |

hit the second special case if Cs==0 (infinite exposure time), which | |

would yield an incorrect final value of 0 (full exposure). | |

A similar issue applies to COLORDODGE_NV, where some specifications | |

include a special case for Cb==0 while others do not. We have added a | |

special case there as well. | |

Revision History | |

Rev. Date Author Changes | |

---- -------- -------- ----------------------------------------- | |

9 09/30/14 pbrown Fix incorrectly specified color clamping in | |

the HSL blend modes. | |

8 02/26/14 pbrown For non-coherent blending, clarify that all | |

writes to a sample are considered to "touch" | |

that sample and require a BlendBarrierOES call | |

before blending overlapping geometry. Clears, | |

non-blended geometry, and copies by | |

BlitFramebuffer or TexSubImage are all | |

considered to "touch" a sample (bug 11738). | |

Specify that non-premultiplied values | |

corresponding to ill-conditioned premultiplied | |

colors such as (1,1,1,0) are undefined (bug | |

11739). Update issue (12) related to | |

ill-conditioned premultiplied colors. | |

7 11/06/13 pbrown Fix the language about non-coherent blending | |

to specify that results are undefined only if an | |

individual *sample* is touched more than once | |

(instead of *pixel*). Minor language tweaks to | |

use "equations" consistently, instead of | |

sometimes using "modes". | |

6 10/21/13 pbrown Add NV-suffixed names for tokens reusing values | |

from core OpenGL enums (XOR, RED, GREEN, BLUE) | |

that are not in core OpenGL ES 2.0. This allows | |

code targeting both APIs to use the same | |

NV-suffixed #defines. Some older versions of | |

the OpenGL "glext.h" header will not have the | |

NV-suffixed names. | |

5 10/21/13 pbrown Add a reference to the Adobe supplement to | |

ISO 32000-1, which includes the corrected | |

equations for COLORDODGE_NV and COLORBURN_NV. | |

Move "NVIDIA Implementation Details" down | |

a bit in the spec. | |

4 10/16/13 pbrown Modify the definition of COLORDODGE_NV and | |

COLORBURN_NV to match de facto standard | |

implemenations and new CSS/SVG compositing | |

spec; add issue (34). | |

3 08/19/13 pbrown Fix typos in the OpenGL ES 2.0 and 3.0 | |

interactions sections. | |

2 07/25/13 mjk Add W3C CSS compositing reference. | |

1 pbrown Internal revisions. |