135c4bbdfSmrg/*
235c4bbdfSmrg * Copyright © 2014 Keith Packard
335c4bbdfSmrg *
435c4bbdfSmrg * Permission to use, copy, modify, distribute, and sell this software and its
535c4bbdfSmrg * documentation for any purpose is hereby granted without fee, provided that
635c4bbdfSmrg * the above copyright notice appear in all copies and that both that copyright
735c4bbdfSmrg * notice and this permission notice appear in supporting documentation, and
835c4bbdfSmrg * that the name of the copyright holders not be used in advertising or
935c4bbdfSmrg * publicity pertaining to distribution of the software without specific,
1035c4bbdfSmrg * written prior permission.  The copyright holders make no representations
1135c4bbdfSmrg * about the suitability of this software for any purpose.  It is provided "as
1235c4bbdfSmrg * is" without express or implied warranty.
1335c4bbdfSmrg *
1435c4bbdfSmrg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1535c4bbdfSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1635c4bbdfSmrg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1735c4bbdfSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1835c4bbdfSmrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1935c4bbdfSmrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
2035c4bbdfSmrg * OF THIS SOFTWARE.
2135c4bbdfSmrg */
2235c4bbdfSmrg
2335c4bbdfSmrg#include "glamor_priv.h"
2435c4bbdfSmrg#include "glamor_program.h"
2535c4bbdfSmrg#include "glamor_transform.h"
2635c4bbdfSmrg#include "glamor_transfer.h"
2735c4bbdfSmrg#include "glamor_prepare.h"
2835c4bbdfSmrg
2935c4bbdfSmrgstatic const char dash_vs_vars[] =
3035c4bbdfSmrg    "attribute vec3 primitive;\n"
3135c4bbdfSmrg    "varying float dash_offset;\n";
3235c4bbdfSmrg
3335c4bbdfSmrgstatic const char dash_vs_exec[] =
3435c4bbdfSmrg    "       dash_offset = primitive.z / dash_length;\n"
351b5d61b8Smrg    "       vec2 pos = vec2(0,0);\n"
3635c4bbdfSmrg    GLAMOR_POS(gl_Position, primitive.xy);
3735c4bbdfSmrg
3835c4bbdfSmrgstatic const char dash_fs_vars[] =
3935c4bbdfSmrg    "varying float dash_offset;\n";
4035c4bbdfSmrg
4135c4bbdfSmrgstatic const char on_off_fs_exec[] =
4235c4bbdfSmrg    "       float pattern = texture2D(dash, vec2(dash_offset, 0.5)).w;\n"
4335c4bbdfSmrg    "       if (pattern == 0.0)\n"
4435c4bbdfSmrg    "               discard;\n";
4535c4bbdfSmrg
4635c4bbdfSmrg/* XXX deal with stippled double dashed lines once we have stippling support */
4735c4bbdfSmrgstatic const char double_fs_exec[] =
4835c4bbdfSmrg    "       float pattern = texture2D(dash, vec2(dash_offset, 0.5)).w;\n"
4935c4bbdfSmrg    "       if (pattern == 0.0)\n"
5035c4bbdfSmrg    "               gl_FragColor = bg;\n"
5135c4bbdfSmrg    "       else\n"
5235c4bbdfSmrg    "               gl_FragColor = fg;\n";
5335c4bbdfSmrg
5435c4bbdfSmrg
5535c4bbdfSmrgstatic const glamor_facet glamor_facet_on_off_dash_lines = {
5635c4bbdfSmrg    .version = 130,
5735c4bbdfSmrg    .name = "poly_lines_on_off_dash",
5835c4bbdfSmrg    .vs_vars = dash_vs_vars,
5935c4bbdfSmrg    .vs_exec = dash_vs_exec,
6035c4bbdfSmrg    .fs_vars = dash_fs_vars,
6135c4bbdfSmrg    .fs_exec = on_off_fs_exec,
6235c4bbdfSmrg    .locations = glamor_program_location_dash,
6335c4bbdfSmrg};
6435c4bbdfSmrg
6535c4bbdfSmrgstatic const glamor_facet glamor_facet_double_dash_lines = {
6635c4bbdfSmrg    .version = 130,
6735c4bbdfSmrg    .name = "poly_lines_double_dash",
6835c4bbdfSmrg    .vs_vars = dash_vs_vars,
6935c4bbdfSmrg    .vs_exec = dash_vs_exec,
7035c4bbdfSmrg    .fs_vars = dash_fs_vars,
7135c4bbdfSmrg    .fs_exec = double_fs_exec,
7235c4bbdfSmrg    .locations = (glamor_program_location_dash|
7335c4bbdfSmrg                  glamor_program_location_fg|
7435c4bbdfSmrg                  glamor_program_location_bg),
7535c4bbdfSmrg};
7635c4bbdfSmrg
7735c4bbdfSmrgstatic PixmapPtr
7835c4bbdfSmrgglamor_get_dash_pixmap(GCPtr gc)
7935c4bbdfSmrg{
8035c4bbdfSmrg    glamor_gc_private *gc_priv = glamor_get_gc_private(gc);
8135c4bbdfSmrg    ScreenPtr   screen = gc->pScreen;
8235c4bbdfSmrg    PixmapPtr   pixmap;
8335c4bbdfSmrg    int         offset;
8435c4bbdfSmrg    int         d;
8535c4bbdfSmrg    uint32_t    pixel;
8635c4bbdfSmrg    GCPtr       scratch_gc;
8735c4bbdfSmrg
8835c4bbdfSmrg    if (gc_priv->dash)
8935c4bbdfSmrg        return gc_priv->dash;
9035c4bbdfSmrg
9135c4bbdfSmrg    offset = 0;
9235c4bbdfSmrg    for (d = 0; d < gc->numInDashList; d++)
9335c4bbdfSmrg        offset += gc->dash[d];
9435c4bbdfSmrg
9535c4bbdfSmrg    pixmap = glamor_create_pixmap(screen, offset, 1, 8, 0);
9635c4bbdfSmrg    if (!pixmap)
9735c4bbdfSmrg        goto bail;
9835c4bbdfSmrg
9935c4bbdfSmrg    scratch_gc = GetScratchGC(8, screen);
10035c4bbdfSmrg    if (!scratch_gc)
10135c4bbdfSmrg        goto bail_pixmap;
10235c4bbdfSmrg
10335c4bbdfSmrg    pixel = 0xffffffff;
10435c4bbdfSmrg    offset = 0;
10535c4bbdfSmrg    for (d = 0; d < gc->numInDashList; d++) {
10635c4bbdfSmrg        xRectangle      rect;
10735c4bbdfSmrg        ChangeGCVal     changes;
10835c4bbdfSmrg
10935c4bbdfSmrg        changes.val = pixel;
11035c4bbdfSmrg        (void) ChangeGC(NullClient, scratch_gc,
11135c4bbdfSmrg                        GCForeground, &changes);
11235c4bbdfSmrg        ValidateGC(&pixmap->drawable, scratch_gc);
11335c4bbdfSmrg        rect.x = offset;
11435c4bbdfSmrg        rect.y = 0;
11535c4bbdfSmrg        rect.width = gc->dash[d];
11635c4bbdfSmrg        rect.height = 1;
11735c4bbdfSmrg        scratch_gc->ops->PolyFillRect (&pixmap->drawable, scratch_gc, 1, &rect);
11835c4bbdfSmrg        offset += gc->dash[d];
11935c4bbdfSmrg        pixel = ~pixel;
12035c4bbdfSmrg    }
12135c4bbdfSmrg    FreeScratchGC(scratch_gc);
12235c4bbdfSmrg
12335c4bbdfSmrg    gc_priv->dash = pixmap;
12435c4bbdfSmrg    return pixmap;
12535c4bbdfSmrg
12635c4bbdfSmrgbail_pixmap:
12735c4bbdfSmrg    glamor_destroy_pixmap(pixmap);
12835c4bbdfSmrgbail:
12935c4bbdfSmrg    return NULL;
13035c4bbdfSmrg}
13135c4bbdfSmrg
13235c4bbdfSmrgstatic glamor_program *
13335c4bbdfSmrgglamor_dash_setup(DrawablePtr drawable, GCPtr gc)
13435c4bbdfSmrg{
13535c4bbdfSmrg    ScreenPtr screen = drawable->pScreen;
13635c4bbdfSmrg    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
13735c4bbdfSmrg    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
13835c4bbdfSmrg    glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
13935c4bbdfSmrg    PixmapPtr dash_pixmap;
14035c4bbdfSmrg    glamor_pixmap_private *dash_priv;
14135c4bbdfSmrg    glamor_program *prog;
14235c4bbdfSmrg
14335c4bbdfSmrg    if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv))
14435c4bbdfSmrg        goto bail;
14535c4bbdfSmrg
14635c4bbdfSmrg    if (gc->lineWidth != 0)
14735c4bbdfSmrg        goto bail;
14835c4bbdfSmrg
14935c4bbdfSmrg    dash_pixmap = glamor_get_dash_pixmap(gc);
1501b5d61b8Smrg    dash_priv = glamor_get_pixmap_private(dash_pixmap);
15135c4bbdfSmrg
15235c4bbdfSmrg    if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(dash_priv))
15335c4bbdfSmrg        goto bail;
15435c4bbdfSmrg
15535c4bbdfSmrg    glamor_make_current(glamor_priv);
15635c4bbdfSmrg
15735c4bbdfSmrg    switch (gc->lineStyle) {
15835c4bbdfSmrg    case LineOnOffDash:
15935c4bbdfSmrg        prog = glamor_use_program_fill(pixmap, gc,
16035c4bbdfSmrg                                       &glamor_priv->on_off_dash_line_progs,
16135c4bbdfSmrg                                       &glamor_facet_on_off_dash_lines);
16235c4bbdfSmrg        if (!prog)
16335c4bbdfSmrg            goto bail;
16435c4bbdfSmrg        break;
16535c4bbdfSmrg    case LineDoubleDash:
16635c4bbdfSmrg        if (gc->fillStyle != FillSolid)
16735c4bbdfSmrg            goto bail;
16835c4bbdfSmrg
16935c4bbdfSmrg        prog = &glamor_priv->double_dash_line_prog;
17035c4bbdfSmrg
17135c4bbdfSmrg        if (!prog->prog) {
17235c4bbdfSmrg            if (!glamor_build_program(screen, prog,
17335c4bbdfSmrg                                      &glamor_facet_double_dash_lines,
17435c4bbdfSmrg                                      NULL, NULL, NULL))
17535c4bbdfSmrg                goto bail;
17635c4bbdfSmrg        }
17735c4bbdfSmrg
17835c4bbdfSmrg        if (!glamor_use_program(pixmap, gc, prog, NULL))
17935c4bbdfSmrg            goto bail;
18035c4bbdfSmrg
18135c4bbdfSmrg        glamor_set_color(pixmap, gc->fgPixel, prog->fg_uniform);
18235c4bbdfSmrg        glamor_set_color(pixmap, gc->bgPixel, prog->bg_uniform);
18335c4bbdfSmrg        break;
18435c4bbdfSmrg
18535c4bbdfSmrg    default:
18635c4bbdfSmrg        goto bail;
18735c4bbdfSmrg    }
18835c4bbdfSmrg
18935c4bbdfSmrg
19035c4bbdfSmrg    /* Set the dash pattern as texture 1 */
19135c4bbdfSmrg
19235c4bbdfSmrg    glamor_bind_texture(glamor_priv, GL_TEXTURE1, dash_priv->fbo, FALSE);
19335c4bbdfSmrg    glUniform1i(prog->dash_uniform, 1);
19435c4bbdfSmrg    glUniform1f(prog->dash_length_uniform, dash_pixmap->drawable.width);
19535c4bbdfSmrg
19635c4bbdfSmrg    return prog;
19735c4bbdfSmrg
19835c4bbdfSmrgbail:
19935c4bbdfSmrg    return NULL;
20035c4bbdfSmrg}
20135c4bbdfSmrg
20235c4bbdfSmrgstatic void
20335c4bbdfSmrgglamor_dash_loop(DrawablePtr drawable, GCPtr gc, glamor_program *prog,
20435c4bbdfSmrg                 int n, GLenum mode)
20535c4bbdfSmrg{
20635c4bbdfSmrg    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
20735c4bbdfSmrg    glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
20835c4bbdfSmrg    int box_index;
20935c4bbdfSmrg    int off_x, off_y;
21035c4bbdfSmrg
21135c4bbdfSmrg    glEnable(GL_SCISSOR_TEST);
21235c4bbdfSmrg
21335c4bbdfSmrg    glamor_pixmap_loop(pixmap_priv, box_index) {
21435c4bbdfSmrg        int nbox = RegionNumRects(gc->pCompositeClip);
21535c4bbdfSmrg        BoxPtr box = RegionRects(gc->pCompositeClip);
21635c4bbdfSmrg
21735c4bbdfSmrg        glamor_set_destination_drawable(drawable, box_index, TRUE, TRUE,
21835c4bbdfSmrg                                        prog->matrix_uniform, &off_x, &off_y);
21935c4bbdfSmrg
22035c4bbdfSmrg        while (nbox--) {
22135c4bbdfSmrg            glScissor(box->x1 + off_x,
22235c4bbdfSmrg                      box->y1 + off_y,
22335c4bbdfSmrg                      box->x2 - box->x1,
22435c4bbdfSmrg                      box->y2 - box->y1);
22535c4bbdfSmrg            box++;
22635c4bbdfSmrg            glDrawArrays(mode, 0, n);
22735c4bbdfSmrg        }
22835c4bbdfSmrg    }
22935c4bbdfSmrg
23035c4bbdfSmrg    glDisable(GL_SCISSOR_TEST);
23135c4bbdfSmrg    glDisableVertexAttribArray(GLAMOR_VERTEX_POS);
23235c4bbdfSmrg}
23335c4bbdfSmrg
23435c4bbdfSmrgstatic int
23535c4bbdfSmrgglamor_line_length(short x1, short y1, short x2, short y2)
23635c4bbdfSmrg{
23735c4bbdfSmrg    return max(abs(x2 - x1), abs(y2 - y1));
23835c4bbdfSmrg}
23935c4bbdfSmrg
24035c4bbdfSmrgBool
24135c4bbdfSmrgglamor_poly_lines_dash_gl(DrawablePtr drawable, GCPtr gc,
24235c4bbdfSmrg                          int mode, int n, DDXPointPtr points)
24335c4bbdfSmrg{
24435c4bbdfSmrg    ScreenPtr screen = drawable->pScreen;
24535c4bbdfSmrg    glamor_program *prog;
24635c4bbdfSmrg    short *v;
24735c4bbdfSmrg    char *vbo_offset;
24835c4bbdfSmrg    int add_last;
24935c4bbdfSmrg    int dash_pos;
25035c4bbdfSmrg    int prev_x, prev_y;
25135c4bbdfSmrg    int i;
25235c4bbdfSmrg
25335c4bbdfSmrg    if (n < 2)
25435c4bbdfSmrg        return TRUE;
25535c4bbdfSmrg
25635c4bbdfSmrg    if (!(prog = glamor_dash_setup(drawable, gc)))
25735c4bbdfSmrg        return FALSE;
25835c4bbdfSmrg
25935c4bbdfSmrg    add_last = 0;
26035c4bbdfSmrg    if (gc->capStyle != CapNotLast)
26135c4bbdfSmrg        add_last = 1;
26235c4bbdfSmrg
26335c4bbdfSmrg    /* Set up the vertex buffers for the points */
26435c4bbdfSmrg
26535c4bbdfSmrg    v = glamor_get_vbo_space(drawable->pScreen,
26635c4bbdfSmrg                             (n + add_last) * 3 * sizeof (short),
26735c4bbdfSmrg                             &vbo_offset);
26835c4bbdfSmrg
26935c4bbdfSmrg    glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
27035c4bbdfSmrg    glVertexAttribPointer(GLAMOR_VERTEX_POS, 3, GL_SHORT, GL_FALSE,
27135c4bbdfSmrg                          3 * sizeof (short), vbo_offset);
27235c4bbdfSmrg
27335c4bbdfSmrg    dash_pos = gc->dashOffset;
27435c4bbdfSmrg    prev_x = prev_y = 0;
27535c4bbdfSmrg    for (i = 0; i < n; i++) {
27635c4bbdfSmrg        int this_x = points[i].x;
27735c4bbdfSmrg        int this_y = points[i].y;
27835c4bbdfSmrg        if (i) {
27935c4bbdfSmrg            if (mode == CoordModePrevious) {
28035c4bbdfSmrg                this_x += prev_x;
28135c4bbdfSmrg                this_y += prev_y;
28235c4bbdfSmrg            }
28335c4bbdfSmrg            dash_pos += glamor_line_length(prev_x, prev_y,
28435c4bbdfSmrg                                           this_x, this_y);
28535c4bbdfSmrg        }
28635c4bbdfSmrg        v[0] = prev_x = this_x;
28735c4bbdfSmrg        v[1] = prev_y = this_y;
28835c4bbdfSmrg        v[2] = dash_pos;
28935c4bbdfSmrg        v += 3;
29035c4bbdfSmrg    }
29135c4bbdfSmrg
29235c4bbdfSmrg    if (add_last) {
29335c4bbdfSmrg        v[0] = prev_x + 1;
29435c4bbdfSmrg        v[1] = prev_y;
29535c4bbdfSmrg        v[2] = dash_pos + 1;
29635c4bbdfSmrg    }
29735c4bbdfSmrg
29835c4bbdfSmrg    glamor_put_vbo_space(screen);
29935c4bbdfSmrg
30035c4bbdfSmrg    glamor_dash_loop(drawable, gc, prog, n + add_last, GL_LINE_STRIP);
30135c4bbdfSmrg
30235c4bbdfSmrg    return TRUE;
30335c4bbdfSmrg}
30435c4bbdfSmrg
30535c4bbdfSmrgstatic short *
30635c4bbdfSmrgglamor_add_segment(short *v, short x1, short y1, short x2, short y2,
30735c4bbdfSmrg                   int dash_start, int dash_end)
30835c4bbdfSmrg{
30935c4bbdfSmrg    v[0] = x1;
31035c4bbdfSmrg    v[1] = y1;
31135c4bbdfSmrg    v[2] = dash_start;
31235c4bbdfSmrg
31335c4bbdfSmrg    v[3] = x2;
31435c4bbdfSmrg    v[4] = y2;
31535c4bbdfSmrg    v[5] = dash_end;
31635c4bbdfSmrg    return v + 6;
31735c4bbdfSmrg}
31835c4bbdfSmrg
31935c4bbdfSmrgBool
32035c4bbdfSmrgglamor_poly_segment_dash_gl(DrawablePtr drawable, GCPtr gc,
32135c4bbdfSmrg                            int nseg, xSegment *segs)
32235c4bbdfSmrg{
32335c4bbdfSmrg    ScreenPtr screen = drawable->pScreen;
32435c4bbdfSmrg    glamor_program *prog;
32535c4bbdfSmrg    short *v;
32635c4bbdfSmrg    char *vbo_offset;
32735c4bbdfSmrg    int dash_start = gc->dashOffset;
32835c4bbdfSmrg    int add_last;
32935c4bbdfSmrg    int i;
33035c4bbdfSmrg
33135c4bbdfSmrg    if (!(prog = glamor_dash_setup(drawable, gc)))
33235c4bbdfSmrg        return FALSE;
33335c4bbdfSmrg
33435c4bbdfSmrg    add_last = 0;
33535c4bbdfSmrg    if (gc->capStyle != CapNotLast)
33635c4bbdfSmrg        add_last = 1;
33735c4bbdfSmrg
33835c4bbdfSmrg    /* Set up the vertex buffers for the points */
33935c4bbdfSmrg
34035c4bbdfSmrg    v = glamor_get_vbo_space(drawable->pScreen,
34135c4bbdfSmrg                             (nseg<<add_last) * 6 * sizeof (short),
34235c4bbdfSmrg                             &vbo_offset);
34335c4bbdfSmrg
34435c4bbdfSmrg    glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
34535c4bbdfSmrg    glVertexAttribPointer(GLAMOR_VERTEX_POS, 3, GL_SHORT, GL_FALSE,
34635c4bbdfSmrg                          3 * sizeof (short), vbo_offset);
34735c4bbdfSmrg
34835c4bbdfSmrg    for (i = 0; i < nseg; i++) {
34935c4bbdfSmrg        int dash_end = dash_start + glamor_line_length(segs[i].x1, segs[i].y1,
35035c4bbdfSmrg                                                       segs[i].x2, segs[i].y2);
35135c4bbdfSmrg        v = glamor_add_segment(v,
35235c4bbdfSmrg                               segs[i].x1, segs[i].y1,
35335c4bbdfSmrg                               segs[i].x2, segs[i].y2,
35435c4bbdfSmrg                               dash_start, dash_end);
35535c4bbdfSmrg        if (add_last)
35635c4bbdfSmrg            v = glamor_add_segment(v,
35735c4bbdfSmrg                                   segs[i].x2, segs[i].y2,
35835c4bbdfSmrg                                   segs[i].x2 + 1, segs[i].y2,
35935c4bbdfSmrg                                   dash_end, dash_end + 1);
36035c4bbdfSmrg    }
36135c4bbdfSmrg
36235c4bbdfSmrg    glamor_put_vbo_space(screen);
36335c4bbdfSmrg
36435c4bbdfSmrg    glamor_dash_loop(drawable, gc, prog, nseg << (1 + add_last), GL_LINES);
36535c4bbdfSmrg
36635c4bbdfSmrg    return TRUE;
36735c4bbdfSmrg}
368