glamor_text.c (16075B)
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 33 static void 34 glamor_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 90 static int 91 glamor_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 223 static const char vs_vars_text[] = 224 "attribute vec4 primitive;\n" 225 "attribute vec2 source;\n" 226 "varying vec2 glyph_pos;\n"; 227 228 static 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 233 static const char fs_vars_text[] = 234 "varying vec2 glyph_pos;\n"; 235 236 static const char fs_exec_text[] = 237 " ivec2 itile_texture = ivec2(glyph_pos);\n" 238 " uint x = uint(itile_texture.x & 7);\n" 239 " itile_texture.x >>= 3;\n" 240 " uint texel = texelFetch(font, itile_texture, 0).x;\n" 241 " uint bit = (texel >> x) & uint(1);\n" 242 " if (bit == uint(0))\n" 243 " discard;\n"; 244 245 static const char fs_exec_te[] = 246 " ivec2 itile_texture = ivec2(glyph_pos);\n" 247 " uint x = uint(itile_texture.x & 7);\n" 248 " itile_texture.x >>= 3;\n" 249 " uint texel = texelFetch(font, itile_texture, 0).x;\n" 250 " uint bit = (texel >> x) & uint(1);\n" 251 " if (bit == uint(0))\n" 252 " gl_FragColor = bg;\n" 253 " else\n" 254 " gl_FragColor = fg;\n"; 255 256 static const glamor_facet glamor_facet_poly_text = { 257 .name = "poly_text", 258 .version = 130, 259 .vs_vars = vs_vars_text, 260 .vs_exec = vs_exec_text, 261 .fs_vars = fs_vars_text, 262 .fs_exec = fs_exec_text, 263 .source_name = "source", 264 .locations = glamor_program_location_font, 265 }; 266 267 static Bool 268 glamor_poly_text(DrawablePtr drawable, GCPtr gc, 269 int x, int y, int count, char *chars, Bool sixteen, int *final_pos) 270 { 271 ScreenPtr screen = drawable->pScreen; 272 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); 273 PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); 274 glamor_program *prog; 275 glamor_pixmap_private *pixmap_priv; 276 glamor_font_t *glamor_font; 277 CharInfoPtr charinfo[255]; /* encoding only has 1 byte for count */ 278 279 glamor_font = glamor_font_get(drawable->pScreen, gc->font); 280 if (!glamor_font) 281 goto bail; 282 283 glamor_get_glyphs(gc->font, glamor_font, count, chars, sixteen, charinfo); 284 285 pixmap_priv = glamor_get_pixmap_private(pixmap); 286 if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) 287 goto bail; 288 289 glamor_make_current(glamor_priv); 290 291 prog = glamor_use_program_fill(pixmap, gc, &glamor_priv->poly_text_progs, &glamor_facet_poly_text); 292 293 if (!prog) 294 goto bail; 295 296 x = glamor_text(drawable, gc, glamor_font, prog, 297 x, y, count, chars, charinfo, sixteen); 298 299 *final_pos = x; 300 return TRUE; 301 302 bail: 303 return FALSE; 304 } 305 306 int 307 glamor_poly_text8(DrawablePtr drawable, GCPtr gc, 308 int x, int y, int count, char *chars) 309 { 310 int final_pos; 311 312 if (glamor_poly_text(drawable, gc, x, y, count, chars, FALSE, &final_pos)) 313 return final_pos; 314 return miPolyText8(drawable, gc, x, y, count, chars); 315 } 316 317 int 318 glamor_poly_text16(DrawablePtr drawable, GCPtr gc, 319 int x, int y, int count, unsigned short *chars) 320 { 321 int final_pos; 322 323 if (glamor_poly_text(drawable, gc, x, y, count, (char *) chars, TRUE, &final_pos)) 324 return final_pos; 325 return miPolyText16(drawable, gc, x, y, count, chars); 326 } 327 328 /* 329 * Draw image text, which is always solid in copy mode and has the 330 * background cleared while painting the text. For fonts which have 331 * their bitmap metrics exactly equal to the area to clear, we can use 332 * the accelerated version which paints both fg and bg at the same 333 * time. Otherwise, clear the whole area and then paint the glyphs on 334 * top 335 */ 336 337 static const glamor_facet glamor_facet_image_text = { 338 .name = "image_text", 339 .version = 130, 340 .vs_vars = vs_vars_text, 341 .vs_exec = vs_exec_text, 342 .fs_vars = fs_vars_text, 343 .fs_exec = fs_exec_text, 344 .source_name = "source", 345 .locations = glamor_program_location_font, 346 }; 347 348 static Bool 349 use_image_solid(PixmapPtr pixmap, GCPtr gc, glamor_program *prog, void *arg) 350 { 351 return glamor_set_solid(pixmap, gc, FALSE, prog->fg_uniform); 352 } 353 354 static const glamor_facet glamor_facet_image_fill = { 355 .name = "solid", 356 .fs_exec = " gl_FragColor = fg;\n", 357 .locations = glamor_program_location_fg, 358 .use = use_image_solid, 359 }; 360 361 static Bool 362 glamor_te_text_use(PixmapPtr pixmap, GCPtr gc, glamor_program *prog, void *arg) 363 { 364 if (!glamor_set_solid(pixmap, gc, FALSE, prog->fg_uniform)) 365 return FALSE; 366 glamor_set_color(pixmap, gc->bgPixel, prog->bg_uniform); 367 return TRUE; 368 } 369 370 static const glamor_facet glamor_facet_te_text = { 371 .name = "te_text", 372 .version = 130, 373 .vs_vars = vs_vars_text, 374 .vs_exec = vs_exec_text, 375 .fs_vars = fs_vars_text, 376 .fs_exec = fs_exec_te, 377 .locations = glamor_program_location_fg | glamor_program_location_bg | glamor_program_location_font, 378 .source_name = "source", 379 .use = glamor_te_text_use, 380 }; 381 382 static Bool 383 glamor_image_text(DrawablePtr drawable, GCPtr gc, 384 int x, int y, int count, char *chars, 385 Bool sixteen) 386 { 387 ScreenPtr screen = drawable->pScreen; 388 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); 389 PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); 390 glamor_program *prog; 391 glamor_pixmap_private *pixmap_priv; 392 glamor_font_t *glamor_font; 393 const glamor_facet *prim_facet; 394 const glamor_facet *fill_facet; 395 CharInfoPtr charinfo[255]; /* encoding only has 1 byte for count */ 396 397 pixmap_priv = glamor_get_pixmap_private(pixmap); 398 if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) 399 return FALSE; 400 401 glamor_font = glamor_font_get(drawable->pScreen, gc->font); 402 if (!glamor_font) 403 return FALSE; 404 405 glamor_get_glyphs(gc->font, glamor_font, count, chars, sixteen, charinfo); 406 407 glamor_make_current(glamor_priv); 408 409 if (TERMINALFONT(gc->font)) 410 prog = &glamor_priv->te_text_prog; 411 else 412 prog = &glamor_priv->image_text_prog; 413 414 if (prog->failed) 415 goto bail; 416 417 if (!prog->prog) { 418 if (TERMINALFONT(gc->font)) { 419 prim_facet = &glamor_facet_te_text; 420 fill_facet = NULL; 421 } else { 422 prim_facet = &glamor_facet_image_text; 423 fill_facet = &glamor_facet_image_fill; 424 } 425 426 if (!glamor_build_program(screen, prog, prim_facet, fill_facet, NULL, NULL)) 427 goto bail; 428 } 429 430 if (!TERMINALFONT(gc->font)) { 431 int width = 0; 432 int c; 433 RegionRec region; 434 BoxRec box; 435 int off_x, off_y; 436 437 /* Check planemask before drawing background to 438 * bail early if it's not OK 439 */ 440 if (!glamor_set_planemask(gc->depth, gc->planemask)) 441 goto bail; 442 for (c = 0; c < count; c++) 443 if (charinfo[c]) 444 width += charinfo[c]->metrics.characterWidth; 445 446 glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y); 447 448 if (width >= 0) { 449 box.x1 = drawable->x + x; 450 box.x2 = drawable->x + x + width; 451 } else { 452 box.x1 = drawable->x + x + width; 453 box.x2 = drawable->x + x; 454 } 455 box.y1 = drawable->y + y - gc->font->info.fontAscent; 456 box.y2 = drawable->y + y + gc->font->info.fontDescent; 457 RegionInit(®ion, &box, 1); 458 RegionIntersect(®ion, ®ion, gc->pCompositeClip); 459 RegionTranslate(®ion, off_x, off_y); 460 glamor_solid_boxes(pixmap, RegionRects(®ion), RegionNumRects(®ion), gc->bgPixel); 461 RegionUninit(®ion); 462 } 463 464 if (!glamor_use_program(pixmap, gc, prog, NULL)) 465 goto bail; 466 467 (void) glamor_text(drawable, gc, glamor_font, prog, 468 x, y, count, chars, charinfo, sixteen); 469 470 return TRUE; 471 472 bail: 473 return FALSE; 474 } 475 476 void 477 glamor_image_text8(DrawablePtr drawable, GCPtr gc, 478 int x, int y, int count, char *chars) 479 { 480 if (!glamor_image_text(drawable, gc, x, y, count, chars, FALSE)) 481 miImageText8(drawable, gc, x, y, count, chars); 482 } 483 484 void 485 glamor_image_text16(DrawablePtr drawable, GCPtr gc, 486 int x, int y, int count, unsigned short *chars) 487 { 488 if (!glamor_image_text(drawable, gc, x, y, count, (char *) chars, TRUE)) 489 miImageText16(drawable, gc, x, y, count, chars); 490 }