1/*
2 * Copyright © 2009 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 *    Junyan He <junyan.he@linux.intel.com>
25 *
26 */
27
28/** @file glamor_gradient.c
29 *
30 * Gradient acceleration implementation
31 */
32
33#include "glamor_priv.h"
34
35#define LINEAR_SMALL_STOPS (6 + 2)
36#define LINEAR_LARGE_STOPS (16 + 2)
37
38#define RADIAL_SMALL_STOPS (6 + 2)
39#define RADIAL_LARGE_STOPS (16 + 2)
40
41static char *
42_glamor_create_getcolor_fs_source(ScreenPtr screen, int stops_count,
43                                  int use_array)
44{
45    char *gradient_fs = NULL;
46
47#define gradient_fs_getcolor\
48	    GLAMOR_DEFAULT_PRECISION\
49	    "uniform int n_stop;\n"\
50	    "uniform float stops[%d];\n"\
51	    "uniform vec4 stop_colors[%d];\n"\
52	    "vec4 get_color(float stop_len)\n"\
53	    "{\n"\
54	    "    int i = 0;\n"\
55	    "    vec4 stop_color_before;\n"\
56	    "    vec4 gradient_color;\n"\
57	    "    float stop_delta;\n"\
58	    "    float percentage; \n"\
59	    "    \n"\
60	    "    if(stop_len < stops[0])\n"\
61	    "        return vec4(0.0, 0.0, 0.0, 0.0); \n"\
62	    "    for(i = 1; i < n_stop; i++) {\n"\
63	    "        if(stop_len < stops[i])\n"\
64	    "            break; \n"\
65	    "    }\n"\
66	    "    if(i == n_stop)\n"\
67	    "        return vec4(0.0, 0.0, 0.0, 0.0); \n"\
68	    "    \n"\
69	    "    stop_color_before = stop_colors[i-1];\n"\
70	    "    stop_delta = stops[i] - stops[i-1];\n"\
71	    "    if(stop_delta > 2.0)\n"\
72	    "        percentage = 0.0;\n" /*For comply with pixman, walker->stepper overflow.*/\
73	    "    else if(stop_delta < 0.000001)\n"\
74	    "        percentage = 0.0;\n"\
75	    "    else \n"\
76	    "        percentage = (stop_len - stops[i-1])/stop_delta;\n"\
77	    "    \n"\
78	    "    gradient_color = stop_color_before;\n"\
79	    "    if(percentage != 0.0)\n"\
80	    "        gradient_color += (stop_colors[i] - gradient_color)*percentage;\n"\
81	    "    return vec4(gradient_color.rgb * gradient_color.a, gradient_color.a);\n"\
82	    "}\n"
83
84    /* Because the array access for shader is very slow, the performance is very low
85       if use array. So use global uniform to replace for it if the number of n_stops is small. */
86    const char *gradient_fs_getcolor_no_array =
87        GLAMOR_DEFAULT_PRECISION
88        "uniform int n_stop;\n"
89        "uniform float stop0;\n"
90        "uniform float stop1;\n"
91        "uniform float stop2;\n"
92        "uniform float stop3;\n"
93        "uniform float stop4;\n"
94        "uniform float stop5;\n"
95        "uniform float stop6;\n"
96        "uniform float stop7;\n"
97        "uniform vec4 stop_color0;\n"
98        "uniform vec4 stop_color1;\n"
99        "uniform vec4 stop_color2;\n"
100        "uniform vec4 stop_color3;\n"
101        "uniform vec4 stop_color4;\n"
102        "uniform vec4 stop_color5;\n"
103        "uniform vec4 stop_color6;\n"
104        "uniform vec4 stop_color7;\n"
105        "\n"
106        "vec4 get_color(float stop_len)\n"
107        "{\n"
108        "    vec4 stop_color_before;\n"
109        "    vec4 stop_color_after;\n"
110        "    vec4 gradient_color;\n"
111        "    float stop_before;\n"
112        "    float stop_delta;\n"
113        "    float percentage; \n"
114        "    \n"
115        "    if((stop_len < stop0) && (n_stop >= 1)) {\n"
116        "        stop_color_before = vec4(0.0, 0.0, 0.0, 0.0);\n"
117        "        stop_delta = 0.0;\n"
118        "    } else if((stop_len < stop1) && (n_stop >= 2)) {\n"
119        "        stop_color_before = stop_color0;\n"
120        "        stop_color_after = stop_color1;\n"
121        "        stop_before = stop0;\n"
122        "        stop_delta = stop1 - stop0;\n"
123        "    } else if((stop_len < stop2) && (n_stop >= 3)) {\n"
124        "        stop_color_before = stop_color1;\n"
125        "        stop_color_after = stop_color2;\n"
126        "        stop_before = stop1;\n"
127        "        stop_delta = stop2 - stop1;\n"
128        "    } else if((stop_len < stop3) && (n_stop >= 4)){\n"
129        "        stop_color_before = stop_color2;\n"
130        "        stop_color_after = stop_color3;\n"
131        "        stop_before = stop2;\n"
132        "        stop_delta = stop3 - stop2;\n"
133        "    } else if((stop_len < stop4) && (n_stop >= 5)){\n"
134        "        stop_color_before = stop_color3;\n"
135        "        stop_color_after = stop_color4;\n"
136        "        stop_before = stop3;\n"
137        "        stop_delta = stop4 - stop3;\n"
138        "    } else if((stop_len < stop5) && (n_stop >= 6)){\n"
139        "        stop_color_before = stop_color4;\n"
140        "        stop_color_after = stop_color5;\n"
141        "        stop_before = stop4;\n"
142        "        stop_delta = stop5 - stop4;\n"
143        "    } else if((stop_len < stop6) && (n_stop >= 7)){\n"
144        "        stop_color_before = stop_color5;\n"
145        "        stop_color_after = stop_color6;\n"
146        "        stop_before = stop5;\n"
147        "        stop_delta = stop6 - stop5;\n"
148        "    } else if((stop_len < stop7) && (n_stop >= 8)){\n"
149        "        stop_color_before = stop_color6;\n"
150        "        stop_color_after = stop_color7;\n"
151        "        stop_before = stop6;\n"
152        "        stop_delta = stop7 - stop6;\n"
153        "    } else {\n"
154        "        stop_color_before = vec4(0.0, 0.0, 0.0, 0.0);\n"
155        "        stop_delta = 0.0;\n"
156        "    }\n"
157        "    if(stop_delta > 2.0)\n"
158        "        percentage = 0.0;\n" //For comply with pixman, walker->stepper overflow.
159        "    else if(stop_delta < 0.000001)\n"
160        "        percentage = 0.0;\n"
161        "    else\n"
162        "        percentage = (stop_len - stop_before)/stop_delta;\n"
163        "    \n"
164        "    gradient_color = stop_color_before;\n"
165        "    if(percentage != 0.0)\n"
166        "        gradient_color += (stop_color_after - gradient_color)*percentage;\n"
167        "    return vec4(gradient_color.rgb * gradient_color.a, gradient_color.a);\n"
168        "}\n";
169
170    if (use_array) {
171        XNFasprintf(&gradient_fs,
172                    gradient_fs_getcolor, stops_count, stops_count);
173        return gradient_fs;
174    }
175    else {
176        return XNFstrdup(gradient_fs_getcolor_no_array);
177    }
178}
179
180static void
181_glamor_create_radial_gradient_program(ScreenPtr screen, int stops_count,
182                                       int dyn_gen)
183{
184    glamor_screen_private *glamor_priv;
185    int index;
186
187    GLint gradient_prog = 0;
188    char *gradient_fs = NULL;
189    GLint fs_prog, vs_prog;
190
191    const char *gradient_vs =
192        GLAMOR_DEFAULT_PRECISION
193        "attribute vec4 v_position;\n"
194        "attribute vec4 v_texcoord;\n"
195        "varying vec2 source_texture;\n"
196        "\n"
197        "void main()\n"
198        "{\n"
199        "    gl_Position = v_position;\n"
200        "    source_texture = v_texcoord.xy;\n"
201        "}\n";
202
203    /*
204     *     Refer to pixman radial gradient.
205     *
206     *     The problem is given the two circles of c1 and c2 with the radius of r1 and
207     *     r1, we need to calculate the t, which is used to do interpolate with stops,
208     *     using the fomula:
209     *     length((1-t)*c1 + t*c2 - p) = (1-t)*r1 + t*r2
210     *     expand the fomula with xy coond, get the following:
211     *     sqrt(sqr((1-t)*c1.x + t*c2.x - p.x) + sqr((1-t)*c1.y + t*c2.y - p.y))
212     *           = (1-t)r1 + t*r2
213     *     <====> At*t- 2Bt + C = 0
214     *     where A = sqr(c2.x - c1.x) + sqr(c2.y - c1.y) - sqr(r2 -r1)
215     *           B = (p.x - c1.x)*(c2.x - c1.x) + (p.y - c1.y)*(c2.y - c1.y) + r1*(r2 -r1)
216     *           C = sqr(p.x - c1.x) + sqr(p.y - c1.y) - r1*r1
217     *
218     *     solve the fomula and we get the result of
219     *     t = (B + sqrt(B*B - A*C)) / A  or
220     *     t = (B - sqrt(B*B - A*C)) / A  (quadratic equation have two solutions)
221     *
222     *     The solution we are going to prefer is the bigger one, unless the
223     *     radius associated to it is negative (or it falls outside the valid t range)
224     */
225
226#define gradient_radial_fs_template\
227	    GLAMOR_DEFAULT_PRECISION\
228	    "uniform mat3 transform_mat;\n"\
229	    "uniform int repeat_type;\n"\
230	    "uniform float A_value;\n"\
231	    "uniform vec2 c1;\n"\
232	    "uniform float r1;\n"\
233	    "uniform vec2 c2;\n"\
234	    "uniform float r2;\n"\
235	    "varying vec2 source_texture;\n"\
236	    "\n"\
237	    "vec4 get_color(float stop_len);\n"\
238	    "\n"\
239	    "int t_invalid;\n"\
240	    "\n"\
241	    "float get_stop_len()\n"\
242	    "{\n"\
243	    "    float t = 0.0;\n"\
244	    "    float sqrt_value;\n"\
245	    "    t_invalid = 0;\n"\
246	    "    \n"\
247	    "    vec3 tmp = vec3(source_texture.x, source_texture.y, 1.0);\n"\
248	    "    vec3 source_texture_trans = transform_mat * tmp;\n"\
249	    "    source_texture_trans.xy = source_texture_trans.xy/source_texture_trans.z;\n"\
250	    "    float B_value = (source_texture_trans.x - c1.x) * (c2.x - c1.x)\n"\
251	    "                     + (source_texture_trans.y - c1.y) * (c2.y - c1.y)\n"\
252	    "                     + r1 * (r2 - r1);\n"\
253	    "    float C_value = (source_texture_trans.x - c1.x) * (source_texture_trans.x - c1.x)\n"\
254	    "                     + (source_texture_trans.y - c1.y) * (source_texture_trans.y - c1.y)\n"\
255	    "                     - r1*r1;\n"\
256	    "    if(abs(A_value) < 0.00001) {\n"\
257	    "        if(B_value == 0.0) {\n"\
258	    "            t_invalid = 1;\n"\
259	    "            return t;\n"\
260	    "        }\n"\
261	    "        t = 0.5 * C_value / B_value;"\
262	    "    } else {\n"\
263	    "        sqrt_value = B_value * B_value - A_value * C_value;\n"\
264	    "        if(sqrt_value < 0.0) {\n"\
265	    "            t_invalid = 1;\n"\
266	    "            return t;\n"\
267	    "        }\n"\
268	    "        sqrt_value = sqrt(sqrt_value);\n"\
269	    "        t = (B_value + sqrt_value) / A_value;\n"\
270	    "    }\n"\
271	    "    if(repeat_type == %d) {\n" /* RepeatNone case. */\
272	    "        if((t <= 0.0) || (t > 1.0))\n"\
273	    /*           try another if first one invalid*/\
274	    "            t = (B_value - sqrt_value) / A_value;\n"\
275	    "        \n"\
276	    "        if((t <= 0.0) || (t > 1.0)) {\n" /*still invalid, return.*/\
277	    "            t_invalid = 1;\n"\
278	    "            return t;\n"\
279	    "        }\n"\
280	    "    } else {\n"\
281	    "        if(t * (r2 - r1) <= -1.0 * r1)\n"\
282	    /*           try another if first one invalid*/\
283	    "            t = (B_value - sqrt_value) / A_value;\n"\
284	    "        \n"\
285	    "        if(t * (r2 -r1) <= -1.0 * r1) {\n" /*still invalid, return.*/\
286	    "            t_invalid = 1;\n"\
287	    "            return t;\n"\
288	    "        }\n"\
289	    "    }\n"\
290	    "    \n"\
291	    "    if(repeat_type == %d){\n" /* repeat normal*/\
292	    "        t = fract(t);\n"\
293	    "    }\n"\
294	    "    \n"\
295	    "    if(repeat_type == %d) {\n" /* repeat reflect*/\
296	    "        t = abs(fract(t * 0.5 + 0.5) * 2.0 - 1.0);\n"\
297	    "    }\n"\
298	    "    \n"\
299	    "    return t;\n"\
300	    "}\n"\
301	    "\n"\
302	    "void main()\n"\
303	    "{\n"\
304	    "    float stop_len = get_stop_len();\n"\
305	    "    if(t_invalid == 1) {\n"\
306	    "        gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);\n"\
307	    "    } else {\n"\
308	    "        gl_FragColor = get_color(stop_len);\n"\
309	    "    }\n"\
310	    "}\n"\
311	    "\n"\
312            "%s\n" /* fs_getcolor_source */
313    char *fs_getcolor_source;
314
315    glamor_priv = glamor_get_screen_private(screen);
316
317    if ((glamor_priv->radial_max_nstops >= stops_count) && (dyn_gen)) {
318        /* Very Good, not to generate again. */
319        return;
320    }
321
322    glamor_make_current(glamor_priv);
323
324    if (dyn_gen && glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][2]) {
325        glDeleteProgram(glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][2]);
326        glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][2] = 0;
327    }
328
329    gradient_prog = glCreateProgram();
330
331    vs_prog = glamor_compile_glsl_prog(GL_VERTEX_SHADER, gradient_vs);
332
333    fs_getcolor_source =
334        _glamor_create_getcolor_fs_source(screen, stops_count,
335                                          (stops_count > 0));
336
337    XNFasprintf(&gradient_fs,
338                gradient_radial_fs_template,
339                PIXMAN_REPEAT_NONE, PIXMAN_REPEAT_NORMAL,
340                PIXMAN_REPEAT_REFLECT,
341                fs_getcolor_source);
342
343    fs_prog = glamor_compile_glsl_prog(GL_FRAGMENT_SHADER, gradient_fs);
344
345    free(gradient_fs);
346    free(fs_getcolor_source);
347
348    glAttachShader(gradient_prog, vs_prog);
349    glAttachShader(gradient_prog, fs_prog);
350    glDeleteShader(vs_prog);
351    glDeleteShader(fs_prog);
352
353    glBindAttribLocation(gradient_prog, GLAMOR_VERTEX_POS, "v_position");
354    glBindAttribLocation(gradient_prog, GLAMOR_VERTEX_SOURCE, "v_texcoord");
355
356    glamor_link_glsl_prog(screen, gradient_prog, "radial gradient");
357
358    if (dyn_gen) {
359        index = 2;
360        glamor_priv->radial_max_nstops = stops_count;
361    }
362    else if (stops_count) {
363        index = 1;
364    }
365    else {
366        index = 0;
367    }
368
369    glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][index] = gradient_prog;
370}
371
372static void
373_glamor_create_linear_gradient_program(ScreenPtr screen, int stops_count,
374                                       int dyn_gen)
375{
376    glamor_screen_private *glamor_priv;
377
378    int index = 0;
379    GLint gradient_prog = 0;
380    char *gradient_fs = NULL;
381    GLint fs_prog, vs_prog;
382
383    const char *gradient_vs =
384        GLAMOR_DEFAULT_PRECISION
385        "attribute vec4 v_position;\n"
386        "attribute vec4 v_texcoord;\n"
387        "varying vec2 source_texture;\n"
388        "\n"
389        "void main()\n"
390        "{\n"
391        "    gl_Position = v_position;\n"
392        "    source_texture = v_texcoord.xy;\n"
393        "}\n";
394
395    /*
396     *                                      |
397     *                                      |\
398     *                                      | \
399     *                                      |  \
400     *                                      |   \
401     *                                      |\   \
402     *                                      | \   \
403     *     cos_val =                        |\ p1d \   /
404     *      sqrt(1/(slope*slope+1.0))  ------>\ \   \ /
405     *                                      |  \ \   \
406     *                                      |   \ \ / \
407     *                                      |    \ *Pt1\
408     *         *p1                          |     \     \     *P
409     *          \                           |    / \     \   /
410     *           \                          |   /   \     \ /
411     *            \                         |       pd     \
412     *             \                        |         \   / \
413     *            p2*                       |          \ /   \       /
414     *        slope = (p2.y - p1.y) /       |           /     p2d   /
415     *                    (p2.x - p1.x)     |          /       \   /
416     *                                      |         /         \ /
417     *                                      |        /           /
418     *                                      |       /           /
419     *                                      |      /           *Pt2
420     *                                      |                 /
421     *                                      |                /
422     *                                      |               /
423     *                                      |              /
424     *                                      |             /
425     *                               -------+---------------------------------
426     *                                     O|
427     *                                      |
428     *                                      |
429     *
430     *      step 1: compute the distance of p, pt1 and pt2 in the slope direction.
431     *              Calculate the distance on Y axis first and multiply cos_val to
432     *              get the value on slope direction(pd, p1d and p2d represent the
433     *              distance of p, pt1, and pt2 respectively).
434     *
435     *      step 2: calculate the percentage of (pd - p1d)/(p2d - p1d).
436     *              If (pd - p1d) > (p2d - p1d) or < 0, then sub or add (p2d - p1d)
437     *              to make it in the range of [0, (p2d - p1d)].
438     *
439     *      step 3: compare the percentage to every stop and find the stpos just
440     *              before and after it. Use the interpolation fomula to compute RGBA.
441     */
442
443#define gradient_fs_template	\
444	    GLAMOR_DEFAULT_PRECISION\
445	    "uniform mat3 transform_mat;\n"\
446	    "uniform int repeat_type;\n"\
447	    "uniform int hor_ver;\n"\
448	    "uniform float pt_slope;\n"\
449	    "uniform float cos_val;\n"\
450	    "uniform float p1_distance;\n"\
451	    "uniform float pt_distance;\n"\
452	    "varying vec2 source_texture;\n"\
453	    "\n"\
454	    "vec4 get_color(float stop_len);\n"\
455	    "\n"\
456	    "float get_stop_len()\n"\
457	    "{\n"\
458	    "    vec3 tmp = vec3(source_texture.x, source_texture.y, 1.0);\n"\
459	    "    float distance;\n"\
460	    "    float _p1_distance;\n"\
461	    "    float _pt_distance;\n"\
462	    "    float y_dist;\n"\
463	    "    vec3 source_texture_trans = transform_mat * tmp;\n"\
464	    "    \n"\
465	    "    if(hor_ver == 0) { \n" /*Normal case.*/\
466	    "        y_dist = source_texture_trans.y - source_texture_trans.x*pt_slope;\n"\
467	    "        distance = y_dist * cos_val;\n"\
468	    "        _p1_distance = p1_distance * source_texture_trans.z;\n"\
469	    "        _pt_distance = pt_distance * source_texture_trans.z;\n"\
470	    "        \n"\
471	    "    } else if (hor_ver == 1) {\n"/*horizontal case.*/\
472	    "        distance = source_texture_trans.x;\n"\
473	    "        _p1_distance = p1_distance * source_texture_trans.z;\n"\
474	    "        _pt_distance = pt_distance * source_texture_trans.z;\n"\
475	    "    } \n"\
476	    "    \n"\
477	    "    distance = (distance - _p1_distance) / _pt_distance;\n"\
478	    "    \n"\
479	    "    if(repeat_type == %d){\n" /* repeat normal*/\
480	    "        distance = fract(distance);\n"\
481	    "    }\n"\
482	    "    \n"\
483	    "    if(repeat_type == %d) {\n" /* repeat reflect*/\
484	    "        distance = abs(fract(distance * 0.5 + 0.5) * 2.0 - 1.0);\n"\
485	    "    }\n"\
486	    "    \n"\
487	    "    return distance;\n"\
488	    "}\n"\
489	    "\n"\
490	    "void main()\n"\
491	    "{\n"\
492	    "    float stop_len = get_stop_len();\n"\
493	    "    gl_FragColor = get_color(stop_len);\n"\
494	    "}\n"\
495	    "\n"\
496            "%s" /* fs_getcolor_source */
497    char *fs_getcolor_source;
498
499    glamor_priv = glamor_get_screen_private(screen);
500
501    if ((glamor_priv->linear_max_nstops >= stops_count) && (dyn_gen)) {
502        /* Very Good, not to generate again. */
503        return;
504    }
505
506    glamor_make_current(glamor_priv);
507    if (dyn_gen && glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][2]) {
508        glDeleteProgram(glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][2]);
509        glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][2] = 0;
510    }
511
512    gradient_prog = glCreateProgram();
513
514    vs_prog = glamor_compile_glsl_prog(GL_VERTEX_SHADER, gradient_vs);
515
516    fs_getcolor_source =
517        _glamor_create_getcolor_fs_source(screen, stops_count, stops_count > 0);
518
519    XNFasprintf(&gradient_fs,
520                gradient_fs_template,
521                PIXMAN_REPEAT_NORMAL, PIXMAN_REPEAT_REFLECT,
522                fs_getcolor_source);
523
524    fs_prog = glamor_compile_glsl_prog(GL_FRAGMENT_SHADER, gradient_fs);
525    free(gradient_fs);
526    free(fs_getcolor_source);
527
528    glAttachShader(gradient_prog, vs_prog);
529    glAttachShader(gradient_prog, fs_prog);
530    glDeleteShader(vs_prog);
531    glDeleteShader(fs_prog);
532
533    glBindAttribLocation(gradient_prog, GLAMOR_VERTEX_POS, "v_position");
534    glBindAttribLocation(gradient_prog, GLAMOR_VERTEX_SOURCE, "v_texcoord");
535
536    glamor_link_glsl_prog(screen, gradient_prog, "linear gradient");
537
538    if (dyn_gen) {
539        index = 2;
540        glamor_priv->linear_max_nstops = stops_count;
541    }
542    else if (stops_count) {
543        index = 1;
544    }
545    else {
546        index = 0;
547    }
548
549    glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][index] = gradient_prog;
550}
551
552void
553glamor_init_gradient_shader(ScreenPtr screen)
554{
555    glamor_screen_private *glamor_priv;
556    int i;
557
558    glamor_priv = glamor_get_screen_private(screen);
559
560    for (i = 0; i < 3; i++) {
561        glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][i] = 0;
562        glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][i] = 0;
563    }
564    glamor_priv->linear_max_nstops = 0;
565    glamor_priv->radial_max_nstops = 0;
566
567    _glamor_create_linear_gradient_program(screen, 0, 0);
568    _glamor_create_linear_gradient_program(screen, LINEAR_LARGE_STOPS, 0);
569
570    _glamor_create_radial_gradient_program(screen, 0, 0);
571    _glamor_create_radial_gradient_program(screen, RADIAL_LARGE_STOPS, 0);
572}
573
574static void
575_glamor_gradient_convert_trans_matrix(PictTransform *from, float to[3][3],
576                                      int width, int height, int normalize)
577{
578    /*
579     * Because in the shader program, we normalize all the pixel cood to [0, 1],
580     * so with the transform matrix, the correct logic should be:
581     * v_s = A*T*v
582     * v_s: point vector in shader after normalized.
583     * A: The transition matrix from   width X height --> 1.0 X 1.0
584     * T: The transform matrix.
585     * v: point vector in width X height space.
586     *
587     * result is OK if we use this fomula. But for every point in width X height space,
588     * we can just use their normalized point vector in shader, namely we can just
589     * use the result of A*v in shader. So we have no chance to insert T in A*v.
590     * We can just convert v_s = A*T*v to v_s = A*T*inv(A)*A*v, where inv(A) is the
591     * inverse matrix of A. Now, v_s = (A*T*inv(A)) * (A*v)
592     * So, to get the correct v_s, we need to cacula1 the matrix: (A*T*inv(A)), and
593     * we name this matrix T_s.
594     *
595     * Firstly, because A is for the scale conversion, we find
596     *      --         --
597     *      |1/w  0   0 |
598     * A =  | 0  1/h  0 |
599     *      | 0   0  1.0|
600     *      --         --
601     * so T_s = A*T*inv(a) and result
602     *
603     *       --                      --
604     *       | t11      h*t12/w  t13/w|
605     * T_s = | w*t21/h  t22      t23/h|
606     *       | w*t31    h*t32    t33  |
607     *       --                      --
608     */
609
610    to[0][0] = (float) pixman_fixed_to_double(from->matrix[0][0]);
611    to[0][1] = (float) pixman_fixed_to_double(from->matrix[0][1])
612        * (normalize ? (((float) height) / ((float) width)) : 1.0);
613    to[0][2] = (float) pixman_fixed_to_double(from->matrix[0][2])
614        / (normalize ? ((float) width) : 1.0);
615
616    to[1][0] = (float) pixman_fixed_to_double(from->matrix[1][0])
617        * (normalize ? (((float) width) / ((float) height)) : 1.0);
618    to[1][1] = (float) pixman_fixed_to_double(from->matrix[1][1]);
619    to[1][2] = (float) pixman_fixed_to_double(from->matrix[1][2])
620        / (normalize ? ((float) height) : 1.0);
621
622    to[2][0] = (float) pixman_fixed_to_double(from->matrix[2][0])
623        * (normalize ? ((float) width) : 1.0);
624    to[2][1] = (float) pixman_fixed_to_double(from->matrix[2][1])
625        * (normalize ? ((float) height) : 1.0);
626    to[2][2] = (float) pixman_fixed_to_double(from->matrix[2][2]);
627
628    DEBUGF("the transform matrix is:\n%f\t%f\t%f\n%f\t%f\t%f\n%f\t%f\t%f\n",
629           to[0][0], to[0][1], to[0][2],
630           to[1][0], to[1][1], to[1][2], to[2][0], to[2][1], to[2][2]);
631}
632
633static int
634_glamor_gradient_set_pixmap_destination(ScreenPtr screen,
635                                        glamor_screen_private *glamor_priv,
636                                        PicturePtr dst_picture,
637                                        GLfloat *xscale, GLfloat *yscale,
638                                        int x_source, int y_source,
639                                        int tex_normalize)
640{
641    glamor_pixmap_private *pixmap_priv;
642    PixmapPtr pixmap = NULL;
643    GLfloat *v;
644    char *vbo_offset;
645
646    pixmap = glamor_get_drawable_pixmap(dst_picture->pDrawable);
647    pixmap_priv = glamor_get_pixmap_private(pixmap);
648
649    if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) {     /* should always have here. */
650        return 0;
651    }
652
653    glamor_set_destination_pixmap_priv_nc(glamor_priv, pixmap, pixmap_priv);
654
655    pixmap_priv_get_dest_scale(pixmap, pixmap_priv, xscale, yscale);
656
657    DEBUGF("xscale = %f, yscale = %f,"
658           " x_source = %d, y_source = %d, width = %d, height = %d\n",
659           *xscale, *yscale, x_source, y_source,
660           dst_picture->pDrawable->width, dst_picture->pDrawable->height);
661
662    v = glamor_get_vbo_space(screen, 16 * sizeof(GLfloat), &vbo_offset);
663
664    glamor_set_normalize_vcoords_tri_strip(*xscale, *yscale,
665                                           0, 0,
666                                           (INT16) (dst_picture->pDrawable->
667                                                    width),
668                                           (INT16) (dst_picture->pDrawable->
669                                                    height),
670                                           v);
671
672    if (tex_normalize) {
673        glamor_set_normalize_tcoords_tri_stripe(*xscale, *yscale,
674                                                x_source, y_source,
675                                                (INT16) (dst_picture->
676                                                         pDrawable->width +
677                                                         x_source),
678                                                (INT16) (dst_picture->
679                                                         pDrawable->height +
680                                                         y_source),
681                                                &v[8]);
682    }
683    else {
684        glamor_set_tcoords_tri_strip(x_source, y_source,
685                                     (INT16) (dst_picture->pDrawable->width) +
686                                     x_source,
687                                     (INT16) (dst_picture->pDrawable->height) +
688                                     y_source,
689                                     &v[8]);
690    }
691
692    DEBUGF("vertices --> leftup : %f X %f, rightup: %f X %f,"
693           "rightbottom: %f X %f, leftbottom : %f X %f\n",
694           v[0], v[1], v[2], v[3],
695           v[4], v[5], v[6], v[7]);
696    DEBUGF("tex_vertices --> leftup : %f X %f, rightup: %f X %f,"
697           "rightbottom: %f X %f, leftbottom : %f X %f\n",
698           v[8], v[9], v[10], v[11],
699           v[12], v[13], v[14], v[15]);
700
701    glamor_make_current(glamor_priv);
702
703    glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT,
704                          GL_FALSE, 0, vbo_offset);
705    glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_FLOAT,
706                          GL_FALSE, 0, vbo_offset + 8 * sizeof(GLfloat));
707
708    glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
709    glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE);
710
711    glamor_put_vbo_space(screen);
712    return 1;
713}
714
715static int
716_glamor_gradient_set_stops(PicturePtr src_picture, PictGradient *pgradient,
717                           GLfloat *stop_colors, GLfloat *n_stops)
718{
719    int i;
720    int count = 1;
721
722    for (i = 0; i < pgradient->nstops; i++) {
723        stop_colors[count * 4] =
724            pixman_fixed_to_double(pgradient->stops[i].color.red);
725        stop_colors[count * 4 + 1] =
726            pixman_fixed_to_double(pgradient->stops[i].color.green);
727        stop_colors[count * 4 + 2] =
728            pixman_fixed_to_double(pgradient->stops[i].color.blue);
729        stop_colors[count * 4 + 3] =
730            pixman_fixed_to_double(pgradient->stops[i].color.alpha);
731
732        n_stops[count] =
733            (GLfloat) pixman_fixed_to_double(pgradient->stops[i].x);
734        count++;
735    }
736
737    /* for the end stop. */
738    count++;
739
740    switch (src_picture->repeatType) {
741#define REPEAT_FILL_STOPS(m, n) \
742			stop_colors[(m)*4 + 0] = stop_colors[(n)*4 + 0]; \
743			stop_colors[(m)*4 + 1] = stop_colors[(n)*4 + 1]; \
744			stop_colors[(m)*4 + 2] = stop_colors[(n)*4 + 2]; \
745			stop_colors[(m)*4 + 3] = stop_colors[(n)*4 + 3];
746
747    default:
748    case PIXMAN_REPEAT_NONE:
749        stop_colors[0] = 0.0;   //R
750        stop_colors[1] = 0.0;   //G
751        stop_colors[2] = 0.0;   //B
752        stop_colors[3] = 0.0;   //Alpha
753        n_stops[0] = n_stops[1];
754
755        stop_colors[0 + (count - 1) * 4] = 0.0; //R
756        stop_colors[1 + (count - 1) * 4] = 0.0; //G
757        stop_colors[2 + (count - 1) * 4] = 0.0; //B
758        stop_colors[3 + (count - 1) * 4] = 0.0; //Alpha
759        n_stops[count - 1] = n_stops[count - 2];
760        break;
761    case PIXMAN_REPEAT_NORMAL:
762        REPEAT_FILL_STOPS(0, count - 2);
763        n_stops[0] = n_stops[count - 2] - 1.0;
764
765        REPEAT_FILL_STOPS(count - 1, 1);
766        n_stops[count - 1] = n_stops[1] + 1.0;
767        break;
768    case PIXMAN_REPEAT_REFLECT:
769        REPEAT_FILL_STOPS(0, 1);
770        n_stops[0] = -n_stops[1];
771
772        REPEAT_FILL_STOPS(count - 1, count - 2);
773        n_stops[count - 1] = 1.0 + 1.0 - n_stops[count - 2];
774        break;
775    case PIXMAN_REPEAT_PAD:
776        REPEAT_FILL_STOPS(0, 1);
777        n_stops[0] = -(float) INT_MAX;
778
779        REPEAT_FILL_STOPS(count - 1, count - 2);
780        n_stops[count - 1] = (float) INT_MAX;
781        break;
782#undef REPEAT_FILL_STOPS
783    }
784
785    for (i = 0; i < count; i++) {
786        DEBUGF("n_stops[%d] = %f, color = r:%f g:%f b:%f a:%f\n",
787               i, n_stops[i],
788               stop_colors[i * 4], stop_colors[i * 4 + 1],
789               stop_colors[i * 4 + 2], stop_colors[i * 4 + 3]);
790    }
791
792    return count;
793}
794
795PicturePtr
796glamor_generate_radial_gradient_picture(ScreenPtr screen,
797                                        PicturePtr src_picture,
798                                        int x_source, int y_source,
799                                        int width, int height,
800                                        PictFormatShort format)
801{
802    glamor_screen_private *glamor_priv;
803    PicturePtr dst_picture = NULL;
804    PixmapPtr pixmap = NULL;
805    GLint gradient_prog = 0;
806    int error;
807    int stops_count = 0;
808    int count = 0;
809    GLfloat *stop_colors = NULL;
810    GLfloat *n_stops = NULL;
811    GLfloat xscale, yscale;
812    float transform_mat[3][3];
813    static const float identity_mat[3][3] = { {1.0, 0.0, 0.0},
814    {0.0, 1.0, 0.0},
815    {0.0, 0.0, 1.0}
816    };
817    GLfloat stop_colors_st[RADIAL_SMALL_STOPS * 4];
818    GLfloat n_stops_st[RADIAL_SMALL_STOPS];
819    GLfloat A_value;
820    GLfloat cxy[4];
821    float c1x, c1y, c2x, c2y, r1, r2;
822
823    GLint transform_mat_uniform_location = 0;
824    GLint repeat_type_uniform_location = 0;
825    GLint n_stop_uniform_location = 0;
826    GLint stops_uniform_location = 0;
827    GLint stop_colors_uniform_location = 0;
828    GLint stop0_uniform_location = 0;
829    GLint stop1_uniform_location = 0;
830    GLint stop2_uniform_location = 0;
831    GLint stop3_uniform_location = 0;
832    GLint stop4_uniform_location = 0;
833    GLint stop5_uniform_location = 0;
834    GLint stop6_uniform_location = 0;
835    GLint stop7_uniform_location = 0;
836    GLint stop_color0_uniform_location = 0;
837    GLint stop_color1_uniform_location = 0;
838    GLint stop_color2_uniform_location = 0;
839    GLint stop_color3_uniform_location = 0;
840    GLint stop_color4_uniform_location = 0;
841    GLint stop_color5_uniform_location = 0;
842    GLint stop_color6_uniform_location = 0;
843    GLint stop_color7_uniform_location = 0;
844    GLint A_value_uniform_location = 0;
845    GLint c1_uniform_location = 0;
846    GLint r1_uniform_location = 0;
847    GLint c2_uniform_location = 0;
848    GLint r2_uniform_location = 0;
849
850    glamor_priv = glamor_get_screen_private(screen);
851    glamor_make_current(glamor_priv);
852
853    /* Create a pixmap with VBO. */
854    pixmap = glamor_create_pixmap(screen,
855                                  width, height,
856                                  PIXMAN_FORMAT_DEPTH(format), 0);
857    if (!pixmap)
858        goto GRADIENT_FAIL;
859
860    dst_picture = CreatePicture(0, &pixmap->drawable,
861                                PictureMatchFormat(screen,
862                                                   PIXMAN_FORMAT_DEPTH(format),
863                                                   format), 0, 0, serverClient,
864                                &error);
865
866    /* Release the reference, picture will hold the last one. */
867    glamor_destroy_pixmap(pixmap);
868
869    if (!dst_picture)
870        goto GRADIENT_FAIL;
871
872    ValidatePicture(dst_picture);
873
874    stops_count = src_picture->pSourcePict->radial.nstops + 2;
875
876    /* Because the max value of nstops is unknown, so create a program
877       when nstops > LINEAR_LARGE_STOPS. */
878    if (stops_count <= RADIAL_SMALL_STOPS) {
879        gradient_prog = glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][0];
880    }
881    else if (stops_count <= RADIAL_LARGE_STOPS) {
882        gradient_prog = glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][1];
883    }
884    else {
885        _glamor_create_radial_gradient_program(screen,
886                                               src_picture->pSourcePict->linear.
887                                               nstops + 2, 1);
888        gradient_prog = glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][2];
889    }
890
891    /* Bind all the uniform vars . */
892    transform_mat_uniform_location = glGetUniformLocation(gradient_prog,
893                                                          "transform_mat");
894    repeat_type_uniform_location = glGetUniformLocation(gradient_prog,
895                                                        "repeat_type");
896    n_stop_uniform_location = glGetUniformLocation(gradient_prog, "n_stop");
897    A_value_uniform_location = glGetUniformLocation(gradient_prog, "A_value");
898    c1_uniform_location = glGetUniformLocation(gradient_prog, "c1");
899    r1_uniform_location = glGetUniformLocation(gradient_prog, "r1");
900    c2_uniform_location = glGetUniformLocation(gradient_prog, "c2");
901    r2_uniform_location = glGetUniformLocation(gradient_prog, "r2");
902
903    if (src_picture->pSourcePict->radial.nstops + 2 <= RADIAL_SMALL_STOPS) {
904        stop0_uniform_location =
905            glGetUniformLocation(gradient_prog, "stop0");
906        stop1_uniform_location =
907            glGetUniformLocation(gradient_prog, "stop1");
908        stop2_uniform_location =
909            glGetUniformLocation(gradient_prog, "stop2");
910        stop3_uniform_location =
911            glGetUniformLocation(gradient_prog, "stop3");
912        stop4_uniform_location =
913            glGetUniformLocation(gradient_prog, "stop4");
914        stop5_uniform_location =
915            glGetUniformLocation(gradient_prog, "stop5");
916        stop6_uniform_location =
917            glGetUniformLocation(gradient_prog, "stop6");
918        stop7_uniform_location =
919            glGetUniformLocation(gradient_prog, "stop7");
920
921        stop_color0_uniform_location =
922            glGetUniformLocation(gradient_prog, "stop_color0");
923        stop_color1_uniform_location =
924            glGetUniformLocation(gradient_prog, "stop_color1");
925        stop_color2_uniform_location =
926            glGetUniformLocation(gradient_prog, "stop_color2");
927        stop_color3_uniform_location =
928            glGetUniformLocation(gradient_prog, "stop_color3");
929        stop_color4_uniform_location =
930            glGetUniformLocation(gradient_prog, "stop_color4");
931        stop_color5_uniform_location =
932            glGetUniformLocation(gradient_prog, "stop_color5");
933        stop_color6_uniform_location =
934            glGetUniformLocation(gradient_prog, "stop_color6");
935        stop_color7_uniform_location =
936            glGetUniformLocation(gradient_prog, "stop_color7");
937    }
938    else {
939        stops_uniform_location =
940            glGetUniformLocation(gradient_prog, "stops");
941        stop_colors_uniform_location =
942            glGetUniformLocation(gradient_prog, "stop_colors");
943    }
944
945    glUseProgram(gradient_prog);
946
947    glUniform1i(repeat_type_uniform_location, src_picture->repeatType);
948
949    if (src_picture->transform) {
950        _glamor_gradient_convert_trans_matrix(src_picture->transform,
951                                              transform_mat, width, height, 0);
952        glUniformMatrix3fv(transform_mat_uniform_location,
953                           1, 1, &transform_mat[0][0]);
954    }
955    else {
956        glUniformMatrix3fv(transform_mat_uniform_location,
957                           1, 1, &identity_mat[0][0]);
958    }
959
960    if (!_glamor_gradient_set_pixmap_destination
961        (screen, glamor_priv, dst_picture, &xscale, &yscale, x_source, y_source,
962         0))
963        goto GRADIENT_FAIL;
964
965    glamor_set_alu(screen, GXcopy);
966
967    /* Set all the stops and colors to shader. */
968    if (stops_count > RADIAL_SMALL_STOPS) {
969        stop_colors = xallocarray(stops_count, 4 * sizeof(float));
970        if (stop_colors == NULL) {
971            ErrorF("Failed to allocate stop_colors memory.\n");
972            goto GRADIENT_FAIL;
973        }
974
975        n_stops = xallocarray(stops_count, sizeof(float));
976        if (n_stops == NULL) {
977            ErrorF("Failed to allocate n_stops memory.\n");
978            goto GRADIENT_FAIL;
979        }
980    }
981    else {
982        stop_colors = stop_colors_st;
983        n_stops = n_stops_st;
984    }
985
986    count =
987        _glamor_gradient_set_stops(src_picture,
988                                   &src_picture->pSourcePict->gradient,
989                                   stop_colors, n_stops);
990
991    if (src_picture->pSourcePict->linear.nstops + 2 <= RADIAL_SMALL_STOPS) {
992        int j = 0;
993
994        glUniform4f(stop_color0_uniform_location,
995                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
996                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
997        j++;
998        glUniform4f(stop_color1_uniform_location,
999                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1000                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1001        j++;
1002        glUniform4f(stop_color2_uniform_location,
1003                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1004                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1005        j++;
1006        glUniform4f(stop_color3_uniform_location,
1007                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1008                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1009        j++;
1010        glUniform4f(stop_color4_uniform_location,
1011                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1012                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1013        j++;
1014        glUniform4f(stop_color5_uniform_location,
1015                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1016                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1017        j++;
1018        glUniform4f(stop_color6_uniform_location,
1019                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1020                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1021        j++;
1022        glUniform4f(stop_color7_uniform_location,
1023                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1024                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1025
1026        j = 0;
1027        glUniform1f(stop0_uniform_location, n_stops[j++]);
1028        glUniform1f(stop1_uniform_location, n_stops[j++]);
1029        glUniform1f(stop2_uniform_location, n_stops[j++]);
1030        glUniform1f(stop3_uniform_location, n_stops[j++]);
1031        glUniform1f(stop4_uniform_location, n_stops[j++]);
1032        glUniform1f(stop5_uniform_location, n_stops[j++]);
1033        glUniform1f(stop6_uniform_location, n_stops[j++]);
1034        glUniform1f(stop7_uniform_location, n_stops[j++]);
1035        glUniform1i(n_stop_uniform_location, count);
1036    }
1037    else {
1038        glUniform4fv(stop_colors_uniform_location, count, stop_colors);
1039        glUniform1fv(stops_uniform_location, count, n_stops);
1040        glUniform1i(n_stop_uniform_location, count);
1041    }
1042
1043    c1x = (float) pixman_fixed_to_double(src_picture->pSourcePict->radial.c1.x);
1044    c1y = (float) pixman_fixed_to_double(src_picture->pSourcePict->radial.c1.y);
1045    c2x = (float) pixman_fixed_to_double(src_picture->pSourcePict->radial.c2.x);
1046    c2y = (float) pixman_fixed_to_double(src_picture->pSourcePict->radial.c2.y);
1047
1048    r1 = (float) pixman_fixed_to_double(src_picture->pSourcePict->radial.c1.
1049                                        radius);
1050    r2 = (float) pixman_fixed_to_double(src_picture->pSourcePict->radial.c2.
1051                                        radius);
1052
1053    glamor_set_circle_centre(width, height, c1x, c1y, cxy);
1054    glUniform2fv(c1_uniform_location, 1, cxy);
1055    glUniform1f(r1_uniform_location, r1);
1056
1057    glamor_set_circle_centre(width, height, c2x, c2y, cxy);
1058    glUniform2fv(c2_uniform_location, 1, cxy);
1059    glUniform1f(r2_uniform_location, r2);
1060
1061    A_value =
1062        (c2x - c1x) * (c2x - c1x) + (c2y - c1y) * (c2y - c1y) - (r2 -
1063                                                                 r1) * (r2 -
1064                                                                        r1);
1065    glUniform1f(A_value_uniform_location, A_value);
1066
1067    DEBUGF("C1:(%f, %f) R1:%f\nC2:(%f, %f) R2:%f\nA = %f\n",
1068           c1x, c1y, r1, c2x, c2y, r2, A_value);
1069
1070    /* Now rendering. */
1071    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1072
1073    /* Do the clear logic. */
1074    if (stops_count > RADIAL_SMALL_STOPS) {
1075        free(n_stops);
1076        free(stop_colors);
1077    }
1078
1079    glDisableVertexAttribArray(GLAMOR_VERTEX_POS);
1080    glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE);
1081
1082    return dst_picture;
1083
1084 GRADIENT_FAIL:
1085    if (dst_picture) {
1086        FreePicture(dst_picture, 0);
1087    }
1088
1089    if (stops_count > RADIAL_SMALL_STOPS) {
1090        if (n_stops)
1091            free(n_stops);
1092        if (stop_colors)
1093            free(stop_colors);
1094    }
1095
1096    glDisableVertexAttribArray(GLAMOR_VERTEX_POS);
1097    glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE);
1098    return NULL;
1099}
1100
1101PicturePtr
1102glamor_generate_linear_gradient_picture(ScreenPtr screen,
1103                                        PicturePtr src_picture,
1104                                        int x_source, int y_source,
1105                                        int width, int height,
1106                                        PictFormatShort format)
1107{
1108    glamor_screen_private *glamor_priv;
1109    PicturePtr dst_picture = NULL;
1110    PixmapPtr pixmap = NULL;
1111    GLint gradient_prog = 0;
1112    int error;
1113    float pt_distance;
1114    float p1_distance;
1115    GLfloat cos_val;
1116    int stops_count = 0;
1117    GLfloat *stop_colors = NULL;
1118    GLfloat *n_stops = NULL;
1119    int count = 0;
1120    float slope;
1121    GLfloat xscale, yscale;
1122    GLfloat pt1[2], pt2[2];
1123    float transform_mat[3][3];
1124    static const float identity_mat[3][3] = { {1.0, 0.0, 0.0},
1125    {0.0, 1.0, 0.0},
1126    {0.0, 0.0, 1.0}
1127    };
1128    GLfloat stop_colors_st[LINEAR_SMALL_STOPS * 4];
1129    GLfloat n_stops_st[LINEAR_SMALL_STOPS];
1130
1131    GLint transform_mat_uniform_location = 0;
1132    GLint n_stop_uniform_location = 0;
1133    GLint stops_uniform_location = 0;
1134    GLint stop0_uniform_location = 0;
1135    GLint stop1_uniform_location = 0;
1136    GLint stop2_uniform_location = 0;
1137    GLint stop3_uniform_location = 0;
1138    GLint stop4_uniform_location = 0;
1139    GLint stop5_uniform_location = 0;
1140    GLint stop6_uniform_location = 0;
1141    GLint stop7_uniform_location = 0;
1142    GLint stop_colors_uniform_location = 0;
1143    GLint stop_color0_uniform_location = 0;
1144    GLint stop_color1_uniform_location = 0;
1145    GLint stop_color2_uniform_location = 0;
1146    GLint stop_color3_uniform_location = 0;
1147    GLint stop_color4_uniform_location = 0;
1148    GLint stop_color5_uniform_location = 0;
1149    GLint stop_color6_uniform_location = 0;
1150    GLint stop_color7_uniform_location = 0;
1151    GLint pt_slope_uniform_location = 0;
1152    GLint repeat_type_uniform_location = 0;
1153    GLint hor_ver_uniform_location = 0;
1154    GLint cos_val_uniform_location = 0;
1155    GLint p1_distance_uniform_location = 0;
1156    GLint pt_distance_uniform_location = 0;
1157
1158    glamor_priv = glamor_get_screen_private(screen);
1159    glamor_make_current(glamor_priv);
1160
1161    /* Create a pixmap with VBO. */
1162    pixmap = glamor_create_pixmap(screen,
1163                                  width, height,
1164                                  PIXMAN_FORMAT_DEPTH(format), 0);
1165
1166    if (!pixmap)
1167        goto GRADIENT_FAIL;
1168
1169    dst_picture = CreatePicture(0, &pixmap->drawable,
1170                                PictureMatchFormat(screen,
1171                                                   PIXMAN_FORMAT_DEPTH(format),
1172                                                   format), 0, 0, serverClient,
1173                                &error);
1174
1175    /* Release the reference, picture will hold the last one. */
1176    glamor_destroy_pixmap(pixmap);
1177
1178    if (!dst_picture)
1179        goto GRADIENT_FAIL;
1180
1181    ValidatePicture(dst_picture);
1182
1183    stops_count = src_picture->pSourcePict->linear.nstops + 2;
1184
1185    /* Because the max value of nstops is unknown, so create a program
1186       when nstops > LINEAR_LARGE_STOPS. */
1187    if (stops_count <= LINEAR_SMALL_STOPS) {
1188        gradient_prog = glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][0];
1189    }
1190    else if (stops_count <= LINEAR_LARGE_STOPS) {
1191        gradient_prog = glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][1];
1192    }
1193    else {
1194        _glamor_create_linear_gradient_program(screen,
1195                                               src_picture->pSourcePict->linear.
1196                                               nstops + 2, 1);
1197        gradient_prog = glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][2];
1198    }
1199
1200    /* Bind all the uniform vars . */
1201    n_stop_uniform_location =
1202        glGetUniformLocation(gradient_prog, "n_stop");
1203    pt_slope_uniform_location =
1204        glGetUniformLocation(gradient_prog, "pt_slope");
1205    repeat_type_uniform_location =
1206        glGetUniformLocation(gradient_prog, "repeat_type");
1207    hor_ver_uniform_location =
1208        glGetUniformLocation(gradient_prog, "hor_ver");
1209    transform_mat_uniform_location =
1210        glGetUniformLocation(gradient_prog, "transform_mat");
1211    cos_val_uniform_location =
1212        glGetUniformLocation(gradient_prog, "cos_val");
1213    p1_distance_uniform_location =
1214        glGetUniformLocation(gradient_prog, "p1_distance");
1215    pt_distance_uniform_location =
1216        glGetUniformLocation(gradient_prog, "pt_distance");
1217
1218    if (src_picture->pSourcePict->linear.nstops + 2 <= LINEAR_SMALL_STOPS) {
1219        stop0_uniform_location =
1220            glGetUniformLocation(gradient_prog, "stop0");
1221        stop1_uniform_location =
1222            glGetUniformLocation(gradient_prog, "stop1");
1223        stop2_uniform_location =
1224            glGetUniformLocation(gradient_prog, "stop2");
1225        stop3_uniform_location =
1226            glGetUniformLocation(gradient_prog, "stop3");
1227        stop4_uniform_location =
1228            glGetUniformLocation(gradient_prog, "stop4");
1229        stop5_uniform_location =
1230            glGetUniformLocation(gradient_prog, "stop5");
1231        stop6_uniform_location =
1232            glGetUniformLocation(gradient_prog, "stop6");
1233        stop7_uniform_location =
1234            glGetUniformLocation(gradient_prog, "stop7");
1235
1236        stop_color0_uniform_location =
1237            glGetUniformLocation(gradient_prog, "stop_color0");
1238        stop_color1_uniform_location =
1239            glGetUniformLocation(gradient_prog, "stop_color1");
1240        stop_color2_uniform_location =
1241            glGetUniformLocation(gradient_prog, "stop_color2");
1242        stop_color3_uniform_location =
1243            glGetUniformLocation(gradient_prog, "stop_color3");
1244        stop_color4_uniform_location =
1245            glGetUniformLocation(gradient_prog, "stop_color4");
1246        stop_color5_uniform_location =
1247            glGetUniformLocation(gradient_prog, "stop_color5");
1248        stop_color6_uniform_location =
1249            glGetUniformLocation(gradient_prog, "stop_color6");
1250        stop_color7_uniform_location =
1251            glGetUniformLocation(gradient_prog, "stop_color7");
1252    }
1253    else {
1254        stops_uniform_location =
1255            glGetUniformLocation(gradient_prog, "stops");
1256        stop_colors_uniform_location =
1257            glGetUniformLocation(gradient_prog, "stop_colors");
1258    }
1259
1260    glUseProgram(gradient_prog);
1261
1262    glUniform1i(repeat_type_uniform_location, src_picture->repeatType);
1263
1264    /* set the transform matrix. */
1265    if (src_picture->transform) {
1266        _glamor_gradient_convert_trans_matrix(src_picture->transform,
1267                                              transform_mat, width, height, 1);
1268        glUniformMatrix3fv(transform_mat_uniform_location,
1269                           1, 1, &transform_mat[0][0]);
1270    }
1271    else {
1272        glUniformMatrix3fv(transform_mat_uniform_location,
1273                           1, 1, &identity_mat[0][0]);
1274    }
1275
1276    if (!_glamor_gradient_set_pixmap_destination
1277        (screen, glamor_priv, dst_picture, &xscale, &yscale, x_source, y_source,
1278         1))
1279        goto GRADIENT_FAIL;
1280
1281    glamor_set_alu(screen, GXcopy);
1282
1283    /* Normalize the PTs. */
1284    glamor_set_normalize_pt(xscale, yscale,
1285                            pixman_fixed_to_double(src_picture->pSourcePict->
1286                                                   linear.p1.x),
1287                            pixman_fixed_to_double(src_picture->pSourcePict->
1288                                                   linear.p1.y),
1289                            pt1);
1290    DEBUGF("pt1:(%f, %f) ---> (%f %f)\n",
1291           pixman_fixed_to_double(src_picture->pSourcePict->linear.p1.x),
1292           pixman_fixed_to_double(src_picture->pSourcePict->linear.p1.y),
1293           pt1[0], pt1[1]);
1294
1295    glamor_set_normalize_pt(xscale, yscale,
1296                            pixman_fixed_to_double(src_picture->pSourcePict->
1297                                                   linear.p2.x),
1298                            pixman_fixed_to_double(src_picture->pSourcePict->
1299                                                   linear.p2.y),
1300                            pt2);
1301    DEBUGF("pt2:(%f, %f) ---> (%f %f)\n",
1302           pixman_fixed_to_double(src_picture->pSourcePict->linear.p2.x),
1303           pixman_fixed_to_double(src_picture->pSourcePict->linear.p2.y),
1304           pt2[0], pt2[1]);
1305
1306    /* Set all the stops and colors to shader. */
1307    if (stops_count > LINEAR_SMALL_STOPS) {
1308        stop_colors = xallocarray(stops_count, 4 * sizeof(float));
1309        if (stop_colors == NULL) {
1310            ErrorF("Failed to allocate stop_colors memory.\n");
1311            goto GRADIENT_FAIL;
1312        }
1313
1314        n_stops = xallocarray(stops_count, sizeof(float));
1315        if (n_stops == NULL) {
1316            ErrorF("Failed to allocate n_stops memory.\n");
1317            goto GRADIENT_FAIL;
1318        }
1319    }
1320    else {
1321        stop_colors = stop_colors_st;
1322        n_stops = n_stops_st;
1323    }
1324
1325    count =
1326        _glamor_gradient_set_stops(src_picture,
1327                                   &src_picture->pSourcePict->gradient,
1328                                   stop_colors, n_stops);
1329
1330    if (src_picture->pSourcePict->linear.nstops + 2 <= LINEAR_SMALL_STOPS) {
1331        int j = 0;
1332
1333        glUniform4f(stop_color0_uniform_location,
1334                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1335                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1336        j++;
1337        glUniform4f(stop_color1_uniform_location,
1338                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1339                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1340        j++;
1341        glUniform4f(stop_color2_uniform_location,
1342                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1343                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1344        j++;
1345        glUniform4f(stop_color3_uniform_location,
1346                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1347                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1348        j++;
1349        glUniform4f(stop_color4_uniform_location,
1350                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1351                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1352        j++;
1353        glUniform4f(stop_color5_uniform_location,
1354                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1355                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1356        j++;
1357        glUniform4f(stop_color6_uniform_location,
1358                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1359                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1360        j++;
1361        glUniform4f(stop_color7_uniform_location,
1362                    stop_colors[4 * j + 0], stop_colors[4 * j + 1],
1363                    stop_colors[4 * j + 2], stop_colors[4 * j + 3]);
1364
1365        j = 0;
1366        glUniform1f(stop0_uniform_location, n_stops[j++]);
1367        glUniform1f(stop1_uniform_location, n_stops[j++]);
1368        glUniform1f(stop2_uniform_location, n_stops[j++]);
1369        glUniform1f(stop3_uniform_location, n_stops[j++]);
1370        glUniform1f(stop4_uniform_location, n_stops[j++]);
1371        glUniform1f(stop5_uniform_location, n_stops[j++]);
1372        glUniform1f(stop6_uniform_location, n_stops[j++]);
1373        glUniform1f(stop7_uniform_location, n_stops[j++]);
1374
1375        glUniform1i(n_stop_uniform_location, count);
1376    }
1377    else {
1378        glUniform4fv(stop_colors_uniform_location, count, stop_colors);
1379        glUniform1fv(stops_uniform_location, count, n_stops);
1380        glUniform1i(n_stop_uniform_location, count);
1381    }
1382
1383    if (src_picture->pSourcePict->linear.p2.y == src_picture->pSourcePict->linear.p1.y) {       // The horizontal case.
1384        glUniform1i(hor_ver_uniform_location, 1);
1385        DEBUGF("p1.y: %f, p2.y: %f, enter the horizontal case\n",
1386               pt1[1], pt2[1]);
1387
1388        p1_distance = pt1[0];
1389        pt_distance = (pt2[0] - p1_distance);
1390        glUniform1f(p1_distance_uniform_location, p1_distance);
1391        glUniform1f(pt_distance_uniform_location, pt_distance);
1392    }
1393    else {
1394        /* The slope need to compute here. In shader, the viewport set will change
1395           the original slope and the slope which is vertical to it will not be correct. */
1396        slope = -(float) (src_picture->pSourcePict->linear.p2.x
1397                          - src_picture->pSourcePict->linear.p1.x) /
1398            (float) (src_picture->pSourcePict->linear.p2.y
1399                     - src_picture->pSourcePict->linear.p1.y);
1400        slope = slope * yscale / xscale;
1401        glUniform1f(pt_slope_uniform_location, slope);
1402        glUniform1i(hor_ver_uniform_location, 0);
1403
1404        cos_val = sqrt(1.0 / (slope * slope + 1.0));
1405        glUniform1f(cos_val_uniform_location, cos_val);
1406
1407        p1_distance = (pt1[1] - pt1[0] * slope) * cos_val;
1408        pt_distance = (pt2[1] - pt2[0] * slope) * cos_val - p1_distance;
1409        glUniform1f(p1_distance_uniform_location, p1_distance);
1410        glUniform1f(pt_distance_uniform_location, pt_distance);
1411    }
1412
1413    /* Now rendering. */
1414    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1415
1416    /* Do the clear logic. */
1417    if (stops_count > LINEAR_SMALL_STOPS) {
1418        free(n_stops);
1419        free(stop_colors);
1420    }
1421
1422    glDisableVertexAttribArray(GLAMOR_VERTEX_POS);
1423    glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE);
1424
1425    return dst_picture;
1426
1427 GRADIENT_FAIL:
1428    if (dst_picture) {
1429        FreePicture(dst_picture, 0);
1430    }
1431
1432    if (stops_count > LINEAR_SMALL_STOPS) {
1433        if (n_stops)
1434            free(n_stops);
1435        if (stop_colors)
1436            free(stop_colors);
1437    }
1438
1439    glDisableVertexAttribArray(GLAMOR_VERTEX_POS);
1440    glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE);
1441    return NULL;
1442}
1443