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 <dixfontstr.h>
25#include "glamor_transform.h"
26
27/*
28 * Fill in the array of charinfo pointers for the provided characters. For
29 * missing characters, place a NULL in the array so that the charinfo array
30 * aligns exactly with chars
31 */
32
33static void
34glamor_get_glyphs(FontPtr font, glamor_font_t *glamor_font,
35                  int count, char *chars, Bool sixteen, CharInfoPtr *charinfo)
36{
37    unsigned long nglyphs;
38    FontEncoding encoding;
39    int char_step;
40    int c;
41
42    if (sixteen) {
43        char_step = 2;
44        if (FONTLASTROW(font) == 0)
45            encoding = Linear16Bit;
46        else
47            encoding = TwoD16Bit;
48    } else {
49        char_step = 1;
50        encoding = Linear8Bit;
51    }
52
53    /* If the font has a default character, then we shouldn't have to
54     * worry about missing glyphs, so just get the whole string all at
55     * once. Otherwise, we have to fetch chars one at a time to notice
56     * missing ones.
57     */
58    if (glamor_font->default_char) {
59        GetGlyphs(font, (unsigned long) count, (unsigned char *) chars,
60                  encoding, &nglyphs, charinfo);
61
62        /* Make sure it worked. There's a bug in libXfont through
63         * version 1.4.7 which would cause it to fail when the font is
64         * a 2D font without a first row, and the application sends a
65         * 1-d request. In this case, libXfont would return zero
66         * glyphs, even when the font had a default character.
67         *
68         * It's easy enough for us to work around that bug here by
69         * simply checking the returned nglyphs and falling through to
70         * the one-at-a-time code below. Not doing this check would
71         * result in uninitialized memory accesses in the rendering code.
72         */
73        if (nglyphs == count)
74            return;
75    }
76
77    for (c = 0; c < count; c++) {
78        GetGlyphs(font, 1, (unsigned char *) chars,
79                  encoding, &nglyphs, &charinfo[c]);
80        if (!nglyphs)
81            charinfo[c] = NULL;
82        chars += char_step;
83    }
84}
85
86/*
87 * Construct quads for the provided list of characters and draw them
88 */
89
90static int
91glamor_text(DrawablePtr drawable, GCPtr gc,
92            glamor_font_t *glamor_font,
93            glamor_program *prog,
94            int x, int y,
95            int count, char *s_chars, CharInfoPtr *charinfo,
96            Bool sixteen)
97{
98    unsigned char *chars = (unsigned char *) s_chars;
99    FontPtr font = gc->font;
100    int off_x, off_y;
101    int c;
102    int nglyph;
103    GLshort *v;
104    char *vbo_offset;
105    CharInfoPtr ci;
106    int firstRow = font->info.firstRow;
107    int firstCol = font->info.firstCol;
108    int glyph_spacing_x = glamor_font->glyph_width_bytes * 8;
109    int glyph_spacing_y = glamor_font->glyph_height;
110    int box_index;
111    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
112    glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
113
114    /* Set the font as texture 1 */
115
116    glActiveTexture(GL_TEXTURE1);
117    glBindTexture(GL_TEXTURE_2D, glamor_font->texture_id);
118    glUniform1i(prog->font_uniform, 1);
119
120    /* Set up the vertex buffers for the font and destination */
121
122    v = glamor_get_vbo_space(drawable->pScreen, count * (6 * sizeof (GLshort)), &vbo_offset);
123
124    glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
125    glVertexAttribDivisor(GLAMOR_VERTEX_POS, 1);
126    glVertexAttribPointer(GLAMOR_VERTEX_POS, 4, GL_SHORT, GL_FALSE,
127                          6 * sizeof (GLshort), vbo_offset);
128
129    glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE);
130    glVertexAttribDivisor(GLAMOR_VERTEX_SOURCE, 1);
131    glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_SHORT, GL_FALSE,
132                          6 * sizeof (GLshort), vbo_offset + 4 * sizeof (GLshort));
133
134    /* Set the vertex coordinates */
135    nglyph = 0;
136
137    for (c = 0; c < count; c++) {
138        if ((ci = *charinfo++)) {
139            int     x1 = x + ci->metrics.leftSideBearing;
140            int     y1 = y - ci->metrics.ascent;
141            int     width = GLYPHWIDTHPIXELS(ci);
142            int     height = GLYPHHEIGHTPIXELS(ci);
143            int     tx, ty = 0;
144            int     row = 0, col;
145            int     second_row = 0;
146            x += ci->metrics.characterWidth;
147
148            if (sixteen) {
149                if (ci == glamor_font->default_char) {
150                    row = glamor_font->default_row;
151                    col = glamor_font->default_col;
152                } else {
153                    row = chars[0];
154                    col = chars[1];
155                }
156                if (FONTLASTROW(font) != 0) {
157                    ty = ((row - firstRow) / 2) * glyph_spacing_y;
158                    second_row = (row - firstRow) & 1;
159                }
160                else
161                    col += row << 8;
162            } else {
163                if (ci == glamor_font->default_char)
164                    col = glamor_font->default_col;
165                else
166                    col = chars[0];
167            }
168
169            tx = (col - firstCol) * glyph_spacing_x;
170            /* adjust for second row layout */
171            tx += second_row * glamor_font->row_width * 8;
172
173            v[ 0] = x1;
174            v[ 1] = y1;
175            v[ 2] = width;
176            v[ 3] = height;
177            v[ 4] = tx;
178            v[ 5] = ty;
179
180            v += 6;
181            nglyph++;
182        }
183        chars += 1 + sixteen;
184    }
185    glamor_put_vbo_space(drawable->pScreen);
186
187    if (nglyph != 0) {
188
189        glEnable(GL_SCISSOR_TEST);
190
191        glamor_pixmap_loop(pixmap_priv, box_index) {
192            BoxPtr box = RegionRects(gc->pCompositeClip);
193            int nbox = RegionNumRects(gc->pCompositeClip);
194
195            glamor_set_destination_drawable(drawable, box_index, TRUE, FALSE,
196                                            prog->matrix_uniform,
197                                            &off_x, &off_y);
198
199            /* Run over the clip list, drawing the glyphs
200             * in each box
201             */
202
203            while (nbox--) {
204                glScissor(box->x1 + off_x,
205                          box->y1 + off_y,
206                          box->x2 - box->x1,
207                          box->y2 - box->y1);
208                box++;
209                glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, nglyph);
210            }
211        }
212        glDisable(GL_SCISSOR_TEST);
213    }
214
215    glVertexAttribDivisor(GLAMOR_VERTEX_SOURCE, 0);
216    glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE);
217    glVertexAttribDivisor(GLAMOR_VERTEX_POS, 0);
218    glDisableVertexAttribArray(GLAMOR_VERTEX_POS);
219
220    return x;
221}
222
223static const char vs_vars_text[] =
224    "attribute vec4 primitive;\n"
225    "attribute vec2 source;\n"
226    "varying vec2 glyph_pos;\n";
227
228static const char vs_exec_text[] =
229    "       vec2 pos = primitive.zw * vec2(gl_VertexID&1, (gl_VertexID&2)>>1);\n"
230    GLAMOR_POS(gl_Position, (primitive.xy + pos))
231    "       glyph_pos = source + pos;\n";
232
233static const char fs_vars_text[] =
234    "varying vec2 glyph_pos;\n";
235
236static const char fs_exec_text[] =
237    "       ivec2 itile_texture = ivec2(glyph_pos);\n"
238#if BITMAP_BIT_ORDER == MSBFirst
239    "       uint x = uint(7) - uint(itile_texture.x & 7);\n"
240#else
241    "       uint x = uint(itile_texture.x & 7);\n"
242#endif
243    "       itile_texture.x >>= 3;\n"
244    "       uint texel = texelFetch(font, itile_texture, 0).x;\n"
245    "       uint bit = (texel >> x) & uint(1);\n"
246    "       if (bit == uint(0))\n"
247    "               discard;\n";
248
249static const char fs_exec_te[] =
250    "       ivec2 itile_texture = ivec2(glyph_pos);\n"
251#if BITMAP_BIT_ORDER == MSBFirst
252    "       uint x = uint(7) - uint(itile_texture.x & 7);\n"
253#else
254    "       uint x = uint(itile_texture.x & 7);\n"
255#endif
256    "       itile_texture.x >>= 3;\n"
257    "       uint texel = texelFetch(font, itile_texture, 0).x;\n"
258    "       uint bit = (texel >> x) & uint(1);\n"
259    "       if (bit == uint(0))\n"
260    "               gl_FragColor = bg;\n"
261    "       else\n"
262    "               gl_FragColor = fg;\n";
263
264static const glamor_facet glamor_facet_poly_text = {
265    .name = "poly_text",
266    .version = 130,
267    .vs_vars = vs_vars_text,
268    .vs_exec = vs_exec_text,
269    .fs_vars = fs_vars_text,
270    .fs_exec = fs_exec_text,
271    .source_name = "source",
272    .locations = glamor_program_location_font,
273};
274
275static Bool
276glamor_poly_text(DrawablePtr drawable, GCPtr gc,
277                 int x, int y, int count, char *chars, Bool sixteen, int *final_pos)
278{
279    ScreenPtr screen = drawable->pScreen;
280    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
281    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
282    glamor_program *prog;
283    glamor_pixmap_private *pixmap_priv;
284    glamor_font_t *glamor_font;
285    CharInfoPtr charinfo[255];  /* encoding only has 1 byte for count */
286
287    glamor_font = glamor_font_get(drawable->pScreen, gc->font);
288    if (!glamor_font)
289        goto bail;
290
291    glamor_get_glyphs(gc->font, glamor_font, count, chars, sixteen, charinfo);
292
293    pixmap_priv = glamor_get_pixmap_private(pixmap);
294    if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv))
295        goto bail;
296
297    glamor_make_current(glamor_priv);
298
299    prog = glamor_use_program_fill(pixmap, gc, &glamor_priv->poly_text_progs, &glamor_facet_poly_text);
300
301    if (!prog)
302        goto bail;
303
304    x = glamor_text(drawable, gc, glamor_font, prog,
305                    x, y, count, chars, charinfo, sixteen);
306
307    *final_pos = x;
308    return TRUE;
309
310bail:
311    return FALSE;
312}
313
314int
315glamor_poly_text8(DrawablePtr drawable, GCPtr gc,
316                   int x, int y, int count, char *chars)
317{
318    int final_pos;
319
320    if (glamor_poly_text(drawable, gc, x, y, count, chars, FALSE, &final_pos))
321        return final_pos;
322    return miPolyText8(drawable, gc, x, y, count, chars);
323}
324
325int
326glamor_poly_text16(DrawablePtr drawable, GCPtr gc,
327                    int x, int y, int count, unsigned short *chars)
328{
329    int final_pos;
330
331    if (glamor_poly_text(drawable, gc, x, y, count, (char *) chars, TRUE, &final_pos))
332        return final_pos;
333    return miPolyText16(drawable, gc, x, y, count, chars);
334}
335
336/*
337 * Draw image text, which is always solid in copy mode and has the
338 * background cleared while painting the text. For fonts which have
339 * their bitmap metrics exactly equal to the area to clear, we can use
340 * the accelerated version which paints both fg and bg at the same
341 * time. Otherwise, clear the whole area and then paint the glyphs on
342 * top
343 */
344
345static const glamor_facet glamor_facet_image_text = {
346    .name = "image_text",
347    .version = 130,
348    .vs_vars = vs_vars_text,
349    .vs_exec = vs_exec_text,
350    .fs_vars = fs_vars_text,
351    .fs_exec = fs_exec_text,
352    .source_name = "source",
353    .locations = glamor_program_location_font,
354};
355
356static Bool
357use_image_solid(PixmapPtr pixmap, GCPtr gc, glamor_program *prog, void *arg)
358{
359    return glamor_set_solid(pixmap, gc, FALSE, prog->fg_uniform);
360}
361
362static const glamor_facet glamor_facet_image_fill = {
363    .name = "solid",
364    .fs_exec = "       gl_FragColor = fg;\n",
365    .locations = glamor_program_location_fg,
366    .use = use_image_solid,
367};
368
369static Bool
370glamor_te_text_use(PixmapPtr pixmap, GCPtr gc, glamor_program *prog, void *arg)
371{
372    if (!glamor_set_solid(pixmap, gc, FALSE, prog->fg_uniform))
373        return FALSE;
374    glamor_set_color(pixmap, gc->bgPixel, prog->bg_uniform);
375    return TRUE;
376}
377
378static const glamor_facet glamor_facet_te_text = {
379    .name = "te_text",
380    .version = 130,
381    .vs_vars = vs_vars_text,
382    .vs_exec = vs_exec_text,
383    .fs_vars = fs_vars_text,
384    .fs_exec = fs_exec_te,
385    .locations = glamor_program_location_fg | glamor_program_location_bg | glamor_program_location_font,
386    .source_name = "source",
387    .use = glamor_te_text_use,
388};
389
390static Bool
391glamor_image_text(DrawablePtr drawable, GCPtr gc,
392                  int x, int y, int count, char *chars,
393                  Bool sixteen)
394{
395    ScreenPtr screen = drawable->pScreen;
396    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
397    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
398    glamor_program *prog;
399    glamor_pixmap_private *pixmap_priv;
400    glamor_font_t *glamor_font;
401    const glamor_facet *prim_facet;
402    const glamor_facet *fill_facet;
403    CharInfoPtr charinfo[255];  /* encoding only has 1 byte for count */
404
405    pixmap_priv = glamor_get_pixmap_private(pixmap);
406    if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv))
407        return FALSE;
408
409    glamor_font = glamor_font_get(drawable->pScreen, gc->font);
410    if (!glamor_font)
411        return FALSE;
412
413    glamor_get_glyphs(gc->font, glamor_font, count, chars, sixteen, charinfo);
414
415    glamor_make_current(glamor_priv);
416
417    if (TERMINALFONT(gc->font))
418        prog = &glamor_priv->te_text_prog;
419    else
420        prog = &glamor_priv->image_text_prog;
421
422    if (prog->failed)
423        goto bail;
424
425    if (!prog->prog) {
426        if (TERMINALFONT(gc->font)) {
427            prim_facet = &glamor_facet_te_text;
428            fill_facet = NULL;
429        } else {
430            prim_facet = &glamor_facet_image_text;
431            fill_facet = &glamor_facet_image_fill;
432        }
433
434        if (!glamor_build_program(screen, prog, prim_facet, fill_facet, NULL, NULL))
435            goto bail;
436    }
437
438    if (!TERMINALFONT(gc->font)) {
439        int width = 0;
440        int c;
441        RegionRec region;
442        BoxRec box;
443        int off_x, off_y;
444
445        /* Check planemask before drawing background to
446         * bail early if it's not OK
447         */
448        if (!glamor_set_planemask(gc->depth, gc->planemask))
449            goto bail;
450        for (c = 0; c < count; c++)
451            if (charinfo[c])
452                width += charinfo[c]->metrics.characterWidth;
453
454        glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y);
455
456        if (width >= 0) {
457            box.x1 = drawable->x + x;
458            box.x2 = drawable->x + x + width;
459        } else {
460            box.x1 = drawable->x + x + width;
461            box.x2 = drawable->x + x;
462        }
463        box.y1 = drawable->y + y - gc->font->info.fontAscent;
464        box.y2 = drawable->y + y + gc->font->info.fontDescent;
465        RegionInit(&region, &box, 1);
466        RegionIntersect(&region, &region, gc->pCompositeClip);
467        RegionTranslate(&region, off_x, off_y);
468        glamor_solid_boxes(pixmap, RegionRects(&region), RegionNumRects(&region), gc->bgPixel);
469        RegionUninit(&region);
470    }
471
472    if (!glamor_use_program(pixmap, gc, prog, NULL))
473        goto bail;
474
475    (void) glamor_text(drawable, gc, glamor_font, prog,
476                       x, y, count, chars, charinfo, sixteen);
477
478    return TRUE;
479
480bail:
481    return FALSE;
482}
483
484void
485glamor_image_text8(DrawablePtr drawable, GCPtr gc,
486                   int x, int y, int count, char *chars)
487{
488    if (!glamor_image_text(drawable, gc, x, y, count, chars, FALSE))
489        miImageText8(drawable, gc, x, y, count, chars);
490}
491
492void
493glamor_image_text16(DrawablePtr drawable, GCPtr gc,
494                    int x, int y, int count, unsigned short *chars)
495{
496    if (!glamor_image_text(drawable, gc, x, y, count, (char *) chars, TRUE))
497        miImageText16(drawable, gc, x, y, count, chars);
498}
499