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(®ion, &box, 1); 466 RegionIntersect(®ion, ®ion, gc->pCompositeClip); 467 RegionTranslate(®ion, off_x, off_y); 468 glamor_solid_boxes(pixmap, RegionRects(®ion), RegionNumRects(®ion), gc->bgPixel); 469 RegionUninit(®ion); 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