xref: /OK3568_Linux_fs/external/xserver/glamor/glamor_dash.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Copyright © 2014 Keith Packard
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22 
23 #include "glamor_priv.h"
24 #include "glamor_program.h"
25 #include "glamor_transform.h"
26 #include "glamor_transfer.h"
27 #include "glamor_prepare.h"
28 
29 static const char dash_vs_vars[] =
30     "attribute vec3 primitive;\n"
31     "varying float dash_offset;\n";
32 
33 static const char dash_vs_exec[] =
34     "       dash_offset = primitive.z / dash_length;\n"
35     "       vec2 pos = vec2(0,0);\n"
36     GLAMOR_POS(gl_Position, primitive.xy);
37 
38 static const char dash_fs_vars[] =
39     "varying float dash_offset;\n";
40 
41 static const char on_off_fs_exec[] =
42     "       float pattern = texture2D(dash, vec2(dash_offset, 0.5)).w;\n"
43     "       if (pattern == 0.0)\n"
44     "               discard;\n";
45 
46 /* XXX deal with stippled double dashed lines once we have stippling support */
47 static const char double_fs_exec[] =
48     "       float pattern = texture2D(dash, vec2(dash_offset, 0.5)).w;\n"
49     "       if (pattern == 0.0)\n"
50     "               gl_FragColor = bg;\n"
51     "       else\n"
52     "               gl_FragColor = fg;\n";
53 
54 
55 static const glamor_facet glamor_facet_on_off_dash_lines = {
56     .version = 130,
57     .name = "poly_lines_on_off_dash",
58     .vs_vars = dash_vs_vars,
59     .vs_exec = dash_vs_exec,
60     .fs_vars = dash_fs_vars,
61     .fs_exec = on_off_fs_exec,
62     .locations = glamor_program_location_dash,
63 };
64 
65 static const glamor_facet glamor_facet_double_dash_lines = {
66     .version = 130,
67     .name = "poly_lines_double_dash",
68     .vs_vars = dash_vs_vars,
69     .vs_exec = dash_vs_exec,
70     .fs_vars = dash_fs_vars,
71     .fs_exec = double_fs_exec,
72     .locations = (glamor_program_location_dash|
73                   glamor_program_location_fg|
74                   glamor_program_location_bg),
75 };
76 
77 static PixmapPtr
glamor_get_dash_pixmap(GCPtr gc)78 glamor_get_dash_pixmap(GCPtr gc)
79 {
80     glamor_gc_private *gc_priv = glamor_get_gc_private(gc);
81     ScreenPtr   screen = gc->pScreen;
82     PixmapPtr   pixmap;
83     int         offset;
84     int         d;
85     uint32_t    pixel;
86     GCPtr       scratch_gc;
87 
88     if (gc_priv->dash)
89         return gc_priv->dash;
90 
91     offset = 0;
92     for (d = 0; d < gc->numInDashList; d++)
93         offset += gc->dash[d];
94 
95     pixmap = glamor_create_pixmap(screen, offset, 1, 8, 0);
96     if (!pixmap)
97         goto bail;
98 
99     scratch_gc = GetScratchGC(8, screen);
100     if (!scratch_gc)
101         goto bail_pixmap;
102 
103     pixel = 0xffffffff;
104     offset = 0;
105     for (d = 0; d < gc->numInDashList; d++) {
106         xRectangle      rect;
107         ChangeGCVal     changes;
108 
109         changes.val = pixel;
110         (void) ChangeGC(NullClient, scratch_gc,
111                         GCForeground, &changes);
112         ValidateGC(&pixmap->drawable, scratch_gc);
113         rect.x = offset;
114         rect.y = 0;
115         rect.width = gc->dash[d];
116         rect.height = 1;
117         scratch_gc->ops->PolyFillRect (&pixmap->drawable, scratch_gc, 1, &rect);
118         offset += gc->dash[d];
119         pixel = ~pixel;
120     }
121     FreeScratchGC(scratch_gc);
122 
123     gc_priv->dash = pixmap;
124     return pixmap;
125 
126 bail_pixmap:
127     glamor_destroy_pixmap(pixmap);
128 bail:
129     return NULL;
130 }
131 
132 static glamor_program *
glamor_dash_setup(DrawablePtr drawable,GCPtr gc)133 glamor_dash_setup(DrawablePtr drawable, GCPtr gc)
134 {
135     ScreenPtr screen = drawable->pScreen;
136     glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
137     PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
138     glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
139     PixmapPtr dash_pixmap;
140     glamor_pixmap_private *dash_priv;
141     glamor_program *prog;
142 
143     if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv))
144         goto bail;
145 
146     if (gc->lineWidth != 0)
147         goto bail;
148 
149     dash_pixmap = glamor_get_dash_pixmap(gc);
150     dash_priv = glamor_get_pixmap_private(dash_pixmap);
151 
152     if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(dash_priv))
153         goto bail;
154 
155     glamor_make_current(glamor_priv);
156 
157     switch (gc->lineStyle) {
158     case LineOnOffDash:
159         prog = glamor_use_program_fill(pixmap, gc,
160                                        &glamor_priv->on_off_dash_line_progs,
161                                        &glamor_facet_on_off_dash_lines);
162         if (!prog)
163             goto bail;
164         break;
165     case LineDoubleDash:
166         if (gc->fillStyle != FillSolid)
167             goto bail;
168 
169         prog = &glamor_priv->double_dash_line_prog;
170 
171         if (!prog->prog) {
172             if (!glamor_build_program(screen, prog,
173                                       &glamor_facet_double_dash_lines,
174                                       NULL, NULL, NULL))
175                 goto bail;
176         }
177 
178         if (!glamor_use_program(pixmap, gc, prog, NULL))
179             goto bail;
180 
181         glamor_set_color(pixmap, gc->fgPixel, prog->fg_uniform);
182         glamor_set_color(pixmap, gc->bgPixel, prog->bg_uniform);
183         break;
184 
185     default:
186         goto bail;
187     }
188 
189 
190     /* Set the dash pattern as texture 1 */
191 
192     glamor_bind_texture(glamor_priv, GL_TEXTURE1, dash_priv->fbo, FALSE);
193     glUniform1i(prog->dash_uniform, 1);
194     glUniform1f(prog->dash_length_uniform, dash_pixmap->drawable.width);
195 
196     return prog;
197 
198 bail:
199     return NULL;
200 }
201 
202 static void
glamor_dash_loop(DrawablePtr drawable,GCPtr gc,glamor_program * prog,int n,GLenum mode)203 glamor_dash_loop(DrawablePtr drawable, GCPtr gc, glamor_program *prog,
204                  int n, GLenum mode)
205 {
206     PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
207     glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
208     int box_index;
209     int off_x, off_y;
210 
211     glEnable(GL_SCISSOR_TEST);
212 
213     glamor_pixmap_loop(pixmap_priv, box_index) {
214         int nbox = RegionNumRects(gc->pCompositeClip);
215         BoxPtr box = RegionRects(gc->pCompositeClip);
216 
217         glamor_set_destination_drawable(drawable, box_index, TRUE, TRUE,
218                                         prog->matrix_uniform, &off_x, &off_y);
219 
220         while (nbox--) {
221             glScissor(box->x1 + off_x,
222                       box->y1 + off_y,
223                       box->x2 - box->x1,
224                       box->y2 - box->y1);
225             box++;
226             glDrawArrays(mode, 0, n);
227         }
228     }
229 
230     glDisable(GL_SCISSOR_TEST);
231     glDisableVertexAttribArray(GLAMOR_VERTEX_POS);
232 
233     glamor_pixmap_invalid(pixmap);
234 }
235 
236 static int
glamor_line_length(short x1,short y1,short x2,short y2)237 glamor_line_length(short x1, short y1, short x2, short y2)
238 {
239     return max(abs(x2 - x1), abs(y2 - y1));
240 }
241 
242 Bool
glamor_poly_lines_dash_gl(DrawablePtr drawable,GCPtr gc,int mode,int n,DDXPointPtr points)243 glamor_poly_lines_dash_gl(DrawablePtr drawable, GCPtr gc,
244                           int mode, int n, DDXPointPtr points)
245 {
246     ScreenPtr screen = drawable->pScreen;
247     glamor_program *prog;
248     short *v;
249     char *vbo_offset;
250     int add_last;
251     int dash_pos;
252     int prev_x, prev_y;
253     int i;
254 
255     if (n < 2)
256         return TRUE;
257 
258     if (!(prog = glamor_dash_setup(drawable, gc)))
259         return FALSE;
260 
261     add_last = 0;
262     if (gc->capStyle != CapNotLast)
263         add_last = 1;
264 
265     /* Set up the vertex buffers for the points */
266 
267     v = glamor_get_vbo_space(drawable->pScreen,
268                              (n + add_last) * 3 * sizeof (short),
269                              &vbo_offset);
270 
271     glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
272     glVertexAttribPointer(GLAMOR_VERTEX_POS, 3, GL_SHORT, GL_FALSE,
273                           3 * sizeof (short), vbo_offset);
274 
275     dash_pos = gc->dashOffset;
276     prev_x = prev_y = 0;
277     for (i = 0; i < n; i++) {
278         int this_x = points[i].x;
279         int this_y = points[i].y;
280         if (i) {
281             if (mode == CoordModePrevious) {
282                 this_x += prev_x;
283                 this_y += prev_y;
284             }
285             dash_pos += glamor_line_length(prev_x, prev_y,
286                                            this_x, this_y);
287         }
288         v[0] = prev_x = this_x;
289         v[1] = prev_y = this_y;
290         v[2] = dash_pos;
291         v += 3;
292     }
293 
294     if (add_last) {
295         v[0] = prev_x + 1;
296         v[1] = prev_y;
297         v[2] = dash_pos + 1;
298     }
299 
300     glamor_put_vbo_space(screen);
301 
302     glamor_dash_loop(drawable, gc, prog, n + add_last, GL_LINE_STRIP);
303 
304     return TRUE;
305 }
306 
307 static short *
glamor_add_segment(short * v,short x1,short y1,short x2,short y2,int dash_start,int dash_end)308 glamor_add_segment(short *v, short x1, short y1, short x2, short y2,
309                    int dash_start, int dash_end)
310 {
311     v[0] = x1;
312     v[1] = y1;
313     v[2] = dash_start;
314 
315     v[3] = x2;
316     v[4] = y2;
317     v[5] = dash_end;
318     return v + 6;
319 }
320 
321 Bool
glamor_poly_segment_dash_gl(DrawablePtr drawable,GCPtr gc,int nseg,xSegment * segs)322 glamor_poly_segment_dash_gl(DrawablePtr drawable, GCPtr gc,
323                             int nseg, xSegment *segs)
324 {
325     ScreenPtr screen = drawable->pScreen;
326     glamor_program *prog;
327     short *v;
328     char *vbo_offset;
329     int dash_start = gc->dashOffset;
330     int add_last;
331     int i;
332 
333     if (!(prog = glamor_dash_setup(drawable, gc)))
334         return FALSE;
335 
336     add_last = 0;
337     if (gc->capStyle != CapNotLast)
338         add_last = 1;
339 
340     /* Set up the vertex buffers for the points */
341 
342     v = glamor_get_vbo_space(drawable->pScreen,
343                              (nseg<<add_last) * 6 * sizeof (short),
344                              &vbo_offset);
345 
346     glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
347     glVertexAttribPointer(GLAMOR_VERTEX_POS, 3, GL_SHORT, GL_FALSE,
348                           3 * sizeof (short), vbo_offset);
349 
350     for (i = 0; i < nseg; i++) {
351         int dash_end = dash_start + glamor_line_length(segs[i].x1, segs[i].y1,
352                                                        segs[i].x2, segs[i].y2);
353         v = glamor_add_segment(v,
354                                segs[i].x1, segs[i].y1,
355                                segs[i].x2, segs[i].y2,
356                                dash_start, dash_end);
357         if (add_last)
358             v = glamor_add_segment(v,
359                                    segs[i].x2, segs[i].y2,
360                                    segs[i].x2 + 1, segs[i].y2,
361                                    dash_end, dash_end + 1);
362     }
363 
364     glamor_put_vbo_space(screen);
365 
366     glamor_dash_loop(drawable, gc, prog, nseg << (1 + add_last), GL_LINES);
367 
368     return TRUE;
369 }
370