static int uxa_glyphs_to_dst(CARD8 op, PicturePtr pSrc, PicturePtr pDst, INT16 xSrc, INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr * glyphs) { ScreenPtr screen = pDst->pDrawable->pScreen; int x, y, n; xSrc -= list->xOff; ySrc -= list->yOff; x = y = 0; while (nlist--) { x += list->xOff; y += list->yOff; n = list->len; while (n--) { GlyphPtr glyph = *glyphs++; PicturePtr glyph_atlas; int glyph_x, glyph_y; struct uxa_glyph *priv; if (glyph->info.width == 0 || glyph->info.height == 0) goto next_glyph; priv = uxa_glyph_get_private(glyph); if (priv != NULL) { glyph_x = priv->x; glyph_y = priv->y; glyph_atlas = priv->cache->picture; } else { glyph_atlas = uxa_glyph_cache(screen, glyph, &glyph_x, &glyph_y); if (glyph_atlas == NULL) { /* no cache for this glyph */ glyph_atlas = GetGlyphPicture(glyph, screen); glyph_x = glyph_y = 0; } } uxa_composite(op, pSrc, glyph_atlas, pDst, xSrc + x - glyph->info.x, ySrc + y - glyph->info.y, glyph_x, glyph_y, x - glyph->info.x, y - glyph->info.y, glyph->info.width, glyph->info.height); next_glyph: x += glyph->info.xOff; y += glyph->info.yOff; } list++; } return 0; }
static ExaGlyphCacheResult exaBufferGlyph(ScreenPtr pScreen, ExaGlyphBufferPtr buffer, GlyphPtr pGlyph, PicturePtr pSrc, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst) { ExaScreenPriv(pScreen); unsigned int format = (GetGlyphPicture(pGlyph, pScreen))->format; int width = pGlyph->info.width; int height = pGlyph->info.height; ExaCompositeRectPtr rect; PicturePtr mask; int i; if (buffer->count == GLYPH_BUFFER_SIZE) return ExaGlyphNeedFlush; if (PICT_FORMAT_BPP(format) == 1) format = PICT_a8; for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; if (format == cache->format && width <= cache->glyphWidth && height <= cache->glyphHeight) { ExaGlyphCacheResult result = exaGlyphCacheBufferGlyph(pScreen, &pExaScr-> glyphCaches [i], buffer, pGlyph, pSrc, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst); switch (result) { case ExaGlyphFail: break; case ExaGlyphSuccess: case ExaGlyphNeedFlush: return result; } } } /* Couldn't find the glyph in the cache, use the glyph picture directly */ mask = GetGlyphPicture(pGlyph, pScreen); if (buffer->mask && buffer->mask != mask) return ExaGlyphNeedFlush; buffer->mask = mask; rect = &buffer->rects[buffer->count]; rect->xSrc = xSrc; rect->ySrc = ySrc; rect->xMask = xMask; rect->yMask = yMask; rect->xDst = xDst; rect->yDst = yDst; rect->width = width; rect->height = height; buffer->count++; return ExaGlyphSuccess; }
/* The most efficient thing to way to upload the glyph to the screen * is to use the UploadToScreen() driver hook; this allows us to * pipeline glyph uploads and to avoid creating gpu backed pixmaps for * glyphs that we'll never use again. * * If we can't do it with UploadToScreen (because the glyph has a gpu copy, * etc), we fall back to CompositePicture. * * We need to damage the cache pixmap manually in either case because the damage * layer unwrapped the picture screen before calling exaGlyphs. */ static void exaGlyphCacheUploadGlyph(ScreenPtr pScreen, ExaGlyphCachePtr cache, int x, int y, GlyphPtr pGlyph) { ExaScreenPriv(pScreen); PicturePtr pGlyphPicture = GetGlyphPicture(pGlyph, pScreen); PixmapPtr pGlyphPixmap = (PixmapPtr) pGlyphPicture->pDrawable; ExaPixmapPriv(pGlyphPixmap); PixmapPtr pCachePixmap = (PixmapPtr) cache->picture->pDrawable; if (!pExaScr->info->UploadToScreen || pExaScr->swappedOut || pExaPixmap->accel_blocked) goto composite; /* If the glyph pixmap is already uploaded, no point in doing * things this way */ if (exaPixmapHasGpuCopy(pGlyphPixmap)) goto composite; /* UploadToScreen only works if bpp match */ if (pGlyphPixmap->drawable.bitsPerPixel != pCachePixmap->drawable.bitsPerPixel) goto composite; if (pExaScr->do_migration) { ExaMigrationRec pixmaps[1]; /* cache pixmap must have a gpu copy. */ pixmaps[0].as_dst = TRUE; pixmaps[0].as_src = FALSE; pixmaps[0].pPix = pCachePixmap; pixmaps[0].pReg = NULL; exaDoMigration(pixmaps, 1, TRUE); } if (!exaPixmapHasGpuCopy(pCachePixmap)) goto composite; /* x,y are in pixmap coordinates, no need for cache{X,Y}off */ if (pExaScr->info->UploadToScreen(pCachePixmap, x, y, pGlyph->info.width, pGlyph->info.height, (char *) pExaPixmap->sys_ptr, pExaPixmap->sys_pitch)) goto damage; composite: CompositePicture(PictOpSrc, pGlyphPicture, None, cache->picture, 0, 0, 0, 0, x, y, pGlyph->info.width, pGlyph->info.height); damage: /* The cache pixmap isn't a window, so no need to offset coordinates. */ exaPixmapDirty(pCachePixmap, x, y, x + cache->glyphWidth, y + cache->glyphHeight); }
void fbGlyphs(CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr *glyphs) { #define N_STACK_GLYPHS 512 ScreenPtr pScreen = pDst->pDrawable->pScreen; pixman_glyph_t stack_glyphs[N_STACK_GLYPHS]; pixman_glyph_t *pglyphs = stack_glyphs; pixman_image_t *srcImage, *dstImage; int srcXoff, srcYoff, dstXoff, dstYoff; GlyphPtr glyph; int n_glyphs; int x, y; int i, n; int xDst = list->xOff, yDst = list->yOff; miCompositeSourceValidate(pSrc); n_glyphs = 0; for (i = 0; i < nlist; ++i) n_glyphs += list[i].len; if (!glyphCache) glyphCache = pixman_glyph_cache_create(); pixman_glyph_cache_freeze (glyphCache); if (n_glyphs > N_STACK_GLYPHS) { if (!(pglyphs = malloc (n_glyphs * sizeof (pixman_glyph_t)))) goto out; } i = 0; x = y = 0; while (nlist--) { x += list->xOff; y += list->yOff; n = list->len; while (n--) { const void *g; glyph = *glyphs++; if (!(g = pixman_glyph_cache_lookup (glyphCache, glyph, NULL))) { pixman_image_t *glyphImage; PicturePtr pPicture; int xoff, yoff; pPicture = GetGlyphPicture(glyph, pScreen); if (!pPicture) { n_glyphs--; goto next; } if (!(glyphImage = image_from_pict(pPicture, FALSE, &xoff, &yoff))) goto out; g = pixman_glyph_cache_insert(glyphCache, glyph, NULL, glyph->info.x, glyph->info.y, glyphImage); free_pixman_pict(pPicture, glyphImage); if (!g) goto out; } pglyphs[i].x = x; pglyphs[i].y = y; pglyphs[i].glyph = g; i++; next: x += glyph->info.xOff; y += glyph->info.yOff; } list++; } if (!(srcImage = image_from_pict(pSrc, FALSE, &srcXoff, &srcYoff))) goto out; if (!(dstImage = image_from_pict(pDst, TRUE, &dstXoff, &dstYoff))) goto out_free_src; if (maskFormat) { pixman_format_code_t format; pixman_box32_t extents; format = maskFormat->format | (maskFormat->depth << 24); pixman_glyph_get_extents(glyphCache, n_glyphs, pglyphs, &extents); pixman_composite_glyphs(op, srcImage, dstImage, format, xSrc + srcXoff + extents.x1 - xDst, ySrc + srcYoff + extents.y1 - yDst, extents.x1, extents.y1, extents.x1 + dstXoff, extents.y1 + dstYoff, extents.x2 - extents.x1, extents.y2 - extents.y1, glyphCache, n_glyphs, pglyphs); } else { pixman_composite_glyphs_no_mask(op, srcImage, dstImage, xSrc + srcXoff - xDst, ySrc + srcYoff - yDst, dstXoff, dstYoff, glyphCache, n_glyphs, pglyphs); } free_pixman_pict(pDst, dstImage); out_free_src: free_pixman_pict(pSrc, srcImage); out: pixman_glyph_cache_thaw(glyphCache); if (pglyphs != stack_glyphs) free(pglyphs); }
static int uxa_glyphs_via_mask(CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr * glyphs) { ScreenPtr screen = pDst->pDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(screen); CARD32 component_alpha; PixmapPtr pixmap, white_pixmap; PicturePtr glyph_atlas, mask, white; int xDst = list->xOff, yDst = list->yOff; int x, y, width, height; int dst_off_x, dst_off_y; int n, error; BoxRec box; uxa_glyph_extents(nlist, list, glyphs, &box); if (box.x2 <= box.x1 || box.y2 <= box.y1) return 0; dst_off_x = box.x1; dst_off_y = box.y1; width = box.x2 - box.x1; height = box.y2 - box.y1; x = -box.x1; y = -box.y1; if (maskFormat->depth == 1) { PictFormatPtr a8Format = PictureMatchFormat(screen, 8, PICT_a8); if (!a8Format) return -1; maskFormat = a8Format; } pixmap = screen->CreatePixmap(screen, width, height, maskFormat->depth, CREATE_PIXMAP_USAGE_SCRATCH); if (!pixmap) return 1; if (!uxa_pixmap_is_offscreen(pixmap)) { screen->DestroyPixmap(pixmap); return -1; } white_pixmap = NULL; white = create_white_solid(screen); if (white) white_pixmap = uxa_get_drawable_pixmap(white->pDrawable); if (!white_pixmap) { if (white) FreePicture(white, 0); screen->DestroyPixmap(pixmap); return -1; } uxa_clear_pixmap(screen, uxa_screen, pixmap); component_alpha = NeedsComponent(maskFormat->format); mask = CreatePicture(0, &pixmap->drawable, maskFormat, CPComponentAlpha, &component_alpha, serverClient, &error); screen->DestroyPixmap(pixmap); if (!mask) { FreePicture(white, 0); return 1; } ValidatePicture(mask); glyph_atlas = NULL; while (nlist--) { x += list->xOff; y += list->yOff; n = list->len; while (n--) { GlyphPtr glyph = *glyphs++; PicturePtr this_atlas; int glyph_x, glyph_y; struct uxa_glyph *priv; if (glyph->info.width == 0 || glyph->info.height == 0) goto next_glyph; priv = uxa_glyph_get_private(glyph); if (priv != NULL) { glyph_x = priv->x; glyph_y = priv->y; this_atlas = priv->cache->picture; } else { if (glyph_atlas) { uxa_screen->info->done_composite(pixmap); glyph_atlas = NULL; } this_atlas = uxa_glyph_cache(screen, glyph, &glyph_x, &glyph_y); if (this_atlas == NULL) { /* no cache for this glyph */ this_atlas = GetGlyphPicture(glyph, screen); glyph_x = glyph_y = 0; } } if (this_atlas != glyph_atlas) { PixmapPtr glyph_pixmap; if (glyph_atlas) uxa_screen->info->done_composite(pixmap); glyph_pixmap = uxa_get_drawable_pixmap(this_atlas->pDrawable); if (!uxa_pixmap_is_offscreen(glyph_pixmap) || !uxa_screen->info->prepare_composite(PictOpAdd, white, this_atlas, mask, white_pixmap, glyph_pixmap, pixmap)) { FreePicture(white, 0); FreePicture(mask, 0); return -1; } glyph_atlas = this_atlas; } uxa_screen->info->composite(pixmap, 0, 0, glyph_x, glyph_y, x - glyph->info.x, y - glyph->info.y, glyph->info.width, glyph->info.height); next_glyph: x += glyph->info.xOff; y += glyph->info.yOff; } list++; } if (glyph_atlas) uxa_screen->info->done_composite(pixmap); uxa_composite(op, pSrc, mask, pDst, dst_off_x + xSrc - xDst, dst_off_y + ySrc - yDst, 0, 0, dst_off_x, dst_off_y, width, height); FreePicture(white, 0); FreePicture(mask, 0); return 0; }
static PicturePtr uxa_glyph_cache(ScreenPtr screen, GlyphPtr glyph, int *out_x, int *out_y) { uxa_screen_t *uxa_screen = uxa_get_screen(screen); PicturePtr glyph_picture = GetGlyphPicture(glyph, screen); uxa_glyph_cache_t *cache = &uxa_screen->glyphCaches[PICT_FORMAT_RGB(glyph_picture->format) != 0]; struct uxa_glyph *priv = NULL; int size, mask, pos, s; if (glyph->info.width > GLYPH_MAX_SIZE || glyph->info.height > GLYPH_MAX_SIZE) return NULL; for (size = GLYPH_MIN_SIZE; size <= GLYPH_MAX_SIZE; size *= 2) if (glyph->info.width <= size && glyph->info.height <= size) break; s = uxa_glyph_size_to_count(size); mask = uxa_glyph_count_to_mask(s); pos = (cache->count + s - 1) & mask; if (pos < GLYPH_CACHE_SIZE) { cache->count = pos + s; } else { for (s = size; s <= GLYPH_MAX_SIZE; s *= 2) { int i = cache->evict & uxa_glyph_size_to_mask(s); GlyphPtr evicted = cache->glyphs[i]; if (evicted == NULL) continue; priv = uxa_glyph_get_private(evicted); if (priv->size >= s) { cache->glyphs[i] = NULL; uxa_glyph_set_private(evicted, NULL); pos = cache->evict & uxa_glyph_size_to_mask(size); } else priv = NULL; break; } if (priv == NULL) { int count = uxa_glyph_size_to_count(size); mask = uxa_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 = uxa_glyph_get_private(evicted); uxa_glyph_set_private(evicted, NULL); cache->glyphs[pos + s] = NULL; } } } /* And pick a new eviction position */ cache->evict = rand() % GLYPH_CACHE_SIZE; } if (priv == NULL) { priv = malloc(sizeof(struct uxa_glyph)); if (priv == NULL) return NULL; } uxa_glyph_set_private(glyph, priv); cache->glyphs[pos] = glyph; priv->cache = cache; priv->size = size; priv->pos = pos; s = pos / ((GLYPH_MAX_SIZE / GLYPH_MIN_SIZE) * (GLYPH_MAX_SIZE / GLYPH_MIN_SIZE)); priv->x = s % (CACHE_PICTURE_SIZE / GLYPH_MAX_SIZE) * GLYPH_MAX_SIZE; priv->y = (s / (CACHE_PICTURE_SIZE / GLYPH_MAX_SIZE)) * GLYPH_MAX_SIZE; for (s = GLYPH_MIN_SIZE; s < GLYPH_MAX_SIZE; s *= 2) { if (pos & 1) priv->x += s; if (pos & 2) priv->y += s; pos >>= 2; } uxa_glyph_cache_upload_glyph(screen, cache, glyph, priv->x, priv->y); *out_x = priv->x; *out_y = priv->y; return cache->picture; }
static void uxa_check_glyphs(CARD8 op, PicturePtr src, PicturePtr dst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr * glyphs) { pixman_image_t *image; PixmapPtr scratch; PicturePtr mask, mask_src = NULL, mask_dst = NULL, white = NULL; int width = 0, height = 0; int x, y, n; int xDst = list->xOff, yDst = list->yOff; BoxRec extents = { 0, 0, 0, 0 }; CARD8 mask_op = 0; if (maskFormat) { pixman_format_code_t format; CARD32 component_alpha; xRenderColor color; int error; uxa_glyph_extents(nlist, list, glyphs, &extents); if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1) return; width = extents.x2 - extents.x1; height = extents.y2 - extents.y1; format = maskFormat->format | (BitsPerPixel(maskFormat->depth) << 24); image = pixman_image_create_bits(format, width, height, NULL, 0); if (!image) return; scratch = GetScratchPixmapHeader(dst->pDrawable->pScreen, width, height, PIXMAN_FORMAT_DEPTH(format), PIXMAN_FORMAT_BPP(format), pixman_image_get_stride(image), pixman_image_get_data(image)); if (!scratch) { pixman_image_unref(image); return; } component_alpha = NeedsComponent(maskFormat->format); mask = CreatePicture(0, &scratch->drawable, maskFormat, CPComponentAlpha, &component_alpha, serverClient, &error); if (!mask) { FreeScratchPixmapHeader(scratch); pixman_image_unref(image); return; } ValidatePicture(mask); x = -extents.x1; y = -extents.y1; color.red = color.green = color.blue = color.alpha = 0xffff; white = CreateSolidPicture(0, &color, &error); mask_op = op; op = PictOpAdd; mask_src = src; src = white; mask_dst = dst; dst = mask; } else { mask = dst; x = 0; y = 0; } while (nlist--) { x += list->xOff; y += list->yOff; n = list->len; while (n--) { GlyphPtr glyph = *glyphs++; PicturePtr g = GetGlyphPicture(glyph, dst->pDrawable->pScreen); if (g) { CompositePicture(op, src, g, dst, xSrc + (x - glyph->info.x) - xDst, ySrc + (y - glyph->info.y) - yDst, 0, 0, x - glyph->info.x, y - glyph->info.y, glyph->info.width, glyph->info.height); } x += glyph->info.xOff; y += glyph->info.yOff; } list++; } if (white) FreePicture(white, 0); if (maskFormat) { x = extents.x1; y = extents.y1; CompositePicture(mask_op, mask_src, mask, mask_dst, xSrc + x - xDst, ySrc + y - yDst, 0, 0, x, y, width, height); FreePicture(mask, 0); FreeScratchPixmapHeader(scratch); pixman_image_unref(image); } }
/* The most efficient thing to way to upload the glyph to the screen * is to use CopyArea; uxa pixmaps are always offscreen. */ static void uxa_glyph_cache_upload_glyph(ScreenPtr screen, uxa_glyph_cache_t * cache, GlyphPtr glyph, int x, int y) { PicturePtr pGlyphPicture = GetGlyphPicture(glyph, screen); PixmapPtr pGlyphPixmap = (PixmapPtr) pGlyphPicture->pDrawable; PixmapPtr pCachePixmap = (PixmapPtr) cache->picture->pDrawable; PixmapPtr scratch; GCPtr gc; gc = GetScratchGC(pCachePixmap->drawable.depth, screen); if (!gc) return; ValidateGC(&pCachePixmap->drawable, gc); scratch = pGlyphPixmap; /* Create a temporary bo to stream the updates to the cache */ if (pGlyphPixmap->drawable.depth != pCachePixmap->drawable.depth || !uxa_pixmap_is_offscreen(scratch)) { scratch = screen->CreatePixmap(screen, glyph->info.width, glyph->info.height, pCachePixmap->drawable.depth, UXA_CREATE_PIXMAP_FOR_MAP); if (scratch) { if (pGlyphPixmap->drawable.depth != pCachePixmap->drawable.depth) { PicturePtr picture; int error; picture = CreatePicture(0, &scratch->drawable, PictureMatchFormat(screen, pCachePixmap->drawable.depth, cache->picture->format), 0, NULL, serverClient, &error); if (picture) { ValidatePicture(picture); uxa_composite(PictOpSrc, pGlyphPicture, NULL, picture, 0, 0, 0, 0, 0, 0, glyph->info.width, glyph->info.height); FreePicture(picture, 0); } } else { uxa_copy_area(&pGlyphPixmap->drawable, &scratch->drawable, gc, 0, 0, glyph->info.width, glyph->info.height, 0, 0); } } else { scratch = pGlyphPixmap; } } uxa_copy_area(&scratch->drawable, &pCachePixmap->drawable, gc, 0, 0, glyph->info.width, glyph->info.height, x, y); if (scratch != pGlyphPixmap) screen->DestroyPixmap(scratch); FreeScratchGC(gc); }