summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas E. Dickey <dickey@invisible-island.net>2022-07-09 09:04:18 -0400
committerThomas E. Dickey <dickey@invisible-island.net>2022-07-09 10:13:56 -0400
commit3ca7a7c375a8c022c068e2534c5a6861e547eaef (patch)
treeb2f6fde63222a2e084f83884375975f565b3bb6e
parentc53a1bc27c11e0c3cdf54e2beffd6cc220703a0b (diff)
downloadxorg-lib-libXft-3ca7a7c375a8c022c068e2534c5a6861e547eaef.tar.gz
merge changes by Christian Werner
see note in libXft merge-request #1 at #note_1222314 Signed-off-by: Thomas E. Dickey <dickey@invisible-island.net>
-rw-r--r--src/xftfreetype.c19
-rw-r--r--src/xftglyphs.c333
-rw-r--r--src/xftrender.c21
3 files changed, 234 insertions, 139 deletions
diff --git a/src/xftfreetype.c b/src/xftfreetype.c
index 51d4613..b2e09e2 100644
--- a/src/xftfreetype.c
+++ b/src/xftfreetype.c
@@ -367,10 +367,10 @@ XftFontInfoFill (Display *dpy, _Xconst FcPattern *pattern, XftFontInfo *fi)
{
XftDisplayInfo *info = _XftDisplayInfoGet (dpy, True);
FcChar8 *filename;
- int id;
+ int id, mid;
double dsize;
double aspect;
- FcMatrix *font_matrix;
+ FcMatrix *font_matrix, fm1;
FcBool hinting, vertical_layout, autohint, global_advance;
int hint_style;
FcChar32 hash, *hashp;
@@ -491,6 +491,21 @@ XftFontInfoFill (Display *dpy, _Xconst FcPattern *pattern, XftFontInfo *fi)
goto bail1;
}
+ mid = 1;
+ while (FcPatternGetMatrix (pattern, FC_MATRIX, mid, &font_matrix) == FcResultMatch) {
+ FcMatrixInit(&fm1);
+ fm1.xx = fi->matrix.xx / (double) 0x10000L;
+ fm1.yy = fi->matrix.yy / (double) 0x10000L;
+ fm1.xy = fi->matrix.xy / (double) 0x10000L;
+ fm1.yx = fi->matrix.yx / (double) 0x10000L;
+ FcMatrixMultiply(&fm1, font_matrix, &fm1);
+ fi->matrix.xx = (FT_Fixed)(0x10000L * fm1.xx);
+ fi->matrix.yy = (FT_Fixed)(0x10000L * fm1.yy);
+ fi->matrix.xy = (FT_Fixed)(0x10000L * fm1.xy);
+ fi->matrix.yx = (FT_Fixed)(0x10000L * fm1.yx);
+ mid++;
+ }
+
fi->transform = (fi->matrix.xx != 0x10000 || fi->matrix.xy != 0 ||
fi->matrix.yx != 0 || fi->matrix.yy != 0x10000);
diff --git a/src/xftglyphs.c b/src/xftglyphs.c
index 2e2ba67..a893394 100644
--- a/src/xftglyphs.c
+++ b/src/xftglyphs.c
@@ -29,6 +29,46 @@
#include FT_GLYPH_H
+typedef double m3x3[3][3];
+
+static inline void
+m3x3_uniform(m3x3 m)
+{
+ m[0][0] = m[1][1] = m[2][2] = 1.0;
+ m[0][1] = m[1][0] = m[0][2] = m[1][2] = m[2][0] = m[2][1] = 0;
+}
+
+static inline void
+m3x3_transform(FT_Vector *v, m3x3 m)
+{
+ double x, y;
+
+ x = (double)v->x;
+ y = (double)v->y;
+ v->x = (FT_Pos)(x * m[0][0] + y * m[0][1] + m[0][2] + 0.5);
+ v->y = (FT_Pos)(x * m[1][0] + y * m[1][1] + m[1][2] + 0.5);
+}
+
+static inline void
+m3x3_invert(m3x3 m, m3x3 mi)
+{
+ double det;
+
+ det = m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]);
+ det -= m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]);
+ det += m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
+ det = 1.0 / det;
+ mi[0][0] = det * (m[1][1] * m[2][2] - m[1][2] * m[2][1]);
+ mi[1][0] = det * (m[1][2] * m[2][0] - m[1][0] * m[2][2]);
+ mi[2][0] = det * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
+ mi[0][1] = det * (m[0][2] * m[2][1] - m[0][1] * m[2][2]);
+ mi[1][1] = det * (m[0][0] * m[2][2] - m[0][2] * m[2][0]);
+ mi[2][1] = det * (m[0][1] * m[2][0] - m[0][0] * m[2][1]);
+ mi[0][2] = det * (m[0][1] * m[1][2] - m[0][2] * m[1][1]);
+ mi[1][2] = det * (m[0][2] * m[1][0] - m[0][0] * m[1][2]);
+ mi[2][2] = det * (m[0][0] * m[1][1] - m[0][1] * m[1][0]);
+}
+
/*
* Validate the memory info for a font
*/
@@ -144,10 +184,10 @@ static int
_compute_xrender_bitmap_size( FT_Bitmap* target,
FT_GlyphSlot slot,
FT_Render_Mode mode,
- FT_Matrix* matrix )
+ FT_Matrix* matrix,
+ m3x3 m )
{
FT_Bitmap* ftbit;
- FT_Vector vector;
int width, height, pitch;
if ( slot->format != FT_GLYPH_FORMAT_BITMAP )
@@ -161,12 +201,59 @@ _compute_xrender_bitmap_size( FT_Bitmap* target,
if ( matrix && mode == FT_RENDER_MODE_NORMAL )
{
- vector.x = ftbit->width;
- vector.y = ftbit->rows;
- FT_Vector_Transform(&vector, matrix);
-
- width = (int)vector.x;
- height = (int)vector.y;
+ FT_Matrix mirror, inverse;
+ FT_Vector vector;
+ int xc, yc;
+ int left, right, top, bottom;
+
+ left = right = top = bottom = 0;
+ for (xc = 0; xc <= 1; xc++) {
+ for (yc = 0; yc <= 1; yc++) {
+ vector.x = xc * width;
+ vector.y = yc * height;
+ FT_Vector_Transform(&vector, matrix);
+ if (xc == 0 && yc == 0) {
+ left = right = (int)vector.x;
+ top = bottom = (int)vector.y;
+ } else {
+ if (left > vector.x) left = (int)vector.x;
+ if (right < vector.x) right = (int)vector.x;
+ if (bottom > vector.y) bottom = (int)vector.y;
+ if (top < vector.y) top = (int)vector.y;
+ }
+ }
+ }
+ width = (int)(right - left);
+ height = (int)(top - bottom);
+
+ mirror.xx = + 0x10000;
+ mirror.yy = - 0x10000;
+ mirror.xy = mirror.yx = 0;
+ inverse = *matrix;
+ FT_Matrix_Multiply(&mirror, &inverse);
+ FT_Matrix_Invert(&inverse);
+ FT_Matrix_Multiply(&mirror, &inverse);
+
+ vector.x = vector.y = 0;
+ FT_Vector_Transform(&vector, &inverse);
+ left = vector.x;
+ bottom = vector.y;
+ vector.x = width;
+ vector.y = height;
+ FT_Vector_Transform(&vector, &inverse);
+ right = vector.x;
+ top = vector.y;
+ left = (right - left) - (int)ftbit->width;
+ bottom = (top - bottom) - (int)ftbit->rows;
+
+ m[0][0] = (double)inverse.xx / 0x10000;
+ m[0][1] = (double)inverse.xy / 0x10000;
+ m[1][0] = (double)inverse.yx / 0x10000;
+ m[1][1] = (double)inverse.yy / 0x10000;
+ m[0][2] = (double)-left / 2;
+ m[1][2] = (double)-bottom / 2;
+ m[2][0] = m[2][1] = 0.0;
+ m[2][2] = 1.0;
}
pitch = (width+3) & ~3;
@@ -238,8 +325,8 @@ _compute_xrender_bitmap_size( FT_Bitmap* target,
*/
static void
_scaled_fill_xrender_bitmap( FT_Bitmap* target,
- FT_Bitmap* source,
- const FT_Matrix* matrix )
+ FT_Bitmap* source,
+ m3x3 m )
{
unsigned char* src_buf = source->buffer;
unsigned char* dst_line = target->buffer;
@@ -247,44 +334,55 @@ _scaled_fill_xrender_bitmap( FT_Bitmap* target,
int width = (int) target->width;
int height = (int) target->rows;
int pitch = target->pitch;
- int h;
- FT_Vector vector;
- FT_Matrix inverse = *matrix;
+ int i, x, y;
+ FT_Vector vector, vector0;
int sampling_width;
int sampling_height;
int sample_count;
if ( src_pitch < 0 )
- src_buf -= src_pitch * ((int) source->rows - 1);
-
- FT_Matrix_Invert(&inverse);
+ src_buf -= src_pitch*(source->rows-1);
/* compute how many source pixels a target pixel spans */
vector.x = 1;
vector.y = 1;
- FT_Vector_Transform(&vector, &inverse);
- sampling_width = (int) vector.x / 2;
- sampling_height = (int) vector.y / 2;
+ m3x3_transform(&vector, m);
+ vector0.x = 0;
+ vector0.y = 0;
+ m3x3_transform(&vector0, m);
+ sampling_width = (vector.x - vector0.x) / 2;
+ sampling_height = (vector.y - vector0.y) / 2;
+ if (sampling_width < 0) sampling_width = -sampling_width;
+ if (sampling_height < 0) sampling_height = -sampling_height;
sample_count = (2 * sampling_width + 1) * (2 * sampling_height + 1);
- for ( h = height; h > 0; h--, dst_line += pitch )
+ for ( y = height; y > 0; y--, dst_line += pitch )
{
- int x;
-
- for ( x = 0; x < width; x++ )
+ for ( x = 0; x < width; x++ )
{
unsigned char* src;
-#define CLAMP(x, min, max) (int) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
+ /* compute target pixel location in source space */
+ vector.x = x;
+ vector.y = height - y;
+ m3x3_transform(&vector, m);
- /* compute target pixel location in source space */
- vector.x = (x * 0x10000) + 0x10000 / 2;
- vector.y = ((height - h) * 0x10000) + 0x10000 / 2;
- FT_Vector_Transform(&vector, &inverse);
- vector.x = CLAMP(FT_RoundFix(vector.x) / 0x10000, 0, source->width - 1);
- vector.y = CLAMP(FT_RoundFix(vector.y) / 0x10000, 0, source->rows - 1);
+ if (source->pixel_mode == FT_PIXEL_MODE_BGRA)
+ {
+ if (vector.x < -sampling_width || vector.x > source->width + sampling_width)
+ continue;
+ if (vector.y < -sampling_height || vector.y > source->rows + sampling_height)
+ continue;
+ }
+ else
+ {
+ if (vector.x < 0 || vector.x >= source->width)
+ continue;
+ if (vector.y < 0 || vector.y >= source->rows)
+ continue;
+ }
- switch ( source->pixel_mode )
+ switch ( source->pixel_mode )
{
case FT_PIXEL_MODE_MONO: /* convert mono to 8-bit gray, scale using nearest pixel */
src = src_buf + (vector.y * src_pitch);
@@ -300,22 +398,28 @@ _scaled_fill_xrender_bitmap( FT_Bitmap* target,
case FT_PIXEL_MODE_BGRA: /* scale by averaging all relevant source pixels, keep BGRA format */
{
int sample_x, sample_y;
- int bgra[4] = {0,0,0,0};
- int i;
+ int bgra[4] = { 0, 0, 0, 0 };
+
for (sample_y = - sampling_height; sample_y < sampling_height + 1; ++sample_y)
{
- int src_y = CLAMP(vector.y + sample_y, 0, source->rows - 1);
+ int src_y = vector.y + sample_y;
+
+ if (src_y < 0 || src_y >= source->rows)
+ continue;
src = src_buf + (src_y * src_pitch);
for (sample_x = - sampling_width; sample_x < sampling_width + 1; ++sample_x)
{
- int src_x = CLAMP(vector.x + sample_x, 0, source->width - 1);
+ int src_x = vector.x + sample_x;
+
+ if (src_x < 0 || src_x >= source->width)
+ continue;
for (i = 0; i < 4; ++i)
bgra[i] += src[src_x * 4 + i];
}
}
for (i = 0; i < 4; ++i)
- dst_line[4 * x + i] = (unsigned char) (bgra[i] / sample_count);
+ dst_line[4 * x + i] = bgra[i] / sample_count;
break;
}
}
@@ -347,15 +451,15 @@ _fill_xrender_bitmap( FT_Bitmap* target,
{
unsigned char* srcLine = ftbit->buffer;
- unsigned char* dstLine = target->buffer;
- int src_pitch = ftbit->pitch;
- int width = (int)target->width;
- int height = (int)target->rows;
- int pitch = target->pitch;
- int subpixel;
- int h;
-
- subpixel = ( mode == FT_RENDER_MODE_LCD ||
+ unsigned char* dstLine = target->buffer;
+ int src_pitch = ftbit->pitch;
+ int width = (int)target->width;
+ int height = (int)target->rows;
+ int pitch = target->pitch;
+ int subpixel;
+ int h;
+
+ subpixel = ( mode == FT_RENDER_MODE_LCD ||
mode == FT_RENDER_MODE_LCD_V );
if ( src_pitch < 0 )
@@ -525,55 +629,6 @@ _fill_xrender_bitmap( FT_Bitmap* target,
}
}
-/* This function creates a Picture for the given glyph on the default root window
- * It will only work in Xinerama mode
- *
- * dpy :: target display
- *
- * format :: target pixmap format
- *
- * width :: picture width
- *
- * width :: picture height
- *
- * data :: bitmap data
- *
- */
-static Picture
-_create_glyph_bgra_picture (Display *dpy,
- XRenderPictFormat *format,
- unsigned width,
- unsigned height,
- unsigned char *data)
-{
- XImage image = {
- (int) width, (int) height, 0, ZPixmap, (char *)data,
- dpy->byte_order, dpy->bitmap_unit, dpy->bitmap_bit_order, 32,
- 32, 0, 32,
- 0, 0, 0, 0, {0}
- };
- Picture picture;
- Pixmap pixmap;
- GC gc;
-
- pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy), width, height, 32);
- if (!pixmap)
- return None;
-
- gc = XCreateGC(dpy, pixmap, 0, NULL);
- if (!gc)
- return None;
-
- XInitImage(&image);
- XPutImage(dpy, pixmap, gc, &image, 0, 0, 0, 0, width, height);
- picture = XRenderCreatePicture(dpy, pixmap, format, 0, NULL);
-
- XFreeGC(dpy, gc);
- XFreePixmap(dpy, pixmap);
-
- return picture;
-}
-
_X_EXPORT void
XftFontLoadGlyphs (Display *dpy,
XftFont *pub,
@@ -598,6 +653,7 @@ XftFontLoadGlyphs (Display *dpy,
FT_Bitmap* ftbit;
FT_Bitmap local;
FT_Vector vector;
+ m3x3 m;
FT_Face face;
FT_Render_Mode mode = FT_RENDER_MODE_MONO;
FcBool transform;
@@ -612,7 +668,7 @@ XftFontLoadGlyphs (Display *dpy,
return;
if (font->info.color)
- mode = FT_RENDER_MODE_NORMAL;
+ mode = FT_RENDER_MODE_NORMAL;
if (font->info.antialias)
{
switch (font->info.rgba) {
@@ -688,22 +744,22 @@ XftFontLoadGlyphs (Display *dpy,
*/
int xc, yc;
left = right = top = bottom = 0;
- for(xc = 0; xc <= 1; xc ++) {
- for(yc = 0; yc <= 1; yc++) {
+ for (xc = 0; xc <= 1; xc++) {
+ for (yc = 0; yc <= 1; yc++) {
vector.x = glyphslot->metrics.horiBearingX + xc * glyphslot->metrics.width;
vector.y = glyphslot->metrics.horiBearingY - yc * glyphslot->metrics.height;
FT_Vector_Transform(&vector, &font->info.matrix);
if (XftDebug() & XFT_DBG_GLYPH)
printf("Trans %d %d: %d %d\n", (int) xc, (int) yc,
(int) vector.x, (int) vector.y);
- if(xc == 0 && yc == 0) {
+ if (xc == 0 && yc == 0) {
left = right = (int)vector.x;
top = bottom = (int)vector.y;
} else {
- if(left > vector.x) left = (int)vector.x;
- if(right < vector.x) right = (int)vector.x;
- if(bottom > vector.y) bottom = (int)vector.y;
- if(top < vector.y) top = (int)vector.y;
+ if (left > vector.x) left = (int)vector.x;
+ if (right < vector.x) right = (int)vector.x;
+ if (bottom > vector.y) bottom = (int)vector.y;
+ if (top < vector.y) top = (int)vector.y;
}
}
@@ -711,7 +767,7 @@ XftFontLoadGlyphs (Display *dpy,
left = (int)FLOOR(left);
right = (int)CEIL(right);
bottom = (int)FLOOR(bottom);
- top = CEIL(top);
+ top = (int)CEIL(top);
} else {
left = (int)FLOOR( glyphslot->metrics.horiBearingX );
@@ -780,9 +836,9 @@ XftFontLoadGlyphs (Display *dpy,
vector.x = face->size->metrics.max_advance;
vector.y = 0;
}
- FT_Vector_Transform (&vector, &font->info.matrix);
- xftg->metrics.xOff = (short)(vector.x >> 6);
- xftg->metrics.yOff = (short)(-(vector.y >> 6));
+ FT_Vector_Transform(&vector, &font->info.matrix);
+ xftg->metrics.xOff = (short)(TRUNC(ROUND(vector.x)));
+ xftg->metrics.yOff = (short)(TRUNC(ROUND(vector.y)));
}
else
{
@@ -851,19 +907,21 @@ XftFontLoadGlyphs (Display *dpy,
}
}
- size = _compute_xrender_bitmap_size( &local, glyphslot, mode, glyph_transform ? &font->info.matrix : NULL );
+ m3x3_uniform(m);
+ size = _compute_xrender_bitmap_size( &local, glyphslot, mode, glyph_transform ? &font->info.matrix : NULL, m );
if ( size < 0 )
continue;
xftg->metrics.width = (unsigned short)local.width;
xftg->metrics.height = (unsigned short)local.rows;
- if (transform)
+ if (glyph_transform)
{
+ m3x3 mi;
+
+ m3x3_invert(m, mi);
vector.x = - glyphslot->bitmap_left;
vector.y = glyphslot->bitmap_top;
-
- FT_Vector_Transform(&vector, &font->info.matrix);
-
+ m3x3_transform(&vector, mi);
xftg->metrics.x = (short)vector.x;
xftg->metrics.y = (short)vector.y;
}
@@ -896,12 +954,12 @@ XftFontLoadGlyphs (Display *dpy,
local.buffer = bufBitmap;
- if (mode == FT_RENDER_MODE_NORMAL && glyph_transform)
- _scaled_fill_xrender_bitmap(&local, &glyphslot->bitmap, &font->info.matrix);
- else
+ if (mode == FT_RENDER_MODE_NORMAL && glyph_transform)
+ _scaled_fill_xrender_bitmap(&local, &glyphslot->bitmap, m);
+ else
_fill_xrender_bitmap( &local, glyphslot, mode,
- (font->info.rgba == FC_RGBA_BGR ||
- font->info.rgba == FC_RGBA_VBGR ) );
+ (font->info.rgba == FC_RGBA_BGR ||
+ font->info.rgba == FC_RGBA_VBGR) );
/*
* Copy or convert into local buffer.
@@ -916,7 +974,11 @@ XftFontLoadGlyphs (Display *dpy,
*/
glyph = (Glyph) glyphindex;
- xftg->picture = 0;
+ if (xftg->picture)
+ {
+ XRenderFreePicture(dpy, xftg->picture);
+ xftg->picture = 0;
+ }
xftg->glyph_memory = (size_t)size + font->sizeof_glyph;
if (font->format)
{
@@ -948,10 +1010,31 @@ XftFontLoadGlyphs (Display *dpy,
}
if (glyphslot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
- xftg->picture = _create_glyph_bgra_picture(dpy, font->format,
- local.width,
- local.rows,
- bufBitmap);
+ {
+ Pixmap pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy), local.width, local.rows, 32);
+ GC gc = XCreateGC(dpy, pixmap, 0, NULL);
+ XImage image = {
+ local.width, local.rows, 0, ZPixmap, (char *)bufBitmap,
+ dpy->byte_order, dpy->bitmap_unit, dpy->bitmap_bit_order, 32,
+ 32, local.width * 4 - local.pitch, 32,
+ 0, 0, 0
+ };
+
+ XInitImage(&image);
+ XPutImage(dpy, pixmap, gc, &image, 0, 0, 0, 0, local.width, local.rows);
+ xftg->picture = XRenderCreatePicture(dpy, pixmap, font->format, 0, NULL);
+
+ XFreeGC(dpy, gc);
+ XFreePixmap(dpy, pixmap);
+ /*
+ * Record 256 times higher memory pressure for unrotated
+ * pictures, and maximum for rotated pictures.
+ */
+ if (font->info.matrix.xy || font->info.matrix.yx)
+ xftg->glyph_memory += font->max_glyph_memory - size;
+ else
+ xftg->glyph_memory += (size_t)size * 255;
+ }
else
XRenderAddGlyphs (dpy, font->glyphset, &glyph,
&xftg->metrics, 1,
@@ -1059,11 +1142,8 @@ XftFontUnloadGlyphs (Display *dpy,
}
}
}
- else
- {
- if (xftg->bitmap)
- free (xftg->bitmap);
- }
+ else if (xftg->bitmap)
+ free (xftg->bitmap);
font->glyph_memory -= xftg->glyph_memory;
if (info)
info->glyph_memory -= xftg->glyph_memory;
@@ -1140,6 +1220,7 @@ XftFontCheckGlyph (Display *dpy,
xftg->bitmap = NULL;
xftg->glyph_memory = 0;
+ xftg->picture = 0;
font->glyphs[glyph] = xftg;
if (font->track_mem_usage) {
diff --git a/src/xftrender.c b/src/xftrender.c
index 721cae0..98c86cd 100644
--- a/src/xftrender.c
+++ b/src/xftrender.c
@@ -156,15 +156,10 @@ XftGlyphRender (Display *dpy,
glyph = font->glyphs[wire];
if (glyph->picture)
{
- _XftCompositeString(dpy, op, src, dst, font->format, font->glyphset,
- srcx, srcy, x, y, width, chars, j);
- XRenderComposite(dpy, PictOpOver, glyph->picture, None,
- dst, 0, 0, 0, 0, dstx, dsty - glyph->metrics.y,
- glyph->metrics.width, glyph->metrics.height);
-
+ _XftCompositeString(dpy, op, src, dst, font->format, font->glyphset, srcx, srcy, x, y, width, chars, j);
+ XRenderComposite(dpy, PictOpOver, glyph->picture, None, dst, 0, 0, 0, 0, dstx - glyph->metrics.x, dsty - glyph->metrics.y, glyph->metrics.width, glyph->metrics.height);
dstx += glyph->metrics.xOff;
dsty += glyph->metrics.yOff;
-
x = dstx;
y = dsty;
j = 0;
@@ -388,8 +383,10 @@ XftGlyphSpecRender (Display *dpy,
{
XRenderComposite(dpy, PictOpOver, glyph->picture, None,
dst, 0, 0, 0, 0,
- glyphs[i].x, glyphs[i].y - glyph->metrics.y,
- glyph->metrics.width, glyph->metrics.height);
+ glyphs[i].x - glyph->metrics.x,
+ glyphs[i].y - glyph->metrics.y,
+ glyph->metrics.width,
+ glyph->metrics.height);
continue;
}
if (!i || x != glyphs[i].x || y != glyphs[i].y)
@@ -674,8 +671,10 @@ XftGlyphFontSpecRender (Display *dpy,
{
XRenderComposite(dpy, PictOpOver, glyph->picture, None,
dst, 0, 0, 0, 0,
- glyphs[i].x, glyphs[i].y - glyph->metrics.y,
- glyph->metrics.width, glyph->metrics.height);
+ glyphs[i].x - glyph->metrics.x,
+ glyphs[i].y - glyph->metrics.y,
+ glyph->metrics.width,
+ glyph->metrics.height);
continue;
}
if (!i || pub != prevPublic || x != glyphs[i].x || y != glyphs[i].y)