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