summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/glamor_glyphs.c348
1 files changed, 288 insertions, 60 deletions
diff --git a/src/glamor_glyphs.c b/src/glamor_glyphs.c
index 362e46f..9b9e8cd 100644
--- a/src/glamor_glyphs.c
+++ b/src/glamor_glyphs.c
@@ -16,7 +16,7 @@
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL Red Hat
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Permission to use, copy, modify, distribute, and sell this software and its
@@ -33,7 +33,7 @@
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: Owen Taylor <otaylor@fishsoup.net>
@@ -69,14 +69,20 @@
typedef struct {
PicturePtr source;
- glamor_composite_rect_t rects[GLYPH_BUFFER_SIZE];
+ glamor_composite_rect_t rects[GLYPH_BUFFER_SIZE + 4];
int count;
} glamor_glyph_buffer_t;
+#define LEFT_SIDE 1
+#define RIGHT_SIDE 2
+
struct glamor_glyph {
glamor_glyph_cache_t *cache;
uint16_t x, y;
uint16_t size, pos;
+ int y1,y2,y3,y4; /* Use to check real intersect or not. */
+ Bool has_edge_y;
+ Bool cached;
};
typedef enum {
@@ -253,8 +259,8 @@ glamor_glyph_cache_upload_glyph(ScreenPtr screen,
PictureMatchFormat
(screen,
pCachePixmap->
- drawable.depth,
- cache->picture->format),
+ drawable.depth,
+ cache->picture->format),
0, NULL, serverClient,
&error);
if (picture) {
@@ -264,7 +270,7 @@ glamor_glyph_cache_upload_glyph(ScreenPtr screen,
NULL, picture,
0, 0, 0, 0, 0,
0,
- glyph->info.width,
+ glyph->info.width,
glyph->info.height);
FreePicture(picture, 0);
}
@@ -299,7 +305,8 @@ glamor_glyph_unrealize(ScreenPtr screen, GlyphPtr glyph)
if (priv == NULL)
return;
- priv->cache->glyphs[priv->pos] = NULL;
+ if (priv->cached)
+ priv->cache->glyphs[priv->pos] = NULL;
glamor_glyph_set_private(glyph, NULL);
free(priv);
@@ -355,13 +362,16 @@ glamor_glyph_extents(int nlist,
* bounding box, which appears to be good enough to catch most cases at least.
*/
static Bool
-glamor_glyphs_intersect(int nlist, GlyphListPtr list, GlyphPtr * glyphs)
+glamor_glyphs_intersect(int nlist, GlyphListPtr list, GlyphPtr * glyphs,
+ PictFormatShort mask_format,
+ ScreenPtr screen, Bool check_fake_overlap)
{
int x1, x2, y1, y2;
int n;
int x, y;
BoxRec extents;
Bool first = TRUE;
+ struct glamor_glyph *priv;
x = 0;
y = 0;
@@ -376,6 +386,7 @@ glamor_glyphs_intersect(int nlist, GlyphListPtr list, GlyphPtr * glyphs)
list++;
while (n--) {
GlyphPtr glyph = *glyphs++;
+ BoxRec left_box, right_box;
if (glyph->info.width == 0
|| glyph->info.height == 0) {
@@ -383,17 +394,22 @@ glamor_glyphs_intersect(int nlist, GlyphListPtr list, GlyphPtr * glyphs)
y += glyph->info.yOff;
continue;
}
-
+ if (mask_format
+ && mask_format != GlyphPicture(glyph)[screen->myNum]->format)
+ return TRUE;
x1 = x - glyph->info.x;
if (x1 < MINSHORT)
x1 = MINSHORT;
y1 = y - glyph->info.y;
if (y1 < MINSHORT)
y1 = MINSHORT;
+ if (check_fake_overlap)
+ priv = glamor_glyph_get_private(glyph);
x2 = x1 + glyph->info.width;
+ y2 = y1 + glyph->info.height;
+
if (x2 > MAXSHORT)
x2 = MAXSHORT;
- y2 = y1 + glyph->info.height;
if (y2 > MAXSHORT)
y2 = MAXSHORT;
@@ -402,18 +418,64 @@ glamor_glyphs_intersect(int nlist, GlyphListPtr list, GlyphPtr * glyphs)
extents.y1 = y1;
extents.x2 = x2;
extents.y2 = y2;
+
+ if (check_fake_overlap && priv
+ && priv->has_edge_y && glyph->info.yOff == 0) {
+ left_box.x1 = x1;
+ left_box.x2 = x1 + 2;
+ left_box.y1 = priv->y1;
+ left_box.y2 = priv->y2;
+
+ right_box.x1 = x2 - 2;
+ right_box.x2 = x2;
+ right_box.y1 = priv->y3;
+ right_box.y2 = priv->y4;
+ }
+
first = FALSE;
} else {
+
if (x1 < extents.x2 && x2 > extents.x1
&& y1 < extents.y2
&& y2 > extents.y1) {
- return TRUE;
+ if (check_fake_overlap && priv
+ && priv->has_edge_y && glyph->info.yOff == 0) {
+
+ if (x1 < left_box.x2 && x1 + 2 > left_box.x1
+ && priv->y1 < left_box.y2 && priv->y2 > left_box.y1)
+ return TRUE;
+
+ if (x2 - 2 < right_box.x2 && x2 > right_box.x1
+ && priv->y1 < right_box.y2 && priv->y2 > right_box.y1)
+ return TRUE;
+
+ if (x1 + 2 < extents.x2 && x2 - 2 > extents.x1)
+ return TRUE;
+ } else
+ return TRUE;
}
- if (x1 < extents.x1)
+ if (x1 < extents.x1) {
extents.x1 = x1;
- if (x2 > extents.x2)
+ if (check_fake_overlap && priv
+ && priv->has_edge_y && glyph->info.yOff == 0) {
+ left_box.x1 = x1;
+ left_box.x2 = x1 + 2;
+ left_box.y1 = priv->y1;
+ left_box.y2 = priv->y2;
+ }
+ }
+ if (x2 > extents.x2) {
extents.x2 = x2;
+
+ if (check_fake_overlap && priv
+ && priv->has_edge_y && glyph->info.yOff == 0) {
+ right_box.x1 = x2 - 2;
+ right_box.x2 = x2;
+ right_box.y1 = priv->y3;
+ right_box.y2 = priv->y4;
+ }
+ }
if (y1 < extents.y1)
extents.y1 = y1;
if (y2 > extents.y2)
@@ -448,6 +510,92 @@ glamor_glyph_size_to_mask(int size)
glamor_glyph_count_to_mask(glamor_glyph_size_to_count(size));
}
+void
+glamor_glyph_priv_get_extent(GlyphPtr glyph, struct glamor_glyph *priv,
+ PicturePtr glyph_picture)
+{
+ unsigned int *bytes, *words, word0, word1;
+ PixmapPtr glyph_pixmap = (PixmapPtr) glyph_picture->pDrawable;
+ struct glamor_pixmap_private *pixmap_priv;
+ int i, j;
+ int y1, y2, y3, y4;
+ Bool all_zero = TRUE;
+ int pixelsPerWord, bitsPerPixel;
+ unsigned int temp;
+ int x1_offset = 0, x2_offset = 0, temp_offset;
+
+ bitsPerPixel = glyph_pixmap->drawable.bitsPerPixel;
+ pixelsPerWord = 32 / bitsPerPixel;
+ pixmap_priv = glamor_get_pixmap_private(glyph_pixmap);
+
+ if (glyph_pixmap->drawable.width < 4
+ || !(glyph_pixmap->drawable.depth == 8
+ || glyph_pixmap->drawable.depth == 1
+ || glyph_pixmap->drawable.depth == 32)) {
+ priv->has_edge_y = FALSE;
+ return;
+ }
+
+ y1 = y3 = MAXSHORT;
+ y2 = y4 = MINSHORT;
+
+ for(j = 0; j < glyph_pixmap->drawable.height; j++)
+ {
+ if (bitsPerPixel == 8) {
+ word0 = (*(unsigned short*)((unsigned char*)glyph_pixmap->devPrivate.ptr
+ + glyph_pixmap->devKind * j) != 0);
+ word1 = (*(unsigned short*)((unsigned char*)glyph_pixmap->devPrivate.ptr
+ + glyph_pixmap->devKind * j
+ + glyph_pixmap->drawable.width - 2) != 0);
+ } else if (bitsPerPixel == 32) {
+ word0 = (*((unsigned int*)glyph_pixmap->devPrivate.ptr
+ + glyph_pixmap->devKind * j) != 0);
+ word0 = word0 | (*((unsigned int*)glyph_pixmap->devPrivate.ptr
+ + glyph_pixmap->devKind * j + 1) != 0);
+
+ word1 = (*((unsigned int*)glyph_pixmap->devPrivate.ptr
+ + glyph_pixmap->devKind * j
+ + glyph_pixmap->drawable.width -2) != 0);
+ word1 = word0 | (*((unsigned int*)glyph_pixmap->devPrivate.ptr
+ + glyph_pixmap->devKind * j
+ + glyph_pixmap->drawable.width - 1) != 0);
+ } else if (bitsPerPixel == 1) {
+ unsigned char temp;
+ word0 = ((*((unsigned char*)glyph_pixmap->devPrivate.ptr
+ + glyph_pixmap->devKind * j) & 0x3) != 0);
+ temp = *((unsigned char*)glyph_pixmap->devPrivate.ptr
+ + glyph_pixmap->devKind * j
+ + (glyph_pixmap->drawable.width - 2)/8);
+ word1 = ((temp & (1 << ((glyph_pixmap->drawable.width - 2) % 8))) != 0);
+ if (!word1) {
+ temp = *((unsigned char*)glyph_pixmap->devPrivate.ptr
+ + glyph_pixmap->devKind * j
+ + (glyph_pixmap->drawable.width - 1)/8);
+ word1 = ((temp & (1 << ((glyph_pixmap->drawable.width - 1) % 8))) != 0);
+ }
+ }
+ if (word0) {
+ if (j < y1)
+ y1 = j;
+ if (j > y2)
+ y2 = j;
+ }
+ if (word1) {
+ if (j < y3)
+ y3 = j;
+ if (j > y4)
+ y4 = j;
+ }
+ }
+
+ priv->y1 = y1;
+ priv->y2 = y2;
+ priv->y3 = y3;
+ priv->y4 = y4;
+ priv->has_edge_y = TRUE;
+ return;
+}
+
static PicturePtr
glamor_glyph_cache(ScreenPtr screen, GlyphPtr glyph, int *out_x,
int *out_y)
@@ -457,7 +605,7 @@ glamor_glyph_cache(ScreenPtr screen, GlyphPtr glyph, int *out_x,
glamor_glyph_cache_t *cache =
&glamor->glyphCaches[PICT_FORMAT_RGB(glyph_picture->format) !=
0];
- struct glamor_glyph *priv = NULL;
+ struct glamor_glyph *priv = NULL, *evicted_priv = NULL;
int size, mask, pos, s;
if (glyph->info.width > GLYPH_MAX_SIZE
@@ -472,6 +620,8 @@ glamor_glyph_cache(ScreenPtr screen, GlyphPtr glyph, int *out_x,
s = glamor_glyph_size_to_count(size);
mask = glamor_glyph_count_to_mask(s);
pos = (cache->count + s - 1) & mask;
+
+ priv = glamor_glyph_get_private(glyph);
if (pos < GLYPH_CACHE_SIZE) {
cache->count = pos + s;
} else {
@@ -482,46 +632,49 @@ glamor_glyph_cache(ScreenPtr screen, GlyphPtr glyph, int *out_x,
if (evicted == NULL)
continue;
- priv = glamor_glyph_get_private(evicted);
- if (priv->size >= s) {
+ evicted_priv = glamor_glyph_get_private(evicted);
+ assert(evicted_priv->pos == i);
+ if (evicted_priv->size >= s) {
cache->glyphs[i] = NULL;
- glamor_glyph_set_private(evicted, NULL);
+ evicted_priv->cached = FALSE;
pos = cache->evict &
glamor_glyph_size_to_mask(size);
} else
- priv = NULL;
+ evicted_priv = NULL;
break;
}
- if (priv == NULL) {
+ if (evicted_priv == NULL) {
int count = glamor_glyph_size_to_count(size);
mask = glamor_glyph_count_to_mask(count);
pos = cache->evict & mask;
for (s = 0; s < count; s++) {
GlyphPtr evicted = cache->glyphs[pos + s];
if (evicted != NULL) {
- if (priv != NULL)
- free(priv);
- priv =
+ evicted_priv =
glamor_glyph_get_private
(evicted);
- glamor_glyph_set_private(evicted,
- NULL);
+
+ assert(evicted_priv->pos == pos + s);
+ evicted_priv->cached = FALSE;
cache->glyphs[pos + s] = NULL;
}
}
+
}
/* And pick a new eviction position */
cache->evict = rand() % GLYPH_CACHE_SIZE;
}
+new_priv:
if (priv == NULL) {
priv = malloc(sizeof(struct glamor_glyph));
if (priv == NULL)
return NULL;
+ glamor_glyph_set_private(glyph, priv);
+ priv->has_edge_y = FALSE;
}
- glamor_glyph_set_private(glyph, priv);
cache->glyphs[pos] = glyph;
priv->cache = cache;
@@ -544,6 +697,10 @@ glamor_glyph_cache(ScreenPtr screen, GlyphPtr glyph, int *out_x,
glamor_glyph_cache_upload_glyph(screen, cache, glyph, priv->x,
priv->y);
+ if (priv->has_edge_y == FALSE && glyph->info.width >= 4)
+ glamor_glyph_priv_get_extent(glyph, priv, glyph_picture);
+ priv->cached = TRUE;
+
*out_x = priv->x;
*out_y = priv->y;
return cache->picture;
@@ -554,6 +711,7 @@ static glamor_glyph_cache_result_t
glamor_buffer_glyph(ScreenPtr screen,
glamor_glyph_buffer_t * buffer,
GlyphPtr glyph, int x_glyph, int y_glyph,
+ int dx, int dy, int w, int h,
glyphs_flush glyphs_flush, void *flush_arg)
{
glamor_screen_private *glamor_screen =
@@ -588,23 +746,25 @@ glamor_buffer_glyph(ScreenPtr screen,
priv = glamor_glyph_get_private(glyph);
- if (priv) {
+ if (priv && priv->cached) {
rect = &buffer->rects[buffer->count++];
- rect->x_src = priv->x;
- rect->y_src = priv->y;
+ rect->x_src = priv->x + dx;
+ rect->y_src = priv->y + dy;
if (buffer->source == NULL)
buffer->source = priv->cache->picture;
+ assert(priv->cache->glyphs[priv->pos] == glyph);
} else {
if (glyphs_flush)
(*glyphs_flush)(flush_arg);
source = glamor_glyph_cache(screen, glyph, &x, &y);
if (source != NULL) {
rect = &buffer->rects[buffer->count++];
- rect->x_src = x;
- rect->y_src = y;
+ rect->x_src = x + dx;
+ rect->y_src = y + dy;
if (buffer->source == NULL)
buffer->source = source;
} else {
+ /* Couldn't find the glyph in the cache, use the glyph picture directly */
source = GlyphPicture(glyph)[screen->myNum];
if (buffer->source
&& buffer->source != source
@@ -613,18 +773,16 @@ glamor_buffer_glyph(ScreenPtr screen,
buffer->source = source;
rect = &buffer->rects[buffer->count++];
- rect->x_src = 0;
- rect->y_src = 0;
+ rect->x_src = 0 + dx;
+ rect->y_src = 0 + dy;
}
+ priv = glamor_glyph_get_private(glyph);
}
rect->x_dst = x_glyph - glyph->info.x;
rect->y_dst = y_glyph - glyph->info.y;
- rect->width = glyph->info.width;
- rect->height = glyph->info.height;
-
- /* Couldn't find the glyph in the cache, use the glyph picture directly */
-
+ rect->width = w;
+ rect->height = h;
return GLAMOR_GLYPH_SUCCESS;
}
@@ -708,7 +866,6 @@ glamor_glyphs_via_mask(CARD8 op,
fill_rect.width = width;
fill_rect.height = height;
gc->ops->PolyFillRect(&mask_pixmap->drawable, gc, 1, &fill_rect);
-
FreeScratchGC(gc);
x = -extents.x1;
y = -extents.y1;
@@ -734,6 +891,8 @@ glamor_glyphs_via_mask(CARD8 op,
glamor_buffer_glyph(screen, &buffer,
glyph, x, y,
+ 0, 0,
+ glyph->info.width, glyph->info.height,
flush_func,
(void*)&arg);
}
@@ -800,7 +959,8 @@ glamor_glyphs_to_dst(CARD8 op,
PicturePtr dst,
INT16 x_src,
INT16 y_src,
- int nlist, GlyphListPtr list, GlyphPtr * glyphs)
+ int nlist, GlyphListPtr list,
+ GlyphPtr * glyphs)
{
ScreenPtr screen = dst->pDrawable->pScreen;
int x = 0, y = 0;
@@ -809,14 +969,21 @@ glamor_glyphs_to_dst(CARD8 op,
GlyphPtr glyph;
glamor_glyph_buffer_t buffer;
struct glyphs_flush_dst_arg arg;
+ BoxPtr rects;
+ int nrect;
buffer.count = 0;
buffer.source = NULL;
+
+ rects = REGION_RECTS(dst->pCompositeClip);
+ nrect = REGION_NUM_RECTS(dst->pCompositeClip);
+
while (nlist--) {
x += list->xOff;
y += list->yOff;
n = list->len;
while (n--) {
+ int i;
glyph = *glyphs++;
if (glyph->info.width > 0
@@ -836,12 +1003,41 @@ glamor_glyphs_to_dst(CARD8 op,
} else
flush_func = NULL;
- glamor_buffer_glyph(screen,
- &buffer,
- glyph, x, y,
- flush_func,
- (void*)&arg);
-
+ for (i = 0; i < nrect; i++) {
+ int dst_x, dst_y;
+ int dx, dy;
+ int x2, y2;
+
+ dst_x = x - glyph->info.x;
+ dst_y = y - glyph->info.y;
+ x2 = dst_x + glyph->info.width;
+ y2 = dst_y + glyph->info.height;
+ dx = dy = 0;
+ if (rects[i].y1 >= y2)
+ break;
+
+ if (dst_x < rects[i].x1)
+ dx = rects[i].x1 - dst_x, dst_x = rects[i].x1;
+ if (x2 > rects[i].x2)
+ x2 = rects[i].x2;
+ if (dst_y < rects[i].y1)
+ dy = rects[i].y1 - dst_y, dst_y = rects[i].y1;
+ if (y2 > rects[i].y2)
+ y2 = rects[i].y2;
+
+ if (dst_x < x2 && dst_y < y2) {
+
+ glamor_buffer_glyph(screen,
+ &buffer,
+ glyph,
+ dst_x + glyph->info.x,
+ dst_y + glyph->info.y,
+ dx, dy,
+ x2 - dst_x, y2 - dst_y,
+ flush_func,
+ (void*)&arg);
+ }
+ }
}
x += glyph->info.xOff;
@@ -869,13 +1065,14 @@ _glamor_glyphs(CARD8 op,
PicturePtr dst,
PictFormatPtr mask_format,
INT16 x_src,
- INT16 y_src, int nlist, GlyphListPtr list,
+ INT16 y_src, int nlist, GlyphListPtr list,
GlyphPtr * glyphs, Bool fallback)
{
- /* If we don't have a mask format but all the glyphs have the same format
- * and don't intersect, use the glyph format as mask format for the full
- * benefits of the glyph cache.
- */
+ Bool intersected = FALSE;
+ PictFormatShort format;
+ Bool format_checked = FALSE;
+ int check_fake_overlap = TRUE;
+
if (!mask_format) {
Bool same_format = TRUE;
int i;
@@ -889,19 +1086,50 @@ _glamor_glyphs(CARD8 op,
}
}
- if (!same_format || (mask_format->depth != 1 &&
- glamor_glyphs_intersect(nlist, list,
- glyphs))) {
+ if (!same_format)
mask_format = NULL;
- }
+ format_checked = TRUE;
}
- if (mask_format)
- glamor_glyphs_via_mask(op, src, dst, mask_format,
- x_src, y_src, nlist, list, glyphs);
+
+ if (mask_format && !format_checked)
+ format = mask_format->depth << 24 | mask_format->format;
else
+ format = 0;
+
+ if (!(op == PictOpOver
+ || op == PictOpAdd
+ || op == PictOpXor)) {
+ /* C = (0,0,0,0) D = glyphs , SRC = A, DEST = B (faked overlapped glyphs, overlapped with (0,0,0,0)).
+ * For those op, (A IN (C ADD D)) OP B != (A IN D) OP ((A IN C) OP B)
+ * or (A IN (D ADD C)) OP B != (A IN C) OP ((A IN D) OP B)
+ * We need to split the faked regions to three or two, and composite the disoverlapped small
+ * boxes one by one. For other Ops, it's safe to composite the whole box. */
+ check_fake_overlap = FALSE;
+ }
+ intersected = glamor_glyphs_intersect(nlist, list, glyphs,
+ format, dst->pDrawable->pScreen,
+ check_fake_overlap);
+
+ if (!intersected
+ || (((nlist == 1 && list->len == 1) || op == PictOpAdd)
+ && dst->format == (mask_format->depth << 24 | mask_format->format))) {
+
glamor_glyphs_to_dst(op, src, dst, x_src, y_src, nlist,
list, glyphs);
+ return TRUE;
+ }
+
+ if (mask_format)
+ glamor_glyphs_via_mask(op, src, dst, mask_format,
+ x_src, y_src, nlist, list, glyphs);
+ else {
+ /* No mask_format and has intersect and glyphs have different format.
+ * XXX do we need to implement a new glyphs rendering function for
+ * this case?*/
+ glamor_glyphs_to_dst(op, src, dst, x_src, y_src, nlist, list, glyphs);
+ }
+
return TRUE;
}
@@ -913,7 +1141,7 @@ glamor_glyphs(CARD8 op,
INT16 x_src,
INT16 y_src, int nlist, GlyphListPtr list, GlyphPtr * glyphs)
{
- _glamor_glyphs(op, src, dst, mask_format, x_src,
+ _glamor_glyphs(op, src, dst, mask_format, x_src,
y_src, nlist, list, glyphs, TRUE);
}
@@ -923,10 +1151,10 @@ glamor_glyphs_nf(CARD8 op,
PicturePtr dst,
PictFormatPtr mask_format,
INT16 x_src,
- INT16 y_src, int nlist,
+ INT16 y_src, int nlist,
GlyphListPtr list, GlyphPtr * glyphs)
{
- return _glamor_glyphs(op, src, dst, mask_format, x_src,
+ return _glamor_glyphs(op, src, dst, mask_format, x_src,
y_src, nlist, list, glyphs, FALSE);
}