summaryrefslogtreecommitdiffstats
path: root/gfx/2d/ShadersD2D1.hlsl
blob: 163b6b388fbd4a770520c9b36f7f939a20e722ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Texture2D InputTexture : register(t0);
SamplerState InputSampler : register(s0);
Texture2D GradientTexture : register(t1);
SamplerState GradientSampler : register(s1);

cbuffer radialGradientConstants : register(b0)
{
    // Precalculate as much as we can!
    float3 diff : packoffset(c0.x);
    float2 center1 : packoffset(c1.x);
    float A : packoffset(c1.z);
    float radius1 : packoffset(c1.w);
    float sq_radius1 : packoffset(c2.x);

    // The next two values are used for a hack to compensate for an apparent
    // bug in D2D where the GradientSampler SamplerState doesn't get the
    // correct addressing modes.
    float repeat_correct : packoffset(c2.y);
    float allow_odd : packoffset(c2.z);

    float3x2 transform : packoffset(c3.x);
}

cbuffer conicGradientConstants : register(b0)
{
    float2 center : packoffset(c0.x);
    float angle : packoffset(c0.z);
    float start_offset : packoffset(c0.w);
    float end_offset : packoffset(c1.x);

    // The next two values are used for a hack to compensate for an apparent
    // bug in D2D where the GradientSampler SamplerState doesn't get the
    // correct addressing modes.
    float repeat_correct_conic : packoffset(c1.y);
    float allow_odd_conic : packoffset(c1.z);

    float3x2 transform_conic : packoffset(c2.x);
}


static const float M_PI = 3.14159265f;

float4 SampleConicGradientPS(
    float4 clipSpaceOutput  : SV_POSITION,
    float4 sceneSpaceOutput : SCENE_POSITION,
    float4 texelSpaceInput0 : TEXCOORD0
    ) : SV_Target
{
  float2 p = float2(sceneSpaceOutput.x * transform_conic._11 + sceneSpaceOutput.y * transform_conic._21 + transform_conic._31,
                    sceneSpaceOutput.x * transform_conic._12 + sceneSpaceOutput.y * transform_conic._22 + transform_conic._32);
  float2 dir = float2(
    -(center.y - p.y),
     (center.x - p.x));
  float vstart = start_offset;
  float vend = end_offset;
  float n = 1/(vend-vstart);
  float current_angle = atan2(dir.y, dir.x)-angle;
  float lambda = fmod(n*current_angle/M_PI/2+vend-vstart+.5,1);
  float offset = lambda;
  float4 output = GradientTexture.Sample(GradientSampler, float2(offset, 0.5));
  // Premultiply
  output.rgb *= output.a;
  // Multiply the output color by the input mask for the operation.
  output *= InputTexture.Sample(InputSampler, texelSpaceInput0.xy);

  return output;
};

float4 SampleRadialGradientPS(
    float4 clipSpaceOutput  : SV_POSITION,
    float4 sceneSpaceOutput : SCENE_POSITION,
    float4 texelSpaceInput0 : TEXCOORD0
    ) : SV_Target
{
  // Radial gradient painting is defined as the set of circles whose centers
  // are described by C(t) = (C2 - C1) * t + C1; with radii
  // R(t) = (R2 - R1) * t + R1; for R(t) > 0. This shader solves the
  // quadratic equation that arises when calculating t for pixel (x, y).
  //
  // A more extensive derrivation can be found in the pixman radial gradient
  // code.

  float2 p = float2(sceneSpaceOutput.x * transform._11 + sceneSpaceOutput.y * transform._21 + transform._31,
                    sceneSpaceOutput.x * transform._12 + sceneSpaceOutput.y * transform._22 + transform._32);
  float3 dp = float3(p - center1, radius1);

  // dpx * dcx + dpy * dcy + r * dr
  float B = dot(dp, diff);

  float C = pow(dp.x, 2) + pow(dp.y, 2) - sq_radius1;

  float det = pow(B, 2) - A * C;

  float sqrt_det = sqrt(abs(det));

  float2 t = (B + float2(sqrt_det, -sqrt_det)) / A;

  float2 isValid = step(float2(-radius1, -radius1), t * diff.z);

  float upper_t = lerp(t.y, t.x, isValid.x);

  // Addressing mode bug work-around.. first let's see if we should consider odd repetitions separately.
  float oddeven = abs(fmod(floor(upper_t), 2)) * allow_odd;

  // Now let's calculate even or odd addressing in a branchless manner.
  float upper_t_repeated = ((upper_t - floor(upper_t)) * (1.0f - oddeven)) + ((ceil(upper_t) - upper_t) * oddeven);

  float4 output = GradientTexture.Sample(GradientSampler, float2(upper_t * (1.0f - repeat_correct) + upper_t_repeated * repeat_correct, 0.5));
  // Premultiply
  output.rgb *= output.a;
  // Multiply the output color by the input mask for the operation.
  output *= InputTexture.Sample(InputSampler, texelSpaceInput0.xy);

  // In order to compile for PS_4_0_level_9_3 we need to be branchless.
  // This is essentially returning nothing, i.e. bailing early if:
  // det < 0 || max(isValid.x, isValid.y) <= 0
  return output * abs(step(max(isValid.x, isValid.y), 0) - 1.0f) * step(0, det);
};

float4 SampleRadialGradientA0PS(
    float4 clipSpaceOutput  : SV_POSITION,
    float4 sceneSpaceOutput : SCENE_POSITION,
    float4 texelSpaceInput0 : TEXCOORD0
    ) : SV_Target
{
  // This simpler shader is used for the degenerate case where A is 0,
  // i.e. we're actually solving a linear equation.

  float2 p = float2(sceneSpaceOutput.x * transform._11 + sceneSpaceOutput.y * transform._21 + transform._31,
                    sceneSpaceOutput.x * transform._12 + sceneSpaceOutput.y * transform._22 + transform._32);
  float3 dp = float3(p - center1, radius1);

  // dpx * dcx + dpy * dcy + r * dr
  float B = dot(dp, diff);

  float C = pow(dp.x, 2) + pow(dp.y, 2) - pow(radius1, 2);

  float t = 0.5 * C / B;

  // Addressing mode bug work-around.. first let's see if we should consider odd repetitions separately.
  float oddeven = abs(fmod(floor(t), 2)) * allow_odd;

  // Now let's calculate even or odd addressing in a branchless manner.
  float t_repeated = ((t - floor(t)) * (1.0f - oddeven)) + ((ceil(t) - t) * oddeven);

  float4 output = GradientTexture.Sample(GradientSampler, float2(t * (1.0f - repeat_correct) + t_repeated * repeat_correct, 0.5));
  // Premultiply
  output.rgb *= output.a;
  // Multiply the output color by the input mask for the operation.
  output *= InputTexture.Sample(InputSampler, texelSpaceInput0.xy);

  // In order to compile for PS_4_0_level_9_3 we need to be branchless.
  // This is essentially returning nothing, i.e. bailing early if:
  // -radius1 >= t * diff.z
  return output * abs(step(t * diff.z, -radius1) - 1.0f);
};