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 "glamor_font.h" 25 #include <dixfontstr.h> 26 27 static int glamor_font_generation; 28 static int glamor_font_private_index; 29 static int glamor_font_screen_count; 30 31 glamor_font_t * 32 glamor_font_get(ScreenPtr screen, FontPtr font) 33 { 34 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); 35 36 glamor_font_t *privates; 37 glamor_font_t *glamor_font; 38 int overall_width, overall_height; 39 int num_rows; 40 int num_cols; 41 int glyph_width_pixels; 42 int glyph_width_bytes; 43 int glyph_height; 44 int row, col; 45 unsigned char c[2]; 46 CharInfoPtr glyph; 47 unsigned long count; 48 char *bits; 49 50 if (!glamor_glsl_has_ints(glamor_priv)) 51 return NULL; 52 53 privates = FontGetPrivate(font, glamor_font_private_index); 54 if (!privates) { 55 privates = calloc(glamor_font_screen_count, sizeof (glamor_font_t)); 56 if (!privates) 57 return NULL; 58 xfont2_font_set_private(font, glamor_font_private_index, privates); 59 } 60 61 glamor_font = &privates[screen->myNum]; 62 63 if (glamor_font->realized) 64 return glamor_font; 65 66 /* Figure out how many glyphs are in the font */ 67 num_cols = font->info.lastCol - font->info.firstCol + 1; 68 num_rows = font->info.lastRow - font->info.firstRow + 1; 69 70 /* Figure out the size of each glyph */ 71 glyph_width_pixels = font->info.maxbounds.rightSideBearing - font->info.minbounds.leftSideBearing; 72 glyph_height = font->info.maxbounds.ascent + font->info.maxbounds.descent; 73 74 if (glyph_width_pixels <= 0 || glyph_height <= 0) 75 return NULL; 76 77 glyph_width_bytes = (glyph_width_pixels + 7) >> 3; 78 79 glamor_font->glyph_width_pixels = glyph_width_pixels; 80 glamor_font->glyph_width_bytes = glyph_width_bytes; 81 glamor_font->glyph_height = glyph_height; 82 83 /* 84 * Layout the font two blocks of columns wide. 85 * This avoids a problem with some fonts that are too high to fit. 86 */ 87 glamor_font->row_width = glyph_width_bytes * num_cols; 88 89 if (num_rows > 1) { 90 overall_width = glamor_font->row_width * 2; 91 overall_height = glyph_height * ((num_rows + 1) / 2); 92 } else { 93 overall_width = glamor_font->row_width; 94 overall_height = glyph_height; 95 } 96 97 if (overall_width > glamor_priv->max_fbo_size || 98 overall_height > glamor_priv->max_fbo_size) { 99 /* fallback if we don't fit inside a texture */ 100 return NULL; 101 } 102 bits = malloc(overall_width * overall_height); 103 if (!bits) 104 return NULL; 105 106 /* Check whether the font has a default character */ 107 c[0] = font->info.lastRow + 1; 108 c[1] = font->info.lastCol + 1; 109 (*font->get_glyphs)(font, 1, c, TwoD16Bit, &count, &glyph); 110 111 glamor_font->default_char = count ? glyph : NULL; 112 glamor_font->default_row = font->info.defaultCh >> 8; 113 glamor_font->default_col = font->info.defaultCh; 114 115 glamor_priv = glamor_get_screen_private(screen); 116 glamor_make_current(glamor_priv); 117 118 glGenTextures(1, &glamor_font->texture_id); 119 glActiveTexture(GL_TEXTURE0); 120 glBindTexture(GL_TEXTURE_2D, glamor_font->texture_id); 121 122 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 123 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 124 125 /* Paint all of the glyphs */ 126 for (row = 0; row < num_rows; row++) { 127 for (col = 0; col < num_cols; col++) { 128 c[0] = row + font->info.firstRow; 129 c[1] = col + font->info.firstCol; 130 131 (*font->get_glyphs)(font, 1, c, TwoD16Bit, &count, &glyph); 132 133 if (count) { 134 char *dst; 135 char *src = glyph->bits; 136 int gw = GLYPHWIDTHBYTES(glyph); 137 int gh = GLYPHHEIGHTPIXELS(glyph); 138 139 /* Reject fonts where any per-glyph metric is negative 140 * or exceeds the atlas slot size derived from maxbounds. 141 * The PCF parser in libXfont2 does not recompute 142 * maxbounds from per-glyph data, so a crafted PCF file 143 * can violate the maxbounds invariant. 144 * 145 * gw is passed as size_t to memcpy and a negative value 146 * would thus result in OOB access. 147 * 148 * Returning NULL makes glamor fall back to software 149 * rendering. 150 */ 151 if (gw < 0 || gh < 0 || 152 gw > glyph_width_bytes || gh > glyph_height) { 153 glDeleteTextures(1, &glamor_font->texture_id); 154 glamor_font->texture_id = 0; 155 free(bits); 156 return NULL; 157 } 158 159 dst = bits; 160 /* get offset of start of first row */ 161 dst += (row / 2) * glyph_height * overall_width; 162 /* add offset into second row */ 163 dst += (row & 1) ? glamor_font->row_width : 0; 164 165 dst += col * glyph_width_bytes; 166 for (int y = 0; y < gh; y++) { 167 memcpy(dst, src, gw); 168 dst += overall_width; 169 src += GLYPHWIDTHBYTESPADDED(glyph); 170 } 171 } 172 } 173 } 174 175 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 176 177 glamor_priv->suppress_gl_out_of_memory_logging = true; 178 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, overall_width, overall_height, 179 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, bits); 180 glamor_priv->suppress_gl_out_of_memory_logging = false; 181 free(bits); 182 183 if (glGetError() == GL_OUT_OF_MEMORY) { 184 glDeleteTextures(1, &glamor_font->texture_id); 185 glamor_font->texture_id = 0; 186 return NULL; 187 } 188 189 glamor_font->realized = TRUE; 190 191 return glamor_font; 192 } 193 194 static Bool 195 glamor_realize_font(ScreenPtr screen, FontPtr font) 196 { 197 return TRUE; 198 } 199 200 static Bool 201 glamor_unrealize_font(ScreenPtr screen, FontPtr font) 202 { 203 glamor_screen_private *glamor_priv; 204 glamor_font_t *privates = FontGetPrivate(font, glamor_font_private_index); 205 glamor_font_t *glamor_font; 206 int s; 207 208 if (!privates) 209 return TRUE; 210 211 glamor_font = &privates[screen->myNum]; 212 213 if (!glamor_font->realized) 214 return TRUE; 215 216 /* Unrealize the font, freeing the allocated texture */ 217 glamor_font->realized = FALSE; 218 219 glamor_priv = glamor_get_screen_private(screen); 220 glamor_make_current(glamor_priv); 221 glDeleteTextures(1, &glamor_font->texture_id); 222 223 /* Check to see if all of the screens are done with this font 224 * and free the private when that happens 225 */ 226 for (s = 0; s < glamor_font_screen_count; s++) 227 if (privates[s].realized) 228 return TRUE; 229 230 free(privates); 231 xfont2_font_set_private(font, glamor_font_private_index, NULL); 232 return TRUE; 233 } 234 235 Bool 236 glamor_font_init(ScreenPtr screen) 237 { 238 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); 239 240 if (!glamor_glsl_has_ints(glamor_priv)) 241 return TRUE; 242 243 if (glamor_font_generation != serverGeneration) { 244 glamor_font_private_index = xfont2_allocate_font_private_index(); 245 if (glamor_font_private_index == -1) 246 return FALSE; 247 glamor_font_screen_count = 0; 248 glamor_font_generation = serverGeneration; 249 } 250 251 if (screen->myNum >= glamor_font_screen_count) 252 glamor_font_screen_count = screen->myNum + 1; 253 254 screen->RealizeFont = glamor_realize_font; 255 screen->UnrealizeFont = glamor_unrealize_font; 256 return TRUE; 257 } 258