Home | History | Annotate | Line # | Download | only in glamor
      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