Revert "[Tizen] Append 1 pixel if corner radius is big enough"
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / graphics / shaders / color-visual-shader.frag
1 #if defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE) || defined(IS_REQUIRED_BLUR)
2 INPUT mediump vec2 vPosition;
3 INPUT mediump vec2 vRectSize;
4 INPUT mediump vec2 vOptRectSize;
5 #ifdef IS_REQUIRED_ROUNDED_CORNER
6 INPUT mediump vec4 vCornerRadius;
7 #endif
8 #endif
9
10 #if defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE) || defined(IS_REQUIRED_BLUR)
11 // Be used when we calculate anti-alias range near 1 pixel.
12 uniform highp vec3 uScale;
13 #endif
14
15 uniform lowp vec4 uColor;
16 uniform lowp vec3 mixColor;
17 #ifdef IS_REQUIRED_BLUR
18 uniform mediump float blurRadius;
19 #elif defined(IS_REQUIRED_BORDERLINE)
20 uniform mediump float borderlineWidth;
21 uniform mediump float borderlineOffset;
22 uniform lowp vec4 borderlineColor;
23 uniform lowp vec4 uActorColor;
24 #endif
25
26 #if defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE) || defined(IS_REQUIRED_BLUR)
27 // Global values both rounded corner and borderline use
28
29 // radius of rounded corner on this quadrant
30 mediump float gRadius = 0.0;
31
32 // fragment coordinate. NOTE : vec2(0.0, 0.0) is vRectSize, the corner of visual
33 mediump vec2 gFragmentPosition = vec2(0.0, 0.0);
34 // center coordinate of rounded corner circle. vec2(gCenterPosition, gCenterPosition).
35 mediump float gCenterPosition = 0.0;
36 // relative coordinate of gFragmentPosition from gCenterPosition.
37 mediump vec2 gDiff = vec2(0.0, 0.0);
38 // potential value what our algorithm use.
39 mediump float gPotential = 0.0;
40
41 // threshold of potential
42 mediump float gPotentialRange = 0.0;
43 mediump float gMaxOutlinePotential = 0.0;
44 mediump float gMinOutlinePotential = 0.0;
45 mediump float gMaxInlinePotential = 0.0;
46 mediump float gMinInlinePotential = 0.0;
47
48 void calculateCornerRadius()
49 {
50 #ifdef IS_REQUIRED_ROUNDED_CORNER
51   gRadius =
52   mix(
53     mix(vCornerRadius.x, vCornerRadius.y, sign(vPosition.x) * 0.5 + 0.5),
54     mix(vCornerRadius.w, vCornerRadius.z, sign(vPosition.x) * 0.5 + 0.5),
55     sign(vPosition.y) * 0.5 + 0.5
56   );
57 #endif
58 }
59
60 void calculatePosition()
61 {
62   gFragmentPosition = abs(vPosition) - vRectSize;
63   gCenterPosition = -gRadius;
64 #ifdef IS_REQUIRED_BLUR
65 #elif defined(IS_REQUIRED_BORDERLINE)
66   gCenterPosition += borderlineWidth * (clamp(borderlineOffset, -1.0, 1.0) + 1.0) * 0.5;
67 #endif
68   gDiff = gFragmentPosition - gCenterPosition;
69 }
70
71 void calculatePotential()
72 {
73   gPotential = length(max(gDiff, 0.0)) + min(0.0, max(gDiff.x, gDiff.y));
74 }
75
76 void setupMinMaxPotential()
77 {
78   // Set soft anti-alias range at most 10% of visual size.
79   // The range should be inverse proportion with scale of view.
80   // To avoid divid-by-zero, let we allow minimum scale value is 0.001 (0.1%)
81   gPotentialRange = min(1.0, max(vRectSize.x, vRectSize.y) * 0.2) / max(0.001, max(uScale.x, uScale.y));
82
83   gMaxOutlinePotential = gRadius + gPotentialRange;
84   gMinOutlinePotential = gRadius - gPotentialRange;
85
86 #ifdef IS_REQUIRED_BLUR
87   gMaxInlinePotential = gMaxOutlinePotential;
88   gMinInlinePotential = gMinOutlinePotential;
89 #elif defined(IS_REQUIRED_BORDERLINE)
90   gMaxInlinePotential = gMaxOutlinePotential - borderlineWidth;
91   gMinInlinePotential = gMinOutlinePotential - borderlineWidth;
92 #else
93   gMaxInlinePotential = gMaxOutlinePotential;
94   gMinInlinePotential = gMinOutlinePotential;
95 #endif
96
97   // reduce defect near edge of rounded corner.
98   gMaxOutlinePotential += clamp(-min(gDiff.x, gDiff.y) / max(1.0, gRadius), 0.0, 1.0);
99   gMinOutlinePotential += clamp(-min(gDiff.x, gDiff.y) / max(1.0, gRadius), 0.0, 1.0);
100 }
101
102 void PreprocessPotential()
103 {
104   calculateCornerRadius();
105   calculatePosition();
106   calculatePotential();
107
108   setupMinMaxPotential();
109 }
110 #endif
111
112 #ifdef IS_REQUIRED_BLUR
113 #elif defined(IS_REQUIRED_BORDERLINE)
114 lowp vec4 convertBorderlineColor(lowp vec4 textureColor)
115 {
116   mediump float potential = gPotential;
117
118   // default opacity of borderline is 0.0
119   mediump float borderlineOpacity = 0.0;
120
121   // calculate borderline opacity by potential
122   if(potential > gMinInlinePotential)
123   {
124     // potential is inside borderline range.
125     borderlineOpacity = smoothstep(gMinInlinePotential, gMaxInlinePotential, potential);
126
127     // Muliply borderlineWidth to resolve very thin borderline
128     borderlineOpacity *= min(1.0, borderlineWidth / gPotentialRange);
129   }
130
131   lowp vec3  borderlineColorRGB   = borderlineColor.rgb * uActorColor.rgb;
132   lowp float borderlineColorAlpha = borderlineColor.a * uActorColor.a;
133   // NOTE : color-visual is always not preMultiplied.
134
135   // Calculate inside of borderline when alpha is between (0.0  1.0). So we need to apply texture color.
136   // If borderlineOpacity is exactly 0.0, we always use whole texture color. In this case, we don't need to run below code.
137   // But if borderlineOpacity > 0.0 and borderlineColor.a == 0.0, we need to apply tCornerRadius.
138   if(borderlineOpacity > 0.0 && borderlineColor.a * borderlineOpacity < 1.0)
139   {
140     mediump float tCornerRadius = -gCenterPosition + gPotentialRange;
141     mediump float MaxTexturelinePotential = tCornerRadius + gPotentialRange;
142     mediump float MinTexturelinePotential = tCornerRadius - gPotentialRange;
143     if(potential > MaxTexturelinePotential)
144     {
145       // potential is out of texture range.
146       textureColor = vec4(0.0);
147     }
148     else
149     {
150       // potential is in texture range.
151       lowp float textureAlphaScale = mix(1.0, 0.0, smoothstep(MinTexturelinePotential, MaxTexturelinePotential, potential));
152       textureColor.a *= textureAlphaScale;
153       textureColor.rgb *= textureColor.a;
154     }
155
156     // NOTE : color-visual is always not preMultiplied.
157     borderlineColorAlpha *= borderlineOpacity;
158     borderlineColorRGB *= borderlineColorAlpha;
159     // We use pre-multiplied color to reduce operations.
160     // In here, textureColor and borderlineColorRGB is pre-multiplied color now.
161
162     // Manual blend operation with premultiplied colors.
163     // Final alpha = borderlineColorAlpha + (1.0 - borderlineColorAlpha) * textureColor.a.
164     // (Final rgb * alpha) =  borderlineColorRGB + (1.0 - borderlineColorAlpha) * textureColor.rgb
165     // If preMultipliedAlpha == 1.0, just return vec4(rgb*alpha, alpha)
166     // Else, return vec4((rgb*alpha) / alpha, alpha)
167
168     lowp float finalAlpha = mix(textureColor.a, 1.0, borderlineColorAlpha);
169     lowp vec3  finalMultipliedRGB = borderlineColorRGB + (1.0 - borderlineColorAlpha) * textureColor.rgb;
170     // TODO : Need to find some way without division
171     return vec4(finalMultipliedRGB / finalAlpha, finalAlpha);
172   }
173   return mix(textureColor, vec4(borderlineColorRGB, borderlineColorAlpha), borderlineOpacity);
174 }
175 #endif
176
177 #ifdef IS_REQUIRED_BLUR
178 #elif defined(IS_REQUIRED_ROUNDED_CORNER)
179 mediump float calculateCornerOpacity()
180 {
181   mediump float potential = gPotential;
182
183   // default opacity is 1.0
184   mediump float opacity = 1.0;
185
186   // calculate borderline opacity by potential
187   if(potential > gMaxOutlinePotential)
188   {
189     // potential is out of borderline range. just discard here
190     discard;
191   }
192   else if(potential > gMinOutlinePotential)
193   {
194     opacity = 1.0 - smoothstep(gMinOutlinePotential, gMaxOutlinePotential, potential);
195   }
196   return opacity;
197 }
198 #endif
199
200 #ifdef IS_REQUIRED_BLUR
201 mediump float calculateBlurOpacity()
202 {
203 // Don't use borderline!
204   mediump vec2 v = gDiff;
205   mediump float cy = gRadius + blurRadius;
206   mediump float cr = gRadius + blurRadius;
207
208 #ifdef IS_REQUIRED_ROUNDED_CORNER
209   // This routine make perfect circle. If corner radius is not exist, we don't consider prefect circle.
210   cy = min(cy, min(vRectSize.x, vRectSize.y) - gRadius);
211 #endif
212   v = vec2(min(v.x, v.y), max(v.x, v.y));
213   v = v + cy;
214
215   mediump float potential = 0.0;
216   mediump float alias = min(gRadius, 1.0);
217   mediump float potentialMin = cy + gRadius - blurRadius - alias;
218   mediump float potentialMax = cy + gRadius + blurRadius + alias;
219
220   // move center of circles for reduce defact
221   mediump float cyDiff = min(cy, 0.2 * blurRadius);
222   cy -= cyDiff;
223   cr += cyDiff;
224
225   mediump float diffFromBaseline = cy * v.y - (cy + cr) * v.x;
226
227   if(diffFromBaseline > 0.0)
228   {
229     // out of calculation bound.
230     potential = v.y;
231
232     // for anti-alias when blurRaidus = 0.0
233     mediump float heuristicBaselineScale = max(1.0 , cr * (cr + cy));
234     mediump float potentialDiff = min(alias, diffFromBaseline / heuristicBaselineScale);
235     potentialMin += potentialDiff;
236     potentialMax -= potentialDiff;
237   }
238   else
239   {
240     // get some circle centered (x, x) and radius (r = cr / cy * x)
241     // s.t. point v is on that circle
242     // highest point of that circle is (x, x + r) and potential is x + r
243
244     // solve (v.x - x)^2 + (v.y - x)^2 = (cr / cy * x)^2
245 #ifdef IS_REQUIRED_ROUNDED_CORNER
246     // Note : lowspec HW cannot calculate here. need to reduce numeric error
247     highp float A = (cr * cr - 2.0 * cy * cy);
248     highp float B = cy * (v.x + v.y);
249     highp float V = dot(v,v);
250     highp float D = B * B + A * V;
251     potential = V * (cr + cy) / (sqrt(D) + B);
252 #else
253     // We can simplify this value cause cy = 0.8 * blurRadius, cr = 1.2 * blurRadius
254     // potential = 5.0*(sqrt(4.0*(v.x+v.y)^2 + dot(v,v)) - 2.0*(v.x+v.y));
255     //           = 10.0*(v.x+v.y) * (sqrt(1.0 + (length(v) / (2.0*(v.x+v.y)))^2) - 1.0);
256     //           = 10.0*(v.x+v.y) * (sqrt(1.25 - x + x^2) - 1.0);
257     //          ~= 10.0*(v.x+v.y) * (0.11803399 - 0.44721360x + 0.35777088x^2 - 0.14310x^3 + O(x^5)) (Taylor series)
258     //          ~= -1.0557281 * (v.x + v.y) + 2.236068 * length(v) - ~~~ (here, x <= 0.5 * (1.0 - sqrt(0.5)) < 0.1464467)
259     // Note : This simplify need cause we should use it on lowspec HW.
260     mediump float x = 0.5 * (1.0 - length(v) / (v.x + v.y));
261     potential = -1.0557281 * (v.x + v.y) + 2.236068 * length(v) + 10.0 * (v.x + v.y) * (0.35777088 - 0.14310 * x) * x * x;
262 #endif
263   }
264
265   return 1.0 - smoothstep(potentialMin, potentialMax, potential);
266 }
267 #endif
268
269 void main()
270 {
271   lowp vec4 targetColor = vec4(mixColor, 1.0) * uColor;
272
273 #if defined(IS_REQUIRED_BLUR) || defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
274   // skip most potential calculate for performance
275   if(abs(vPosition.x) < vOptRectSize.x && abs(vPosition.y) < vOptRectSize.y)
276   {
277     OUT_COLOR = targetColor;
278   }
279   else
280   {
281     PreprocessPotential();
282 #endif
283
284 #ifdef IS_REQUIRED_BLUR
285 #elif defined(IS_REQUIRED_BORDERLINE)
286     targetColor = convertBorderlineColor(targetColor);
287 #endif
288     OUT_COLOR = targetColor;
289
290 #ifdef IS_REQUIRED_BLUR
291     mediump float opacity = calculateBlurOpacity();
292     OUT_COLOR.a *= opacity;
293 #elif defined(IS_REQUIRED_ROUNDED_CORNER)
294     mediump float opacity = calculateCornerOpacity();
295     OUT_COLOR.a *= opacity;
296 #endif
297
298 #if defined(IS_REQUIRED_BLUR) || defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
299   }
300 #endif
301 }