diff options
Diffstat (limited to 'xorg-server/glamor/glamor_text.c')
-rw-r--r-- | xorg-server/glamor/glamor_text.c | 526 |
1 files changed, 526 insertions, 0 deletions
diff --git a/xorg-server/glamor/glamor_text.c b/xorg-server/glamor/glamor_text.c new file mode 100644 index 000000000..0e4b74c56 --- /dev/null +++ b/xorg-server/glamor/glamor_text.c @@ -0,0 +1,526 @@ +/* + * Copyright © 2014 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "glamor_priv.h" +#include <dixfontstr.h> +#include "glamor_transform.h" + +/* + * Fill in the array of charinfo pointers for the provided characters. For + * missing characters, place a NULL in the array so that the charinfo array + * aligns exactly with chars + */ + +static void +glamor_get_glyphs(FontPtr font, glamor_font_t *glamor_font, + int count, char *chars, Bool sixteen, CharInfoPtr *charinfo) +{ + unsigned long nglyphs; + FontEncoding encoding; + int char_step; + + if (sixteen) { + char_step = 2; + if (FONTLASTROW(font) == 0) + encoding = Linear16Bit; + else + encoding = TwoD16Bit; + } else { + char_step = 1; + encoding = Linear8Bit; + } + + /* If the font has a default character, then we don't have to + * worry about missing glyphs, so just get the whole string all at + * once. Otherwise, we have to fetch chars one at a time to notice + * missing ones. + */ + if (glamor_font->default_char) { + GetGlyphs(font, (unsigned long) count, (unsigned char *) chars, + encoding, &nglyphs, charinfo); + } else { + int c; + for (c = 0; c < count; c++) { + GetGlyphs(font, 1, (unsigned char *) chars, + encoding, &nglyphs, &charinfo[c]); + if (!nglyphs) + charinfo[c] = NULL; + chars += char_step; + } + } +} + +/* + * Construct quads for the provided list of characters and draw them + */ + +static int +glamor_text(DrawablePtr drawable, GCPtr gc, + glamor_font_t *glamor_font, + glamor_program *prog, + int x, int y, + int count, char *s_chars, CharInfoPtr *charinfo, + Bool sixteen) +{ + unsigned char *chars = (unsigned char *) s_chars; + FontPtr font = gc->font; + int off_x, off_y; + int c; + int nglyph; + GLshort *v; + char *vbo_offset; + CharInfoPtr ci; + int firstRow = font->info.firstRow; + int firstCol = font->info.firstCol; + int glyph_spacing_x = glamor_font->glyph_width_bytes * 8; + int glyph_spacing_y = glamor_font->glyph_height; + int box_x, box_y; + PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); + glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap); + + /* Set the font as texture 1 */ + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, glamor_font->texture_id); + glUniform1i(prog->font_uniform, 1); + + /* Set up the vertex buffers for the font and destination */ + + v = glamor_get_vbo_space(drawable->pScreen, count * (6 * sizeof (GLshort)), &vbo_offset); + + glEnableVertexAttribArray(GLAMOR_VERTEX_POS); + glVertexAttribDivisor(GLAMOR_VERTEX_POS, 1); + glVertexAttribPointer(GLAMOR_VERTEX_POS, 4, GL_SHORT, GL_FALSE, + 6 * sizeof (GLshort), vbo_offset); + + glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + glVertexAttribDivisor(GLAMOR_VERTEX_SOURCE, 1); + glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_SHORT, GL_FALSE, + 6 * sizeof (GLshort), vbo_offset + 4 * sizeof (GLshort)); + + /* Set the vertex coordinates */ + nglyph = 0; + + for (c = 0; c < count; c++) { + if ((ci = *charinfo++)) { + int x1 = x + ci->metrics.leftSideBearing; + int y1 = y - ci->metrics.ascent; + int width = GLYPHWIDTHPIXELS(ci); + int height = GLYPHHEIGHTPIXELS(ci); + int tx, ty = 0; + int row = 0, col; + + x += ci->metrics.characterWidth; + + if (sixteen) { + if (ci == glamor_font->default_char) { + row = glamor_font->default_row; + col = glamor_font->default_col; + } else { + row = chars[0]; + col = chars[1]; + } + if (FONTLASTROW(font) != 0) + ty = (row - firstRow) * glyph_spacing_y; + else + col += row << 8; + } else { + if (ci == glamor_font->default_char) + col = glamor_font->default_col; + else + col = chars[0]; + } + + tx = (col - firstCol) * glyph_spacing_x; + + v[ 0] = x1; + v[ 1] = y1; + v[ 2] = width; + v[ 3] = height; + v[ 4] = tx; + v[ 5] = ty; + + v += 6; + nglyph++; + } + chars += 1 + sixteen; + } + glamor_put_vbo_space(drawable->pScreen); + + if (nglyph != 0) { + + glEnable(GL_SCISSOR_TEST); + + glamor_pixmap_loop(pixmap_priv, box_x, box_y) { + BoxPtr box = RegionRects(gc->pCompositeClip); + int nbox = RegionNumRects(gc->pCompositeClip); + + glamor_set_destination_drawable(drawable, box_x, box_y, TRUE, FALSE, prog->matrix_uniform, &off_x, &off_y); + + /* Run over the clip list, drawing the glyphs + * in each box + */ + + while (nbox--) { + glScissor(box->x1 + off_x, + box->y1 + off_y, + box->x2 - box->x1, + box->y2 - box->y1); + box++; + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, nglyph); + } + } + glDisable(GL_SCISSOR_TEST); + } + + glVertexAttribDivisor(GLAMOR_VERTEX_SOURCE, 0); + glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + glVertexAttribDivisor(GLAMOR_VERTEX_POS, 0); + glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + glDisable(GL_COLOR_LOGIC_OP); + + return x; +} + +static const char vs_vars_text[] = + "attribute vec4 primitive;\n" + "attribute vec2 source;\n" + "varying vec2 glyph_pos;\n"; + +static const char vs_exec_text[] = + " vec2 pos = primitive.zw * vec2(gl_VertexID&1, (gl_VertexID&2)>>1);\n" + GLAMOR_POS(gl_Position, (primitive.xy + pos)) + " glyph_pos = source + pos;\n"; + +static const char fs_vars_text[] = + "varying vec2 glyph_pos;\n"; + +static const char fs_exec_text[] = + " ivec2 itile_texture = ivec2(glyph_pos);\n" + " uint x = uint(itile_texture.x & 7);\n" + " itile_texture.x >>= 3;\n" + " uint texel = texelFetch(font, itile_texture, 0).x;\n" + " uint bit = (texel >> x) & uint(1);\n" + " if (bit == uint(0))\n" + " discard;\n"; + +static const char fs_exec_te[] = + " ivec2 itile_texture = ivec2(glyph_pos);\n" + " uint x = uint(itile_texture.x & 7);\n" + " itile_texture.x >>= 3;\n" + " uint texel = texelFetch(font, itile_texture, 0).x;\n" + " uint bit = (texel >> x) & uint(1);\n" + " if (bit == uint(0))\n" + " gl_FragColor = bg;\n" + " else\n" + " gl_FragColor = fg;\n"; + +static const glamor_facet glamor_facet_poly_text = { + .name = "poly_text", + .version = 130, + .vs_vars = vs_vars_text, + .vs_exec = vs_exec_text, + .fs_vars = fs_vars_text, + .fs_exec = fs_exec_text, + .source_name = "source", + .locations = glamor_program_location_font, +}; + +static Bool +glamor_poly_text(DrawablePtr drawable, GCPtr gc, + int x, int y, int count, char *chars, Bool sixteen, int *final_pos) +{ + ScreenPtr screen = drawable->pScreen; + glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); + PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); + glamor_program *prog; + glamor_pixmap_private *pixmap_priv; + glamor_font_t *glamor_font; + CharInfoPtr charinfo[255]; /* encoding only has 1 byte for count */ + + glamor_font = glamor_font_get(drawable->pScreen, gc->font); + if (!glamor_font) + goto bail; + + glamor_get_glyphs(gc->font, glamor_font, count, chars, sixteen, charinfo); + + pixmap_priv = glamor_get_pixmap_private(pixmap); + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) + goto bail; + + glamor_get_context(glamor_priv); + + prog = glamor_use_program_fill(pixmap, gc, &glamor_priv->poly_text_progs, &glamor_facet_poly_text); + + if (!prog) + goto bail_ctx; + + x = glamor_text(drawable, gc, glamor_font, prog, + x, y, count, chars, charinfo, sixteen); + + glDisable(GL_COLOR_LOGIC_OP); + + glamor_put_context(glamor_priv); + + glamor_priv->state = RENDER_STATE; + glamor_priv->render_idle_cnt = 0; + + *final_pos = x; + return TRUE; + +bail_ctx: + glDisable(GL_COLOR_LOGIC_OP); + glamor_put_context(glamor_priv); +bail: + return FALSE; +} + +Bool +glamor_poly_text8_nf(DrawablePtr drawable, GCPtr gc, + int x, int y, int count, char *chars, int *final_pos) +{ + if (glamor_poly_text(drawable, gc, x, y, count, chars, FALSE, final_pos)) + return TRUE; + if (glamor_ddx_fallback_check_pixmap(drawable) && glamor_ddx_fallback_check_gc(gc)) + return FALSE; + *final_pos = miPolyText8(drawable, gc, x, y, count, chars); + return TRUE; +} + +int +glamor_poly_text8(DrawablePtr drawable, GCPtr gc, + int x, int y, int count, char *chars) +{ + int final_pos; + + if (glamor_poly_text(drawable, gc, x, y, count, chars, FALSE, &final_pos)) + return final_pos; + return miPolyText8(drawable, gc, x, y, count, chars); +} + +Bool +glamor_poly_text16_nf(DrawablePtr drawable, GCPtr gc, + int x, int y, int count, unsigned short *chars, int *final_pos) +{ + if (glamor_poly_text(drawable, gc, x, y, count, (char *) chars, TRUE, final_pos)) + return TRUE; + + if (glamor_ddx_fallback_check_pixmap(drawable) && glamor_ddx_fallback_check_gc(gc)) + return FALSE; + *final_pos = miPolyText16(drawable, gc, x, y, count, chars); + return TRUE; +} + +int +glamor_poly_text16(DrawablePtr drawable, GCPtr gc, + int x, int y, int count, unsigned short *chars) +{ + int final_pos; + + if (glamor_poly_text(drawable, gc, x, y, count, (char *) chars, TRUE, &final_pos)) + return final_pos; + return miPolyText16(drawable, gc, x, y, count, chars); +} + +/* + * Draw image text, which is always solid in copy mode and has the + * background cleared while painting the text. For fonts which have + * their bitmap metrics exactly equal to the area to clear, we can use + * the accelerated version which paints both fg and bg at the same + * time. Otherwise, clear the whole area and then paint the glyphs on + * top + */ + +static const glamor_facet glamor_facet_image_text = { + .name = "image_text", + .version = 130, + .vs_vars = vs_vars_text, + .vs_exec = vs_exec_text, + .fs_vars = fs_vars_text, + .fs_exec = fs_exec_text, + .source_name = "source", + .locations = glamor_program_location_font, +}; + +static Bool +use_image_solid(PixmapPtr pixmap, GCPtr gc, glamor_program *prog, void *arg) +{ + return glamor_set_solid(pixmap, gc, FALSE, prog->fg_uniform); +} + +static const glamor_facet glamor_facet_image_fill = { + .name = "solid", + .fs_exec = " gl_FragColor = fg;\n", + .locations = glamor_program_location_fg, + .use = use_image_solid, +}; + +static Bool +glamor_te_text_use(PixmapPtr pixmap, GCPtr gc, glamor_program *prog, void *arg) +{ + if (!glamor_set_solid(pixmap, gc, FALSE, prog->fg_uniform)) + return FALSE; + glamor_set_color(pixmap, gc->bgPixel, prog->bg_uniform); + return TRUE; +} + +static const glamor_facet glamor_facet_te_text = { + .name = "te_text", + .version = 130, + .vs_vars = vs_vars_text, + .vs_exec = vs_exec_text, + .fs_vars = fs_vars_text, + .fs_exec = fs_exec_te, + .locations = glamor_program_location_fg | glamor_program_location_bg | glamor_program_location_font, + .source_name = "source", + .use = glamor_te_text_use, +}; + +static Bool +glamor_image_text(DrawablePtr drawable, GCPtr gc, + int x, int y, int count, char *chars, + Bool sixteen) +{ + ScreenPtr screen = drawable->pScreen; + glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); + PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); + glamor_program *prog; + glamor_pixmap_private *pixmap_priv; + glamor_font_t *glamor_font; + const glamor_facet *prim_facet; + const glamor_facet *fill_facet; + CharInfoPtr charinfo[255]; /* encoding only has 1 byte for count */ + + pixmap_priv = glamor_get_pixmap_private(pixmap); + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) + return FALSE; + + glamor_font = glamor_font_get(drawable->pScreen, gc->font); + if (!glamor_font) + return FALSE; + + glamor_get_glyphs(gc->font, glamor_font, count, chars, sixteen, charinfo); + + glamor_get_context(glamor_priv); + + if (TERMINALFONT(gc->font)) + prog = &glamor_priv->te_text_prog; + else + prog = &glamor_priv->image_text_prog; + + if (prog->failed) + goto bail; + + if (!prog->prog) { + if (TERMINALFONT(gc->font)) { + prim_facet = &glamor_facet_te_text; + fill_facet = NULL; + } else { + prim_facet = &glamor_facet_image_text; + fill_facet = &glamor_facet_image_fill; + } + + if (!glamor_build_program(screen, prog, prim_facet, fill_facet)) + goto bail; + } + + if (!TERMINALFONT(gc->font)) { + int width = 0; + int c; + RegionRec region; + BoxRec box; + int off_x, off_y; + + /* Check planemask before drawing background to + * bail early if it's not OK + */ + if (!glamor_set_planemask(pixmap, gc->planemask)) + goto bail; + for (c = 0; c < count; c++) + if (charinfo[c]) + width += charinfo[c]->metrics.characterWidth; + + glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y); + + if (width >= 0) { + box.x1 = off_x + drawable->x + x; + box.x2 = off_x + drawable->x + x + width; + } else { + box.x1 = off_x + drawable->x + x + width; + box.x2 = off_x + drawable->x + x; + } + box.y1 = off_y + drawable->y + y - gc->font->info.fontAscent; + box.y2 = off_y + drawable->y + y + gc->font->info.fontDescent; + RegionInit(®ion, &box, 1); + RegionIntersect(®ion, ®ion, gc->pCompositeClip); + glamor_solid_boxes(pixmap, RegionRects(®ion), RegionNumRects(®ion), gc->bgPixel); + RegionUninit(®ion); + } + + if (!glamor_use_program(pixmap, gc, prog, NULL)) + goto bail; + + (void) glamor_text(drawable, gc, glamor_font, prog, + x, y, count, chars, charinfo, sixteen); + + glamor_put_context(glamor_priv); + + glamor_priv->state = RENDER_STATE; + glamor_priv->render_idle_cnt = 0; + + return TRUE; + +bail: + glDisable(GL_COLOR_LOGIC_OP); + glamor_put_context(glamor_priv); + return FALSE; +} + +Bool +glamor_image_text8_nf(DrawablePtr drawable, GCPtr gc, + int x, int y, int count, char *chars) +{ + return glamor_image_text(drawable, gc, x, y, count, chars, FALSE); +} + +void +glamor_image_text8(DrawablePtr drawable, GCPtr gc, + int x, int y, int count, char *chars) +{ + if (!glamor_image_text(drawable, gc, x, y, count, chars, FALSE)) + miImageText8(drawable, gc, x, y, count, chars); +} + +Bool +glamor_image_text16_nf(DrawablePtr drawable, GCPtr gc, + int x, int y, int count, unsigned short *chars) +{ + return glamor_image_text(drawable, gc, x, y, count, (char *) chars, FALSE); +} + +void +glamor_image_text16(DrawablePtr drawable, GCPtr gc, + int x, int y, int count, unsigned short *chars) +{ + if (!glamor_image_text(drawable, gc, x, y, count, (char *) chars, TRUE)) + miImageText16(drawable, gc, x, y, count, chars); +} |