diff options
-rw-r--r-- | src/glamor_glyphs.c | 348 |
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); } |