summaryrefslogtreecommitdiff
path: root/src/sna/sna_glyphs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sna/sna_glyphs.c')
-rw-r--r--src/sna/sna_glyphs.c1145
1 files changed, 1145 insertions, 0 deletions
diff --git a/src/sna/sna_glyphs.c b/src/sna/sna_glyphs.c
new file mode 100644
index 00000000..bb4b9cde
--- /dev/null
+++ b/src/sna/sna_glyphs.c
@@ -0,0 +1,1145 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ * Partly based on code Copyright © 2008 Red Hat, Inc.
+ * Partly based on code Copyright © 2000 SuSE, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Intel not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Intel makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INTEL
+ * 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
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Red Hat not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Red Hat makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * Red Hat DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * 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
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of SuSE not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. SuSE makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * 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
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ * Based on code by: Keith Packard <keithp@keithp.com> and Owen Taylor <otaylor@fishsoup.net>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sna.h"
+#include "sna_render.h"
+#include "sna_render_inline.h"
+
+#include <mipict.h>
+#include <fbpict.h>
+#include <fb.h>
+
+#if DEBUG_GLYPHS
+#undef DBG
+#define DBG(x) ErrorF x
+#else
+#define NDEBUG 1
+#endif
+
+#define CACHE_PICTURE_SIZE 1024
+#define GLYPH_MIN_SIZE 8
+#define GLYPH_MAX_SIZE 64
+#define GLYPH_CACHE_SIZE (CACHE_PICTURE_SIZE * CACHE_PICTURE_SIZE / (GLYPH_MIN_SIZE * GLYPH_MIN_SIZE))
+
+#if DEBUG_GLYPHS
+static void _assert_pixmap_contains_box(PixmapPtr pixmap, BoxPtr box, const char *function)
+{
+ if (box->x1 < 0 || box->y1 < 0 ||
+ box->x2 > pixmap->drawable.width ||
+ box->y2 > pixmap->drawable.height)
+ {
+ ErrorF("%s: damage box is beyond the pixmap: box=(%d, %d), (%d, %d), pixmap=(%d, %d)\n",
+ __FUNCTION__,
+ box->x1, box->y1, box->x2, box->y2,
+ pixmap->drawable.width,
+ pixmap->drawable.height);
+ assert(0);
+ }
+}
+#define assert_pixmap_contains_box(p, b) _assert_pixmap_contains_box(p, b, __FUNCTION__)
+#else
+#define assert_pixmap_contains_box(p, b)
+#endif
+
+struct sna_glyph {
+ PicturePtr atlas;
+ struct sna_coordinate coordinate;
+ uint16_t size, pos;
+};
+
+static DevPrivateKeyRec sna_glyph_key;
+
+static inline struct sna_glyph *glyph_get_private(GlyphPtr glyph)
+{
+ return dixGetPrivateAddr(&glyph->devPrivates, &sna_glyph_key);
+}
+
+#define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0)
+
+static void unrealize_glyph_caches(struct sna *sna)
+{
+ struct sna_render *render = &sna->render;
+ int i;
+
+ DBG(("%s\n", __FUNCTION__));
+
+ for (i = 0; i < ARRAY_SIZE(render->glyph); i++) {
+ struct sna_glyph_cache *cache = &render->glyph[i];
+
+ if (cache->picture)
+ FreePicture(cache->picture, 0);
+
+ free(cache->glyphs);
+ }
+ memset(render->glyph, 0, sizeof(render->glyph));
+}
+
+/* All caches for a single format share a single pixmap for glyph storage,
+ * allowing mixing glyphs of different sizes without paying a penalty
+ * for switching between source pixmaps. (Note that for a size of font
+ * right at the border between two sizes, we might be switching for almost
+ * every glyph.)
+ *
+ * This function allocates the storage pixmap, and then fills in the
+ * rest of the allocated structures for all caches with the given format.
+ */
+static Bool realize_glyph_caches(struct sna *sna)
+{
+ ScreenPtr screen = sna->scrn->pScreen;
+ unsigned int formats[] = {
+ PIXMAN_a8,
+ PIXMAN_a8r8g8b8,
+ };
+ int i;
+
+ DBG(("%s\n", __FUNCTION__));
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ struct sna_glyph_cache *cache = &sna->render.glyph[i];
+ PixmapPtr pixmap;
+ PicturePtr picture;
+ CARD32 component_alpha;
+ int depth = PIXMAN_FORMAT_DEPTH(formats[i]);
+ int error;
+ PictFormatPtr pPictFormat = PictureMatchFormat(screen, depth, formats[i]);
+ if (!pPictFormat)
+ goto bail;
+
+ /* Now allocate the pixmap and picture */
+ pixmap = screen->CreatePixmap(screen,
+ CACHE_PICTURE_SIZE,
+ CACHE_PICTURE_SIZE,
+ depth,
+ CREATE_PIXMAP_USAGE_SCRATCH);
+ if (!pixmap)
+ goto bail;
+
+ component_alpha = NeedsComponent(pPictFormat->format);
+ picture = CreatePicture(0, &pixmap->drawable, pPictFormat,
+ CPComponentAlpha, &component_alpha,
+ serverClient, &error);
+
+ screen->DestroyPixmap(pixmap);
+
+ if (!picture)
+ goto bail;
+
+ ValidatePicture(picture);
+
+ cache->count = cache->evict = 0;
+ cache->picture = picture;
+ cache->glyphs = calloc(sizeof(struct sna_glyph *),
+ GLYPH_CACHE_SIZE);
+ if (!cache->glyphs)
+ goto bail;
+
+ cache->evict = rand() % GLYPH_CACHE_SIZE;
+ }
+
+ return TRUE;
+
+bail:
+ unrealize_glyph_caches(sna);
+ return FALSE;
+}
+
+static void
+glyph_cache_upload(ScreenPtr screen,
+ struct sna_glyph_cache *cache,
+ GlyphPtr glyph,
+ int16_t x, int16_t y)
+{
+ DBG(("%s: upload glyph %p to cache (%d, %d)x(%d, %d)\n",
+ __FUNCTION__, glyph, x, y, glyph->info.width, glyph->info.height));
+ sna_composite(PictOpSrc,
+ GlyphPicture(glyph)[screen->myNum], 0, cache->picture,
+ 0, 0,
+ 0, 0,
+ x, y,
+ glyph->info.width,
+ glyph->info.height);
+}
+
+static void
+glyph_extents(int nlist,
+ GlyphListPtr list,
+ GlyphPtr *glyphs,
+ BoxPtr extents)
+{
+ int16_t x1, x2, y1, y2;
+ int16_t x, y;
+
+ x1 = y1 = MAXSHORT;
+ x2 = y2 = MINSHORT;
+ x = y = 0;
+ while (nlist--) {
+ int n = list->len;
+ x += list->xOff;
+ y += list->yOff;
+ list++;
+ while (n--) {
+ GlyphPtr glyph = *glyphs++;
+
+ if (glyph->info.width && glyph->info.height) {
+ int v;
+
+ v = x - glyph->info.x;
+ if (v < x1)
+ x1 = v;
+ v += glyph->info.width;
+ if (v > x2)
+ x2 = v;
+
+ v = y - glyph->info.y;
+ if (v < y1)
+ y1 = v;
+ v += glyph->info.height;
+ if (v > y2)
+ y2 = v;
+ }
+
+ x += glyph->info.xOff;
+ y += glyph->info.yOff;
+ }
+ }
+
+ extents->x1 = x1;
+ extents->x2 = x2;
+ extents->y1 = y1;
+ extents->y2 = y2;
+}
+
+static inline unsigned int
+glyph_size_to_count(int size)
+{
+ size /= GLYPH_MIN_SIZE;
+ return size * size;
+}
+
+static inline unsigned int
+glyph_count_to_mask(int count)
+{
+ return ~(count - 1);
+}
+
+static inline unsigned int
+glyph_size_to_mask(int size)
+{
+ return glyph_count_to_mask(glyph_size_to_count(size));
+}
+
+static int
+glyph_cache(ScreenPtr screen,
+ struct sna_render *render,
+ GlyphPtr glyph)
+{
+ PicturePtr glyph_picture = GlyphPicture(glyph)[screen->myNum];
+ struct sna_glyph_cache *cache = &render->glyph[PICT_FORMAT_RGB(glyph_picture->format) != 0];
+ struct sna_glyph *priv;
+ int size, mask, pos, s;
+
+ if (glyph->info.width > GLYPH_MAX_SIZE ||
+ glyph->info.height > GLYPH_MAX_SIZE)
+ return FALSE;
+
+ for (size = GLYPH_MIN_SIZE; size <= GLYPH_MAX_SIZE; size *= 2)
+ if (glyph->info.width <= size && glyph->info.height <= size)
+ break;
+
+ s = glyph_size_to_count(size);
+ mask = glyph_count_to_mask(s);
+ pos = (cache->count + s - 1) & mask;
+ if (pos < GLYPH_CACHE_SIZE) {
+ cache->count = pos + s;
+ } else {
+ priv = NULL;
+ for (s = size; s <= GLYPH_MAX_SIZE; s *= 2) {
+ int i = cache->evict & glyph_size_to_mask(s);
+ priv = cache->glyphs[i];
+ if (priv == NULL)
+ continue;
+
+ if (priv->size >= s) {
+ cache->glyphs[i] = NULL;
+ priv->atlas = NULL;
+ pos = i;
+ } else
+ priv = NULL;
+ break;
+ }
+ if (priv == NULL) {
+ int count = glyph_size_to_count(size);
+ pos = cache->evict & glyph_count_to_mask(count);
+ for (s = 0; s < count; s++) {
+ priv = cache->glyphs[pos + s];
+ if (priv != NULL) {
+ priv->atlas =NULL;
+ cache->glyphs[pos + s] = NULL;
+ }
+ }
+ }
+
+ /* And pick a new eviction position */
+ cache->evict = rand() % GLYPH_CACHE_SIZE;
+ }
+ assert(cache->glyphs[pos] == NULL);
+
+ priv = glyph_get_private(glyph);
+ cache->glyphs[pos] = priv;
+ priv->atlas = cache->picture;
+ priv->size = size;
+ priv->pos = pos << 1 | (PICT_FORMAT_RGB(glyph_picture->format) != 0);
+ s = pos / ((GLYPH_MAX_SIZE / GLYPH_MIN_SIZE) * (GLYPH_MAX_SIZE / GLYPH_MIN_SIZE));
+ priv->coordinate.x = s % (CACHE_PICTURE_SIZE / GLYPH_MAX_SIZE) * GLYPH_MAX_SIZE;
+ priv->coordinate.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->coordinate.x += s;
+ if (pos & 2)
+ priv->coordinate.y += s;
+ pos >>= 2;
+ }
+
+ glyph_cache_upload(screen, cache, glyph,
+ priv->coordinate.x, priv->coordinate.y);
+
+ return TRUE;
+}
+
+static void apply_damage(struct sna_composite_op *op,
+ const struct sna_composite_rectangles *r)
+{
+ BoxRec box;
+
+ if (op->damage == NULL)
+ return;
+
+ box.x1 = r->dst.x + op->dst.x;
+ box.y1 = r->dst.y + op->dst.y;
+ box.x2 = box.x1 + r->width;
+ box.y2 = box.y1 + r->height;
+
+ assert_pixmap_contains_box(op->dst.pixmap, &box);
+ sna_damage_add_box(op->damage, &box);
+}
+
+#define GET_PRIVATE(g) ((struct sna_glyph *)((char *)(g)->devPrivates + priv_offset))
+static Bool
+glyphs_to_dst(struct sna *sna,
+ CARD8 op,
+ PicturePtr src,
+ PicturePtr dst,
+ INT16 src_x, INT16 src_y,
+ int nlist, GlyphListPtr list, GlyphPtr *glyphs)
+{
+ struct sna_composite_op tmp;
+ ScreenPtr screen = dst->pDrawable->pScreen;
+ const int priv_offset = sna_glyph_key.offset;
+ int index = screen->myNum;
+ PicturePtr glyph_atlas;
+ BoxPtr rects;
+ int nrect;
+ int16_t x, y;
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ DBG(("%s(op=%d, src=(%d, %d), nlist=%d, dst=(%d, %d)+(%d, %d))\n",
+ __FUNCTION__, op, src_x, src_y, nlist,
+ list->xOff, list->yOff, dst->pDrawable->x, dst->pDrawable->y));
+
+ rects = REGION_RECTS(dst->pCompositeClip);
+ nrect = REGION_NUM_RECTS(dst->pCompositeClip);
+
+ x = dst->pDrawable->x;
+ y = dst->pDrawable->y;
+ src_x -= list->xOff + x;
+ src_y -= list->yOff + y;
+
+ glyph_atlas = NULL;
+ while (nlist--) {
+ int n = list->len;
+ x += list->xOff;
+ y += list->yOff;
+ while (n--) {
+ GlyphPtr glyph = *glyphs++;
+ struct sna_glyph priv;
+ int i;
+
+ if (glyph->info.width == 0 || glyph->info.height == 0)
+ goto next_glyph;
+
+ priv = *GET_PRIVATE(glyph);
+ if (priv.atlas == NULL) {
+ if (glyph_atlas) {
+ tmp.done(sna, &tmp);
+ glyph_atlas = NULL;
+ }
+ if (!glyph_cache(screen, &sna->render, glyph)) {
+ /* no cache for this glyph */
+ priv.atlas = GlyphPicture(glyph)[index];
+ priv.coordinate.x = priv.coordinate.y = 0;
+ } else
+ priv = *GET_PRIVATE(glyph);
+ }
+
+ if (priv.atlas != glyph_atlas) {
+ if (glyph_atlas)
+ tmp.done(sna, &tmp);
+
+ if (!sna->render.composite(sna,
+ op, src, priv.atlas, dst,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0,
+ &tmp))
+ return FALSE;
+
+ glyph_atlas = priv.atlas;
+ }
+
+ for (i = 0; i < nrect; i++) {
+ struct sna_composite_rectangles r;
+ int16_t dx, dy;
+ int16_t x2, y2;
+
+ r.dst.x = x - glyph->info.x;
+ r.dst.y = y - glyph->info.y;
+ x2 = r.dst.x + glyph->info.width;
+ y2 = r.dst.y + glyph->info.height;
+ dx = dy = 0;
+
+ DBG(("%s: glyph=(%d, %d), (%d, %d), clip=(%d, %d), (%d, %d)\n",
+ __FUNCTION__,
+ r.dst.x, r.dst.y, x2, y2,
+ rects[i].x1, rects[i].y1,
+ rects[i].x2, rects[i].y2));
+ if (rects[i].y1 >= y2)
+ break;
+
+ if (r.dst.x < rects[i].x1)
+ dx = rects[i].x1 - r.dst.x, r.dst.x = rects[i].x1;
+ if (x2 > rects[i].x2)
+ x2 = rects[i].x2;
+ if (r.dst.y < rects[i].y1)
+ dy = rects[i].y1 - r.dst.y, r.dst.y = rects[i].y1;
+ if (y2 > rects[i].y2)
+ y2 = rects[i].y2;
+
+ if (r.dst.x < x2 && r.dst.y < y2) {
+ DBG(("%s: blt=(%d, %d), (%d, %d)\n",
+ __FUNCTION__, r.dst.x, r.dst.y, x2, y2));
+
+ r.src.x = r.dst.x + src_x;
+ r.src.y = r.dst.y + src_y;
+ r.mask.x = dx + priv.coordinate.x;
+ r.mask.y = dy + priv.coordinate.y;
+ r.width = x2 - r.dst.x;
+ r.height = y2 - r.dst.y;
+ tmp.blt(sna, &tmp, &r);
+ apply_damage(&tmp, &r);
+ }
+ }
+
+next_glyph:
+ x += glyph->info.xOff;
+ y += glyph->info.yOff;
+ }
+ list++;
+ }
+ if (glyph_atlas)
+ tmp.done(sna, &tmp);
+
+ return TRUE;
+}
+
+static Bool
+glyphs_to_dst_slow(struct sna *sna,
+ CARD8 op,
+ PicturePtr src,
+ PicturePtr dst,
+ INT16 src_x, INT16 src_y,
+ int nlist, GlyphListPtr list, GlyphPtr *glyphs)
+{
+ struct sna_composite_op tmp;
+ ScreenPtr screen = dst->pDrawable->pScreen;
+ const int priv_offset = sna_glyph_key.offset;
+ int index = screen->myNum;
+ int x, y, n;
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ DBG(("%s(op=%d, src=(%d, %d), nlist=%d, dst=(%d, %d)+(%d, %d))\n",
+ __FUNCTION__, op, src_x, src_y, nlist,
+ list->xOff, list->yOff, dst->pDrawable->x, dst->pDrawable->y));
+
+ x = dst->pDrawable->x;
+ y = dst->pDrawable->y;
+ src_x -= list->xOff + x;
+ src_y -= list->yOff + y;
+
+ while (nlist--) {
+ x += list->xOff;
+ y += list->yOff;
+ n = list->len;
+ while (n--) {
+ GlyphPtr glyph = *glyphs++;
+ struct sna_glyph priv;
+ BoxPtr rects;
+ int nrect;
+
+ if (glyph->info.width == 0 || glyph->info.height == 0)
+ goto next_glyph;
+
+ priv = *GET_PRIVATE(glyph);
+ if (priv.atlas == NULL) {
+ if (!glyph_cache(screen, &sna->render, glyph)) {
+ /* no cache for this glyph */
+ priv.atlas = GlyphPicture(glyph)[index];
+ priv.coordinate.x = priv.coordinate.y = 0;
+ } else
+ priv = *GET_PRIVATE(glyph);
+ }
+
+ if (!sna->render.composite(sna,
+ op, src, priv.atlas, dst,
+ src_x + x - glyph->info.x,
+ src_y + y - glyph->info.y,
+ priv.coordinate.x, priv.coordinate.y,
+ x - glyph->info.x,
+ y - glyph->info.y,
+ glyph->info.width,
+ glyph->info.height,
+ &tmp))
+ return FALSE;
+
+ rects = REGION_RECTS(dst->pCompositeClip);
+ nrect = REGION_NUM_RECTS(dst->pCompositeClip);
+ do {
+ struct sna_composite_rectangles r;
+ int16_t dx, dy;
+ int16_t x2, y2;
+
+ r.dst.x = x - glyph->info.x;
+ r.dst.y = y - glyph->info.y;
+ x2 = r.dst.x + glyph->info.width;
+ y2 = r.dst.y + glyph->info.height;
+ dx = dy = 0;
+
+ DBG(("%s: glyph=(%d, %d), (%d, %d), clip=(%d, %d), (%d, %d)\n",
+ __FUNCTION__,
+ r.dst.x, r.dst.y, x2, y2,
+ rects->x1, rects->y1,
+ rects->x2, rects->y2));
+ if (rects->y1 >= y2)
+ break;
+
+ if (r.dst.x < rects->x1)
+ dx = rects->x1 - r.dst.x, r.dst.x = rects->x1;
+ if (x2 > rects->x2)
+ x2 = rects->x2;
+ if (r.dst.y < rects->y1)
+ dy = rects->y1 - r.dst.y, r.dst.y = rects->y1;
+ if (y2 > rects->y2)
+ y2 = rects->y2;
+
+ if (r.dst.x < x2 && r.dst.y < y2) {
+ DBG(("%s: blt=(%d, %d), (%d, %d)\n",
+ __FUNCTION__, r.dst.x, r.dst.y, x2, y2));
+
+ r.src.x = r.dst.x + src_x;
+ r.src.y = r.dst.y + src_y;
+ r.mask.x = dx + priv.coordinate.x;
+ r.mask.y = dy + priv.coordinate.y;
+ r.width = x2 - r.dst.x;
+ r.height = y2 - r.dst.y;
+ tmp.blt(sna, &tmp, &r);
+ apply_damage(&tmp, &r);
+ }
+ rects++;
+ } while (--nrect);
+ tmp.done(sna, &tmp);
+
+next_glyph:
+ x += glyph->info.xOff;
+ y += glyph->info.yOff;
+ }
+ list++;
+ }
+
+ return TRUE;
+}
+
+static Bool
+clear_pixmap(struct sna *sna, PixmapPtr pixmap, PictFormat format)
+{
+ BoxRec box;
+ xRenderColor color = { 0 };
+
+ box.x1 = box.y1 = 0;
+ box.x2 = pixmap->drawable.width;
+ box.y2 = pixmap->drawable.height;
+
+ return sna->render.fill_boxes(sna, PictOpClear, format, &color,
+ pixmap, sna_pixmap_get_bo(pixmap),
+ &box, 1);
+}
+
+static Bool
+glyphs_via_mask(struct sna *sna,
+ CARD8 op,
+ PicturePtr src,
+ PicturePtr dst,
+ PictFormatPtr format,
+ INT16 src_x, INT16 src_y,
+ int nlist, GlyphListPtr list, GlyphPtr *glyphs)
+{
+ ScreenPtr screen = dst->pDrawable->pScreen;
+ struct sna_composite_op tmp;
+ const int priv_offset = sna_glyph_key.offset;
+ int index = screen->myNum;
+ CARD32 component_alpha;
+ PixmapPtr pixmap;
+ PicturePtr glyph_atlas, mask;
+ int16_t x, y, width, height;
+ int n, error;
+ BoxRec box;
+
+ DBG(("%s(op=%d, src=(%d, %d), nlist=%d, dst=(%d, %d)+(%d, %d))\n",
+ __FUNCTION__, op, src_x, src_y, nlist,
+ list->xOff, list->yOff, dst->pDrawable->x, dst->pDrawable->y));
+
+ glyph_extents(nlist, list, glyphs, &box);
+ if (box.x2 <= box.x1 || box.y2 <= box.y1)
+ return TRUE;
+
+ DBG(("%s: bounds=((%d, %d), (%d, %d))\n", __FUNCTION__,
+ box.x1, box.y1, box.x2, box.y2));
+
+ if (!sna_compute_composite_extents(&box,
+ src, NULL, dst,
+ src_x, src_y,
+ 0, 0,
+ box.x1, box.y1,
+ box.x2 - box.x1,
+ box.y2 - box.y1))
+ return TRUE;
+
+ DBG(("%s: extents=((%d, %d), (%d, %d))\n", __FUNCTION__,
+ box.x1, box.y1, box.x2, box.y2));
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ width = box.x2 - box.x1;
+ height = box.y2 - box.y1;
+ box.x1 -= dst->pDrawable->x;
+ box.y1 -= dst->pDrawable->y;
+ x = -box.x1;
+ y = -box.y1;
+ src_x += box.x1 - list->xOff;
+ src_y += box.y1 - list->yOff;
+
+ if (format->depth == 1) {
+ PictFormatPtr a8Format =
+ PictureMatchFormat(screen, 8, PICT_a8);
+ if (!a8Format)
+ return FALSE;
+
+ format = a8Format;
+ }
+
+ pixmap = screen->CreatePixmap(screen,
+ width, height, format->depth,
+ CREATE_PIXMAP_USAGE_SCRATCH);
+ if (!pixmap)
+ return FALSE;
+
+ component_alpha = NeedsComponent(format->format);
+ mask = CreatePicture(0, &pixmap->drawable,
+ format, CPComponentAlpha,
+ &component_alpha, serverClient, &error);
+ screen->DestroyPixmap(pixmap);
+ if (!mask)
+ return FALSE;
+
+ ValidatePicture(mask);
+
+ if (!clear_pixmap(sna, pixmap, mask->format)) {
+ FreePicture(mask, 0);
+ return FALSE;
+ }
+
+ glyph_atlas = NULL;
+ do {
+ x += list->xOff;
+ y += list->yOff;
+ n = list->len;
+ while (n--) {
+ GlyphPtr glyph = *glyphs++;
+ struct sna_glyph *priv;
+ PicturePtr this_atlas;
+ struct sna_composite_rectangles r;
+
+ if (glyph->info.width == 0 || glyph->info.height == 0)
+ goto next_glyph;
+
+ priv = GET_PRIVATE(glyph);
+ if (priv->atlas != NULL) {
+ this_atlas = priv->atlas;
+ r.src = priv->coordinate;
+ } else {
+ if (glyph_atlas) {
+ tmp.done(sna, &tmp);
+ glyph_atlas = NULL;
+ }
+ if (glyph_cache(screen, &sna->render, glyph)) {
+ this_atlas = priv->atlas;
+ r.src = priv->coordinate;
+ } else {
+ /* no cache for this glyph */
+ this_atlas = GlyphPicture(glyph)[index];
+ r.src.x = r.src.y = 0;
+ }
+ }
+
+ if (this_atlas != glyph_atlas) {
+ if (glyph_atlas)
+ tmp.done(sna, &tmp);
+
+ if (!sna->render.composite(sna, PictOpAdd,
+ this_atlas, NULL, mask,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0,
+ &tmp)) {
+ FreePicture(mask, 0);
+ return FALSE;
+ }
+
+ glyph_atlas = this_atlas;
+ }
+
+ DBG(("%s: blt glyph origin (%d, %d), offset (%d, %d), src (%d, %d), size (%d, %d)\n",
+ __FUNCTION__,
+ x, y,
+ glyph->info.x, glyph->info.y,
+ r.src.x, r.src.y,
+ glyph->info.width, glyph->info.height));
+
+ r.dst.x = x - glyph->info.x;
+ r.dst.y = y - glyph->info.y;
+ r.width = glyph->info.width;
+ r.height = glyph->info.height;
+ tmp.blt(sna, &tmp, &r);
+
+next_glyph:
+ x += glyph->info.xOff;
+ y += glyph->info.yOff;
+ }
+ list++;
+ } while (--nlist);
+ if (glyph_atlas)
+ tmp.done(sna, &tmp);
+
+ sna_composite(op,
+ src, mask, dst,
+ src_x, src_y,
+ 0, 0,
+ box.x1, box.y1,
+ width, height);
+
+ FreePicture(mask, 0);
+ return TRUE;
+}
+
+Bool sna_glyphs_init(ScreenPtr screen)
+{
+ if (!dixRegisterPrivateKey(&sna_glyph_key,
+ PRIVATE_GLYPH,
+ sizeof(struct sna_glyph)))
+ return FALSE;
+
+ return TRUE;
+}
+
+Bool sna_glyphs_create(struct sna *sna)
+{
+ return realize_glyph_caches(sna);
+}
+
+static PictFormatPtr
+glyphs_format(int nlist, GlyphListPtr list, GlyphPtr * glyphs)
+{
+ PictFormatPtr format = list[0].format;
+ int16_t x1, x2, y1, y2;
+ int16_t x, y;
+ BoxRec extents;
+ Bool first = TRUE;
+
+ x = 0;
+ y = 0;
+ extents.x1 = 0;
+ extents.y1 = 0;
+ extents.x2 = 0;
+ extents.y2 = 0;
+ while (nlist--) {
+ int n = list->len;
+
+ if (format->format != list->format->format)
+ return NULL;
+
+ x += list->xOff;
+ y += list->yOff;
+ list++;
+ while (n--) {
+ GlyphPtr glyph = *glyphs++;
+
+ if (glyph->info.width == 0 || glyph->info.height == 0) {
+ x += glyph->info.xOff;
+ y += glyph->info.yOff;
+ continue;
+ }
+
+ x1 = x - glyph->info.x;
+ if (x1 < MINSHORT)
+ x1 = MINSHORT;
+ y1 = y - glyph->info.y;
+ if (y1 < MINSHORT)
+ y1 = MINSHORT;
+ x2 = x1 + glyph->info.width;
+ if (x2 > MAXSHORT)
+ x2 = MAXSHORT;
+ y2 = y1 + glyph->info.height;
+ if (y2 > MAXSHORT)
+ y2 = MAXSHORT;
+
+ if (first) {
+ extents.x1 = x1;
+ extents.y1 = y1;
+ extents.x2 = x2;
+ extents.y2 = y2;
+ first = FALSE;
+ } else {
+ /* Potential overlap */
+ if (x1 < extents.x2 && x2 > extents.x1 &&
+ y1 < extents.y2 && y2 > extents.y1)
+ return NULL;
+
+ if (x1 < extents.x1)
+ extents.x1 = x1;
+ if (x2 > extents.x2)
+ extents.x2 = x2;
+ if (y1 < extents.y1)
+ extents.y1 = y1;
+ if (y2 > extents.y2)
+ extents.y2 = y2;
+ }
+ x += glyph->info.xOff;
+ y += glyph->info.yOff;
+ }
+ }
+
+ return format;
+}
+
+static void
+glyphs_fallback(CARD8 op,
+ PicturePtr src,
+ PicturePtr dst,
+ PictFormatPtr mask_format,
+ int src_x,
+ int src_y,
+ int nlist,
+ GlyphListPtr list,
+ GlyphPtr *glyphs)
+{
+ int screen = dst->pDrawable->pScreen->myNum;
+ pixman_image_t *dst_image, *mask_image, *src_image;
+ int dx, dy, x, y;
+ BoxRec box;
+ RegionRec region;
+
+ glyph_extents(nlist, list, glyphs, &box);
+ if (box.x2 <= box.x1 || box.y2 <= box.y1)
+ return;
+
+ DBG(("%s: (%d, %d), (%d, %d)\n",
+ __FUNCTION__, box.x1, box.y1, box.x2, box.y2));
+
+ RegionInit(&region, &box, 1);
+ RegionTranslate(&region, dst->pDrawable->x, dst->pDrawable->y);
+ if (dst->pCompositeClip)
+ RegionIntersect(&region, &region, dst->pCompositeClip);
+ DBG(("%s: clipped extents (%d, %d), (%d, %d)\n",
+ __FUNCTION__,
+ RegionExtents(&region)->x1, RegionExtents(&region)->y1,
+ RegionExtents(&region)->x2, RegionExtents(&region)->y2));
+ if (!RegionNotEmpty(&region))
+ return;
+
+ sna_drawable_move_region_to_cpu(dst->pDrawable, &region,
+ true);
+ if (src->pDrawable)
+ sna_drawable_move_to_cpu(src->pDrawable, false);
+
+ dst_image = image_from_pict(dst, TRUE, &x, &y);
+ DBG(("%s: dst offset (%d, %d)\n", __FUNCTION__, x, y));
+ box.x1 += x;
+ box.x2 += x;
+ box.y1 += y;
+ box.y2 += y;
+
+ src_image = image_from_pict(src, FALSE, &dx, &dy);
+ DBG(("%s: src offset (%d, %d)\n", __FUNCTION__, dx, dy));
+ src_x += dx - list->xOff - x;
+ src_y += dy - list->yOff - y;
+
+ if (mask_format) {
+ mask_image =
+ pixman_image_create_bits(mask_format->depth << 24 | mask_format->format,
+ box.x2 - box.x1, box.y2 - box.y1,
+ NULL, 0);
+ if (NeedsComponent(mask_format->format))
+ pixman_image_set_component_alpha(mask_image, TRUE);
+
+ x -= box.x1;
+ y -= box.y1;
+ } else
+ mask_image = dst_image;
+
+ do {
+ int n = list->len;
+ x += list->xOff;
+ y += list->yOff;
+ while (n--) {
+ GlyphPtr g = *glyphs++;
+ PicturePtr picture;
+ pixman_image_t *glyph_image;
+ int dx, dy;
+
+ if (g->info.width == 0 || g->info.height == 0)
+ goto next_glyph;
+
+ picture = GlyphPicture(g)[screen];
+ if (picture == NULL)
+ goto next_glyph;
+
+ glyph_image = image_from_pict(picture, FALSE, &dx, &dy);
+ if (!glyph_image)
+ goto next_glyph;
+
+ if (mask_format) {
+ DBG(("%s: glyph+(%d,%d) to mask (%d, %d)x(%d, %d)\n",
+ __FUNCTION__,
+ dx,dy,
+ x - g->info.x,
+ y - g->info.y,
+ g->info.width,
+ g->info.height));
+
+ pixman_image_composite(PictOpAdd,
+ glyph_image,
+ NULL,
+ mask_image,
+ dx, dy,
+ 0, 0,
+ x - g->info.x,
+ y - g->info.y,
+ g->info.width,
+ g->info.height);
+ } else {
+ DBG(("%s: glyph+(%d, %d) to dst (%d, %d)x(%d, %d)\n",
+ __FUNCTION__,
+ dx, dy,
+ x - g->info.x,
+ y - g->info.y,
+ g->info.width,
+ g->info.height));
+
+ pixman_image_composite(op,
+ src_image,
+ glyph_image,
+ dst_image,
+ src_x + (x - g->info.x),
+ src_y + (y - g->info.y),
+ dx, dy,
+ x - g->info.x,
+ y - g->info.y,
+ g->info.width,
+ g->info.height);
+ }
+ free_pixman_pict(picture,glyph_image);
+
+next_glyph:
+ x += g->info.xOff;
+ y += g->info.yOff;
+ }
+ list++;
+ } while (--nlist);
+
+ if (mask_format) {
+ DBG(("%s: glyph mask composite src=(%d,%d) dst=(%d, %d)x(%d, %d)\n",
+ __FUNCTION__,
+ src_x + box.x1,
+ src_y + box.y1,
+ box.x1, box.y1,
+ box.x2-box.x1, box.y2-box.y1));
+ pixman_image_composite(op, src_image, mask_image, dst_image,
+ src_x + box.x1,
+ src_y + box.y1,
+ 0, 0,
+ box.x1, box.y1,
+ box.x2 - box.x1,
+ box.y2 - box.y1);
+ pixman_image_unref(mask_image);
+ }
+
+ free_pixman_pict(src, src_image);
+ free_pixman_pict(dst, dst_image);
+ RegionUninit(&region);
+}
+
+void
+sna_glyphs(CARD8 op,
+ PicturePtr src,
+ PicturePtr dst,
+ PictFormatPtr mask,
+ INT16 src_x, INT16 src_y,
+ int nlist, GlyphListPtr list, GlyphPtr *glyphs)
+{
+ struct sna *sna = to_sna_from_drawable(dst->pDrawable);
+ PictFormatPtr _mask;
+
+ DBG(("%s(op=%d, nlist=%d, src=(%d, %d))\n",
+ __FUNCTION__, op, nlist, src_x, src_y));
+
+ if (REGION_NUM_RECTS(dst->pCompositeClip) == 0)
+ return;
+
+ if (sna->kgem.wedged || !sna->have_render) {
+ DBG(("%s: no render (wedged=%d)\n",
+ __FUNCTION__, sna->kgem.wedged));
+ goto fallback;
+ }
+
+ if (too_small(sna, dst->pDrawable) && !picture_is_gpu(src)) {
+ DBG(("%s: fallback -- too small\n", __FUNCTION__));
+ goto fallback;
+ }
+
+ if (dst->alphaMap || src->alphaMap) {
+ DBG(("%s: fallback -- alpha maps\n", __FUNCTION__));
+ goto fallback;
+ }
+
+ /* XXX discard the mask for non-overlapping glyphs? */
+
+ if (!mask) {
+ if (glyphs_to_dst(sna, op,
+ src, dst,
+ src_x, src_y,
+ nlist, list, glyphs))
+ return;
+ }
+
+ _mask = mask;
+ if (!_mask)
+ _mask = glyphs_format(nlist, list, glyphs);
+ if (_mask) {
+ if (glyphs_via_mask(sna, op,
+ src, dst, _mask,
+ src_x, src_y,
+ nlist, list, glyphs))
+ return;
+ } else {
+ if (glyphs_to_dst_slow(sna, op,
+ src, dst,
+ src_x, src_y,
+ nlist, list, glyphs))
+ return;
+ }
+
+fallback:
+ glyphs_fallback(op, src, dst, mask, src_x, src_y, nlist, list, glyphs);
+}
+
+void
+sna_glyph_unrealize(ScreenPtr screen, GlyphPtr glyph)
+{
+ struct sna_glyph_cache *cache;
+ struct sna_glyph *priv;
+ struct sna *sna;
+
+ priv = glyph_get_private(glyph);
+ if (priv->atlas == NULL)
+ return;
+
+ sna = to_sna_from_screen(screen);
+ cache = &sna->render.glyph[priv->pos & 1];
+ assert(cache->glyphs[priv->pos >> 1] == priv);
+ cache->glyphs[priv->pos >> 1] = NULL;
+ priv->atlas = NULL;
+}
+
+void sna_glyphs_close(struct sna *sna)
+{
+ unrealize_glyph_caches(sna);
+}