1/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6#include "BlendingHelpers.hlslh" 7#include "BlendShaderConstants.h" 8 9typedef float4 rect; 10 11float4x4 mLayerTransform : register(vs, c0); 12float4x4 mProjection : register(vs, c4); 13float4 vRenderTargetOffset : register(vs, c8); 14rect vTextureCoords : register(vs, c9); 15rect vLayerQuad : register(vs, c10); 16float4x4 mMaskTransform : register(vs, c11); 17float4x4 mBackdropTransform : register(vs, c15); 18 19float4 fLayerColor : register(ps, c0); 20float fLayerOpacity : register(ps, c1); 21 22// x = layer type 23// y = mask type 24// z = blend op 25// w = is premultiplied 26uint4 iBlendConfig : register(ps, c2); 27 28row_major float3x3 mYuvColorMatrix : register(ps, c3); 29 30sampler sSampler : register(ps, s0); 31 32// The mix-blend mega shader uses all variables, so we have to make sure they 33// are assigned fixed slots. 34Texture2D tRGB : register(ps, t0); 35Texture2D tY : register(ps, t1); 36Texture2D tCb : register(ps, t2); 37Texture2D tCr : register(ps, t3); 38Texture2D tRGBWhite : register(ps, t4); 39Texture2D tMask : register(ps, t5); 40Texture2D tBackdrop : register(ps, t6); 41 42struct VS_INPUT { 43 float2 vPosition : POSITION; 44}; 45 46struct VS_TEX_INPUT { 47 float2 vPosition : POSITION; 48 float2 vTexCoords : TEXCOORD0; 49}; 50 51struct VS_OUTPUT { 52 float4 vPosition : SV_Position; 53 float2 vTexCoords : TEXCOORD0; 54}; 55 56struct VS_MASK_OUTPUT { 57 float4 vPosition : SV_Position; 58 float2 vTexCoords : TEXCOORD0; 59 float3 vMaskCoords : TEXCOORD1; 60}; 61 62// Combined struct for the mix-blend compatible vertex shaders. 63struct VS_BLEND_OUTPUT { 64 float4 vPosition : SV_Position; 65 float2 vTexCoords : TEXCOORD0; 66 float3 vMaskCoords : TEXCOORD1; 67 float2 vBackdropCoords : TEXCOORD2; 68}; 69 70struct PS_OUTPUT { 71 float4 vSrc; 72 float4 vAlpha; 73}; 74 75float2 TexCoords(const float2 aPosition) 76{ 77 float2 result; 78 const float2 size = vTextureCoords.zw; 79 result.x = vTextureCoords.x + aPosition.x * size.x; 80 result.y = vTextureCoords.y + aPosition.y * size.y; 81 82 return result; 83} 84 85SamplerState LayerTextureSamplerLinear 86{ 87 Filter = MIN_MAG_MIP_LINEAR; 88 AddressU = Clamp; 89 AddressV = Clamp; 90}; 91 92float4 TransformedPosition(float2 aInPosition) 93{ 94 // the current vertex's position on the quad 95 // [x,y,0,1] is mandated by the CSS Transforms spec as the point value to transform 96 float4 position = float4(0, 0, 0, 1); 97 98 // We use 4 component floats to uniquely describe a rectangle, by the structure 99 // of x, y, width, height. This allows us to easily generate the 4 corners 100 // of any rectangle from the 4 corners of the 0,0-1,1 quad that we use as the 101 // stream source for our LayerQuad vertex shader. We do this by doing: 102 // Xout = x + Xin * width 103 // Yout = y + Yin * height 104 float2 size = vLayerQuad.zw; 105 position.x = vLayerQuad.x + aInPosition.x * size.x; 106 position.y = vLayerQuad.y + aInPosition.y * size.y; 107 108 position = mul(mLayerTransform, position); 109 110 return position; 111} 112 113float4 VertexPosition(float4 aTransformedPosition) 114{ 115 float4 result; 116 result.w = aTransformedPosition.w; 117 result.xyz = aTransformedPosition.xyz / aTransformedPosition.w; 118 result -= vRenderTargetOffset; 119 result.xyz *= result.w; 120 121 result = mul(mProjection, result); 122 123 return result; 124} 125 126float2 BackdropPosition(float4 aPosition) 127{ 128 // Move the position from clip space (-1,1) into 0..1 space. 129 float2 pos; 130 pos.x = (aPosition.x + 1.0) / 2.0; 131 pos.y = 1.0 - (aPosition.y + 1.0) / 2.0; 132 133 return mul(mBackdropTransform, float4(pos.xy, 0, 1.0)).xy; 134} 135 136VS_OUTPUT LayerQuadVS(const VS_INPUT aVertex) 137{ 138 VS_OUTPUT outp; 139 float4 position = TransformedPosition(aVertex.vPosition); 140 141 outp.vPosition = VertexPosition(position); 142 outp.vTexCoords = TexCoords(aVertex.vPosition.xy); 143 144 return outp; 145} 146 147float3 MaskCoords(float4 aPosition) 148{ 149 // We use the w coord to do non-perspective correct interpolation: 150 // the quad might be transformed in 3D, in which case it will have some 151 // perspective. The graphics card will do perspective-correct interpolation 152 // of the texture, but our mask is already transformed and so we require 153 // linear interpolation. Therefore, we must correct the interpolation 154 // ourselves, we do this by multiplying all coords by w here, and dividing by 155 // w in the pixel shader (post-interpolation), we pass w in outp.vMaskCoords.z. 156 // See http://en.wikipedia.org/wiki/Texture_mapping#Perspective_correctness 157 return float3(mul(mMaskTransform, (aPosition / aPosition.w)).xy, 1.0) * aPosition.w; 158} 159 160VS_MASK_OUTPUT LayerQuadMaskVS(const VS_INPUT aVertex) 161{ 162 float4 position = TransformedPosition(aVertex.vPosition); 163 164 VS_MASK_OUTPUT outp; 165 outp.vPosition = VertexPosition(position); 166 outp.vMaskCoords = MaskCoords(position); 167 outp.vTexCoords = TexCoords(aVertex.vPosition.xy); 168 return outp; 169} 170 171VS_OUTPUT LayerDynamicVS(const VS_TEX_INPUT aVertex) 172{ 173 VS_OUTPUT outp; 174 175 float4 position = float4(aVertex.vPosition, 0, 1); 176 position = mul(mLayerTransform, position); 177 outp.vPosition = VertexPosition(position); 178 179 outp.vTexCoords = aVertex.vTexCoords; 180 181 return outp; 182} 183 184VS_MASK_OUTPUT LayerDynamicMaskVS(const VS_TEX_INPUT aVertex) 185{ 186 VS_MASK_OUTPUT outp; 187 188 float4 position = float4(aVertex.vPosition, 0, 1); 189 position = mul(mLayerTransform, position); 190 outp.vPosition = VertexPosition(position); 191 192 // calculate the position on the mask texture 193 outp.vMaskCoords = MaskCoords(position); 194 outp.vTexCoords = aVertex.vTexCoords; 195 return outp; 196} 197 198float4 RGBAShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target 199{ 200 float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z; 201 float mask = tMask.Sample(sSampler, maskCoords).r; 202 return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity * mask; 203} 204 205float4 RGBShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target 206{ 207 float4 result; 208 result = tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity; 209 result.a = fLayerOpacity; 210 211 float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z; 212 float mask = tMask.Sample(sSampler, maskCoords).r; 213 return result * mask; 214} 215 216/* From Rec601: 217[R] [1.1643835616438356, 0.0, 1.5960267857142858] [ Y - 16] 218[G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708] x [Cb - 128] 219[B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [Cr - 128] 220 221For [0,1] instead of [0,255], and to 5 places: 222[R] [1.16438, 0.00000, 1.59603] [ Y - 0.06275] 223[G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196] 224[B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196] 225 226From Rec709: 227[R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [ Y - 16] 228[G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444] x [Cb - 128] 229[B] [1.1643835616438356, 2.1124017857142854, 0.0] [Cr - 128] 230 231For [0,1] instead of [0,255], and to 5 places: 232[R] [1.16438, 0.00000, 1.79274] [ Y - 0.06275] 233[G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196] 234[B] [1.16438, 2.11240, 0.00000] [Cr - 0.50196] 235*/ 236float4 CalculateYCbCrColor(const float2 aTexCoords) 237{ 238 float3 yuv; 239 float4 color; 240 241 yuv.x = tY.Sample(sSampler, aTexCoords).r - 0.06275; 242 yuv.y = tCb.Sample(sSampler, aTexCoords).r - 0.50196; 243 yuv.z = tCr.Sample(sSampler, aTexCoords).r - 0.50196; 244 245 color.rgb = mul(mYuvColorMatrix, yuv); 246 color.a = 1.0f; 247 248 return color; 249} 250 251float4 CalculateNV12Color(const float2 aTexCoords) 252{ 253 float3 yuv; 254 float4 color; 255 256 yuv.x = tY.Sample(sSampler, aTexCoords).r - 0.06275; 257 yuv.y = tCb.Sample(sSampler, aTexCoords).r - 0.50196; 258 yuv.z = tCb.Sample(sSampler, aTexCoords).g - 0.50196; 259 260 color.rgb = mul(mYuvColorMatrix, yuv); 261 color.a = 1.0f; 262 263 return color; 264} 265 266float4 YCbCrShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target 267{ 268 float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z; 269 float mask = tMask.Sample(sSampler, maskCoords).r; 270 271 return CalculateYCbCrColor(aVertex.vTexCoords) * fLayerOpacity * mask; 272} 273 274float4 NV12ShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target 275{ 276 float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z; 277 float mask = tMask.Sample(sSampler, maskCoords).r; 278 279 return CalculateNV12Color(aVertex.vTexCoords) * fLayerOpacity * mask; 280} 281 282PS_OUTPUT ComponentAlphaShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target 283{ 284 PS_OUTPUT result; 285 286 result.vSrc = tRGB.Sample(sSampler, aVertex.vTexCoords); 287 result.vAlpha = 1.0 - tRGBWhite.Sample(sSampler, aVertex.vTexCoords) + result.vSrc; 288 result.vSrc.a = result.vAlpha.g; 289 290 float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z; 291 float mask = tMask.Sample(sSampler, maskCoords).r; 292 result.vSrc *= fLayerOpacity * mask; 293 result.vAlpha *= fLayerOpacity * mask; 294 295 return result; 296} 297 298float4 SolidColorShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target 299{ 300 float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z; 301 float mask = tMask.Sample(sSampler, maskCoords).r; 302 return fLayerColor * mask; 303} 304 305/* 306 * Un-masked versions 307 ************************************************************* 308 */ 309float4 RGBAShader(const VS_OUTPUT aVertex) : SV_Target 310{ 311 return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity; 312} 313 314float4 RGBShader(const VS_OUTPUT aVertex) : SV_Target 315{ 316 float4 result; 317 result = tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity; 318 result.a = fLayerOpacity; 319 return result; 320} 321 322float4 YCbCrShader(const VS_OUTPUT aVertex) : SV_Target 323{ 324 return CalculateYCbCrColor(aVertex.vTexCoords) * fLayerOpacity; 325} 326 327float4 NV12Shader(const VS_OUTPUT aVertex) : SV_Target 328{ 329 return CalculateNV12Color(aVertex.vTexCoords) * fLayerOpacity; 330} 331 332PS_OUTPUT ComponentAlphaShader(const VS_OUTPUT aVertex) : SV_Target 333{ 334 PS_OUTPUT result; 335 336 result.vSrc = tRGB.Sample(sSampler, aVertex.vTexCoords); 337 result.vAlpha = 1.0 - tRGBWhite.Sample(sSampler, aVertex.vTexCoords) + result.vSrc; 338 result.vSrc.a = result.vAlpha.g; 339 result.vSrc *= fLayerOpacity; 340 result.vAlpha *= fLayerOpacity; 341 return result; 342} 343 344float4 SolidColorShader(const VS_OUTPUT aVertex) : SV_Target 345{ 346 return fLayerColor; 347} 348 349// Mix-blend compatible vertex shaders. 350VS_BLEND_OUTPUT LayerQuadBlendVS(const VS_INPUT aVertex) 351{ 352 VS_OUTPUT v = LayerQuadVS(aVertex); 353 354 VS_BLEND_OUTPUT o; 355 o.vPosition = v.vPosition; 356 o.vTexCoords = v.vTexCoords; 357 o.vMaskCoords = float3(0, 0, 0); 358 o.vBackdropCoords = BackdropPosition(v.vPosition); 359 return o; 360} 361 362VS_BLEND_OUTPUT LayerQuadBlendMaskVS(const VS_INPUT aVertex) 363{ 364 VS_MASK_OUTPUT v = LayerQuadMaskVS(aVertex); 365 366 VS_BLEND_OUTPUT o; 367 o.vPosition = v.vPosition; 368 o.vTexCoords = v.vTexCoords; 369 o.vMaskCoords = v.vMaskCoords; 370 o.vBackdropCoords = BackdropPosition(v.vPosition); 371 return o; 372} 373 374VS_BLEND_OUTPUT LayerDynamicBlendVS(const VS_TEX_INPUT aVertex) 375{ 376 VS_OUTPUT v = LayerDynamicVS(aVertex); 377 378 VS_BLEND_OUTPUT o; 379 o.vPosition = v.vPosition; 380 o.vTexCoords = v.vTexCoords; 381 o.vMaskCoords = float3(0, 0, 0); 382 o.vBackdropCoords = BackdropPosition(v.vPosition); 383 return o; 384} 385 386VS_BLEND_OUTPUT LayerDynamicBlendMaskVS(const VS_TEX_INPUT aVertex) 387{ 388 VS_MASK_OUTPUT v = LayerDynamicMaskVS(aVertex); 389 390 VS_BLEND_OUTPUT o; 391 o.vPosition = v.vPosition; 392 o.vTexCoords = v.vTexCoords; 393 o.vMaskCoords = v.vMaskCoords; 394 o.vBackdropCoords = BackdropPosition(v.vPosition); 395 return o; 396} 397 398// The layer type and mask type are specified as constants. We use these to 399// call the correct pixel shader to determine the source color for blending. 400// Unfortunately this also requires some boilerplate to convert VS_BLEND_OUTPUT 401// to a compatible pixel shader input. 402float4 ComputeBlendSourceColor(const VS_BLEND_OUTPUT aVertex) 403{ 404 if (iBlendConfig.y == PS_MASK_NONE) { 405 VS_OUTPUT tmp; 406 tmp.vPosition = aVertex.vPosition; 407 tmp.vTexCoords = aVertex.vTexCoords; 408 if (iBlendConfig.x == PS_LAYER_RGB) { 409 return RGBShader(tmp); 410 } else if (iBlendConfig.x == PS_LAYER_RGBA) { 411 return RGBAShader(tmp); 412 } else if (iBlendConfig.x == PS_LAYER_YCBCR) { 413 return YCbCrShader(tmp); 414 } else if (iBlendConfig.x == PS_LAYER_NV12) { 415 return NV12Shader(tmp); 416 } 417 return SolidColorShader(tmp); 418 } else if (iBlendConfig.y == PS_MASK) { 419 VS_MASK_OUTPUT tmp; 420 tmp.vPosition = aVertex.vPosition; 421 tmp.vTexCoords = aVertex.vTexCoords; 422 tmp.vMaskCoords = aVertex.vMaskCoords; 423 424 if (iBlendConfig.x == PS_LAYER_RGB) { 425 return RGBShaderMask(tmp); 426 } else if (iBlendConfig.x == PS_LAYER_RGBA) { 427 return RGBAShaderMask(tmp); 428 } else if (iBlendConfig.x == PS_LAYER_YCBCR) { 429 return YCbCrShaderMask(tmp); 430 } else if (iBlendConfig.x == PS_LAYER_NV12) { 431 return NV12ShaderMask(tmp); 432 } 433 return SolidColorShaderMask(tmp); 434 } 435 436 return float4(0.0, 0.0, 0.0, 1.0); 437} 438 439float3 ChooseBlendFunc(float3 dest, float3 src) 440{ 441 [flatten] switch (iBlendConfig.z) { 442 case PS_BLEND_MULTIPLY: 443 return BlendMultiply(dest, src); 444 case PS_BLEND_SCREEN: 445 return BlendScreen(dest, src); 446 case PS_BLEND_OVERLAY: 447 return BlendOverlay(dest, src); 448 case PS_BLEND_DARKEN: 449 return BlendDarken(dest, src); 450 case PS_BLEND_LIGHTEN: 451 return BlendLighten(dest, src); 452 case PS_BLEND_COLOR_DODGE: 453 return BlendColorDodge(dest, src); 454 case PS_BLEND_COLOR_BURN: 455 return BlendColorBurn(dest, src); 456 case PS_BLEND_HARD_LIGHT: 457 return BlendHardLight(dest, src); 458 case PS_BLEND_SOFT_LIGHT: 459 return BlendSoftLight(dest, src); 460 case PS_BLEND_DIFFERENCE: 461 return BlendDifference(dest, src); 462 case PS_BLEND_EXCLUSION: 463 return BlendExclusion(dest, src); 464 case PS_BLEND_HUE: 465 return BlendHue(dest, src); 466 case PS_BLEND_SATURATION: 467 return BlendSaturation(dest, src); 468 case PS_BLEND_COLOR: 469 return BlendColor(dest, src); 470 case PS_BLEND_LUMINOSITY: 471 return BlendLuminosity(dest, src); 472 default: 473 return float3(0, 0, 0); 474 } 475} 476 477float4 BlendShader(const VS_BLEND_OUTPUT aVertex) : SV_Target 478{ 479 float4 backdrop = tBackdrop.Sample(sSampler, aVertex.vBackdropCoords.xy); 480 float4 source = ComputeBlendSourceColor(aVertex); 481 482 // Shortcut when the backdrop or source alpha is 0, otherwise we may leak 483 // infinity into the blend function and return incorrect results. 484 if (backdrop.a == 0.0) { 485 return source; 486 } 487 if (source.a == 0.0) { 488 return float4(0, 0, 0, 0); 489 } 490 491 // The spec assumes there is no premultiplied alpha. The backdrop is always 492 // premultiplied, so undo the premultiply. If the source is premultiplied we 493 // must fix that as well. 494 backdrop.rgb /= backdrop.a; 495 if (iBlendConfig.w) { 496 source.rgb /= source.a; 497 } 498 499 float4 result; 500 result.rgb = ChooseBlendFunc(backdrop.rgb, source.rgb); 501 result.a = source.a; 502 503 // Factor backdrop alpha, then premultiply for the final OP_OVER. 504 result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb; 505 result.rgb *= result.a; 506 return result; 507} 508