/* * Copyright © 2001 Keith Packard * * Partly based on code that is Copyright © The XFree86 Project 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 Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD 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. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include "exa_priv.h" #ifdef RENDER #include "mipict.h" #if DEBUG_TRACE_FALL static void exaCompositeFallbackPictDesc(PicturePtr pict, char *string, int n) { char format[20]; char size[20]; char loc; int temp; if (!pict) { snprintf(string, n, "None"); return; } switch (pict->format) { case PICT_a8r8g8b8: snprintf(format, 20, "ARGB8888"); break; case PICT_r5g6b5: snprintf(format, 20, "RGB565 "); break; case PICT_x1r5g5b5: snprintf(format, 20, "RGB555 "); break; case PICT_a8: snprintf(format, 20, "A8 "); break; case PICT_a1: snprintf(format, 20, "A1 "); break; default: snprintf(format, 20, "0x%x", (int)pict->format); break; } loc = exaGetOffscreenPixmap(pict->pDrawable, &temp, &temp) ? 's' : 'm'; snprintf(size, 20, "%dx%d%s", pict->pDrawable->width, pict->pDrawable->height, pict->repeat ? " R" : ""); snprintf(string, n, "%p:%c fmt %s (%s)", pict->pDrawable, loc, format, size); } static void exaPrintCompositeFallback(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst) { char sop[20]; char srcdesc[40], maskdesc[40], dstdesc[40]; switch(op) { case PictOpSrc: sprintf(sop, "Src"); break; case PictOpOver: sprintf(sop, "Over"); break; default: sprintf(sop, "0x%x", (int)op); break; } exaCompositeFallbackPictDesc(pSrc, srcdesc, 40); exaCompositeFallbackPictDesc(pMask, maskdesc, 40); exaCompositeFallbackPictDesc(pDst, dstdesc, 40); ErrorF("Composite fallback: op %s, \n" " src %s, \n" " mask %s, \n" " dst %s, \n", sop, srcdesc, maskdesc, dstdesc); } #endif /* DEBUG_TRACE_FALL */ static Bool exaOpReadsDestination (CARD8 op) { /* FALSE (does not read destination) is the list of ops in the protocol * document with "0" in the "Fb" column and no "Ab" in the "Fa" column. * That's just Clear and Src. ReduceCompositeOp() will already have * converted con/disjoint clear/src to Clear or Src. */ switch (op) { case PictOpClear: case PictOpSrc: return FALSE; default: return TRUE; } } static Bool exaGetPixelFromRGBA(CARD32 *pixel, CARD16 red, CARD16 green, CARD16 blue, CARD16 alpha, CARD32 format) { int rbits, bbits, gbits, abits; int rshift, bshift, gshift, ashift; *pixel = 0; if (!PICT_FORMAT_COLOR(format)) return FALSE; rbits = PICT_FORMAT_R(format); gbits = PICT_FORMAT_G(format); bbits = PICT_FORMAT_B(format); abits = PICT_FORMAT_A(format); if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) { bshift = 0; gshift = bbits; rshift = gshift + gbits; ashift = rshift + rbits; } else { /* PICT_TYPE_ABGR */ rshift = 0; gshift = rbits; bshift = gshift + gbits; ashift = bshift + bbits; } *pixel |= ( blue >> (16 - bbits)) << bshift; *pixel |= ( red >> (16 - rbits)) << rshift; *pixel |= (green >> (16 - gbits)) << gshift; *pixel |= (alpha >> (16 - abits)) << ashift; return TRUE; } static Bool exaGetRGBAFromPixel(CARD32 pixel, CARD16 *red, CARD16 *green, CARD16 *blue, CARD16 *alpha, CARD32 format) { int rbits, bbits, gbits, abits; int rshift, bshift, gshift, ashift; if (!PICT_FORMAT_COLOR(format)) return FALSE; rbits = PICT_FORMAT_R(format); gbits = PICT_FORMAT_G(format); bbits = PICT_FORMAT_B(format); abits = PICT_FORMAT_A(format); if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) { bshift = 0; gshift = bbits; rshift = gshift + gbits; ashift = rshift + rbits; } else { /* PICT_TYPE_ABGR */ rshift = 0; gshift = rbits; bshift = gshift + gbits; ashift = bshift + bbits; } *red = ((pixel >> rshift ) & ((1 << rbits) - 1)) << (16 - rbits); while (rbits < 16) { *red |= *red >> rbits; rbits <<= 1; } *green = ((pixel >> gshift ) & ((1 << gbits) - 1)) << (16 - gbits); while (gbits < 16) { *green |= *green >> gbits; gbits <<= 1; } *blue = ((pixel >> bshift ) & ((1 << bbits) - 1)) << (16 - bbits); while (bbits < 16) { *blue |= *blue >> bbits; bbits <<= 1; } if (abits) { *alpha = ((pixel >> ashift ) & ((1 << abits) - 1)) << (16 - abits); while (abits < 16) { *alpha |= *alpha >> abits; abits <<= 1; } } else *alpha = 0xffff; return TRUE; } static int exaTryDriverSolidFill(PicturePtr pSrc, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { ExaScreenPriv (pDst->pDrawable->pScreen); RegionRec region; BoxPtr pbox; int nbox; int dst_off_x, dst_off_y; PixmapPtr pSrcPix, pDstPix; CARD32 pixel; CARD16 red, green, blue, alpha; ExaMigrationRec pixmaps[1]; xDst += pDst->pDrawable->x; yDst += pDst->pDrawable->y; xSrc += pSrc->pDrawable->x; ySrc += pSrc->pDrawable->y; if (!miComputeCompositeRegion (®ion, pSrc, NULL, pDst, xSrc, ySrc, 0, 0, xDst, yDst, width, height)) return 1; pSrcPix = exaGetDrawablePixmap (pSrc->pDrawable); pixel = exaGetPixmapFirstPixel (pSrcPix); pixmaps[0].as_dst = TRUE; pixmaps[0].as_src = FALSE; pixmaps[0].pPix = exaGetDrawablePixmap (pDst->pDrawable); exaDoMigration(pixmaps, 1, TRUE); pDstPix = exaGetOffscreenPixmap (pDst->pDrawable, &dst_off_x, &dst_off_y); if (!pDstPix) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 0; } if (!exaGetRGBAFromPixel(pixel, &red, &green, &blue, &alpha, pSrc->format)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return -1; } if (!exaGetPixelFromRGBA(&pixel, red, green, blue, alpha, pDst->format)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return -1; } if (!(*pExaScr->info->PrepareSolid) (pDstPix, GXcopy, 0xffffffff, pixel)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return -1; } nbox = REGION_NUM_RECTS(®ion); pbox = REGION_RECTS(®ion); while (nbox--) { (*pExaScr->info->Solid) (pDstPix, pbox->x1 + dst_off_x, pbox->y1 + dst_off_y, pbox->x2 + dst_off_x, pbox->y2 + dst_off_y); pbox++; } (*pExaScr->info->DoneSolid) (pDstPix); exaMarkSync(pDst->pDrawable->pScreen); REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 1; } static int exaTryDriverComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { ExaScreenPriv (pDst->pDrawable->pScreen); RegionRec region; BoxPtr pbox; int nbox; int src_off_x, src_off_y, mask_off_x, mask_off_y, dst_off_x, dst_off_y; PixmapPtr pSrcPix, pMaskPix = NULL, pDstPix; struct _Pixmap scratch; ExaMigrationRec pixmaps[3]; pSrcPix = exaGetDrawablePixmap(pSrc->pDrawable); pDstPix = exaGetDrawablePixmap(pDst->pDrawable); if (pMask) pMaskPix = exaGetDrawablePixmap(pMask->pDrawable); /* Bail if we might exceed coord limits by rendering from/to these. We * should really be making some scratch pixmaps with offsets and coords * adjusted to deal with this, but it hasn't been done yet. */ if (pSrcPix->drawable.width > pExaScr->info->maxX || pSrcPix->drawable.height > pExaScr->info->maxY || pDstPix->drawable.width > pExaScr->info->maxX || pDstPix->drawable.height > pExaScr->info->maxY || (pMask && (pMaskPix->drawable.width > pExaScr->info->maxX || pMaskPix->drawable.height > pExaScr->info->maxY))) { return -1; } xDst += pDst->pDrawable->x; yDst += pDst->pDrawable->y; if (pMask) { xMask += pMask->pDrawable->x; yMask += pMask->pDrawable->y; } xSrc += pSrc->pDrawable->x; ySrc += pSrc->pDrawable->y; if (!miComputeCompositeRegion (®ion, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height)) return 1; if (pExaScr->info->CheckComposite && !(*pExaScr->info->CheckComposite) (op, pSrc, pMask, pDst)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return -1; } pixmaps[0].as_dst = TRUE; pixmaps[0].as_src = exaOpReadsDestination(op); pixmaps[0].pPix = exaGetDrawablePixmap (pDst->pDrawable); pixmaps[1].as_dst = FALSE; pixmaps[1].as_src = TRUE; pixmaps[1].pPix = exaGetDrawablePixmap (pSrc->pDrawable); if (pMask) { pixmaps[2].as_dst = FALSE; pixmaps[2].as_src = TRUE; pixmaps[2].pPix = exaGetDrawablePixmap (pMask->pDrawable); exaDoMigration(pixmaps, 3, TRUE); } else { exaDoMigration(pixmaps, 2, TRUE); } pSrcPix = exaGetOffscreenPixmap (pSrc->pDrawable, &src_off_x, &src_off_y); if (pMask) pMaskPix = exaGetOffscreenPixmap (pMask->pDrawable, &mask_off_x, &mask_off_y); pDstPix = exaGetOffscreenPixmap (pDst->pDrawable, &dst_off_x, &dst_off_y); if (!pDstPix) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 0; } if (!pSrcPix && (!pMask || pMaskPix) && pExaScr->info->UploadToScratch) { pSrcPix = exaGetDrawablePixmap (pSrc->pDrawable); if ((*pExaScr->info->UploadToScratch) (pSrcPix, &scratch)) pSrcPix = &scratch; } else if (pSrcPix && pMask && !pMaskPix && pExaScr->info->UploadToScratch) { pMaskPix = exaGetDrawablePixmap (pMask->pDrawable); if ((*pExaScr->info->UploadToScratch) (pMaskPix, &scratch)) pMaskPix = &scratch; } if (!pSrcPix || (pMask && !pMaskPix)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 0; } if (!(*pExaScr->info->PrepareComposite) (op, pSrc, pMask, pDst, pSrcPix, pMaskPix, pDstPix)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return -1; } nbox = REGION_NUM_RECTS(®ion); pbox = REGION_RECTS(®ion); xMask -= xDst; yMask -= yDst; xSrc -= xDst; ySrc -= yDst; while (nbox--) { (*pExaScr->info->Composite) (pDstPix, pbox->x1 + xSrc + src_off_x, pbox->y1 + ySrc + src_off_y, pbox->x1 + xMask + mask_off_x, pbox->y1 + yMask + mask_off_y, pbox->x1 + dst_off_x, pbox->y1 + dst_off_y, pbox->x2 - pbox->x1, pbox->y2 - pbox->y1); pbox++; } (*pExaScr->info->DoneComposite) (pDstPix); exaMarkSync(pDst->pDrawable->pScreen); REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 1; } /** * exaTryMagicTwoPassCompositeHelper implements PictOpOver using two passes of * simpler operations PictOpOutReverse and PictOpAdd. Mainly used for component * alpha and limited 1-tmu cards. * * From http://anholt.livejournal.com/32058.html: * * The trouble is that component-alpha rendering requires two different sources * for blending: one for the source value to the blender, which is the * per-channel multiplication of source and mask, and one for the source alpha * for multiplying with the destination channels, which is the multiplication * of the source channels by the mask alpha. So the equation for Over is: * * dst.A = src.A * mask.A + (1 - (src.A * mask.A)) * dst.A * dst.R = src.R * mask.R + (1 - (src.A * mask.R)) * dst.R * dst.G = src.G * mask.G + (1 - (src.A * mask.G)) * dst.G * dst.B = src.B * mask.B + (1 - (src.A * mask.B)) * dst.B * * But we can do some simpler operations, right? How about PictOpOutReverse, * which has a source factor of 0 and dest factor of (1 - source alpha). We * can get the source alpha value (srca.X = src.A * mask.X) out of the texture * blenders pretty easily. So we can do a component-alpha OutReverse, which * gets us: * * dst.A = 0 + (1 - (src.A * mask.A)) * dst.A * dst.R = 0 + (1 - (src.A * mask.R)) * dst.R * dst.G = 0 + (1 - (src.A * mask.G)) * dst.G * dst.B = 0 + (1 - (src.A * mask.B)) * dst.B * * OK. And if an op doesn't use the source alpha value for the destination * factor, then we can do the channel multiplication in the texture blenders * to get the source value, and ignore the source alpha that we wouldn't use. * We've supported this in the Radeon driver for a long time. An example would * be PictOpAdd, which does: * * dst.A = src.A * mask.A + dst.A * dst.R = src.R * mask.R + dst.R * dst.G = src.G * mask.G + dst.G * dst.B = src.B * mask.B + dst.B * * Hey, this looks good! If we do a PictOpOutReverse and then a PictOpAdd right * after it, we get: * * dst.A = src.A * mask.A + ((1 - (src.A * mask.A)) * dst.A) * dst.R = src.R * mask.R + ((1 - (src.A * mask.R)) * dst.R) * dst.G = src.G * mask.G + ((1 - (src.A * mask.G)) * dst.G) * dst.B = src.B * mask.B + ((1 - (src.A * mask.B)) * dst.B) */ static int exaTryMagicTwoPassCompositeHelper(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { ExaScreenPriv (pDst->pDrawable->pScreen); DrawablePtr pDstDraw = pDst->pDrawable; PixmapPtr pDstPixmap = exaGetDrawablePixmap(pDstDraw); int xoff, yoff; assert(op == PictOpOver); if (pExaScr->info->CheckComposite && (!(*pExaScr->info->CheckComposite)(PictOpOutReverse, pSrc, pMask, pDst) || !(*pExaScr->info->CheckComposite)(PictOpAdd, pSrc, pMask, pDst))) { return -1; } /* Now, we think we should be able to accelerate this operation. First, * composite the destination to be the destination times the source alpha * factors. */ exaComposite(PictOpOutReverse, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); exaGetDrawableDeltas(pDstDraw, pDstPixmap, &xoff, &yoff); xoff += pDstDraw->x; yoff += pDstDraw->y; exaPixmapDirty(pDstPixmap, xDst + xoff, yDst + yoff, xDst + xoff + width, yDst + yoff + height); /* Then, add in the source value times the destination alpha factors (1.0). */ exaComposite(PictOpAdd, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); return 1; } void exaComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { ExaScreenPriv (pDst->pDrawable->pScreen); int ret = -1; Bool saveSrcRepeat = pSrc->repeat; Bool saveMaskRepeat = pMask ? pMask->repeat : 0; ExaMigrationRec pixmaps[3]; int npixmaps = 1; PixmapPtr pSrcPixmap = NULL; pixmaps[0].as_dst = TRUE; pixmaps[0].as_src = exaOpReadsDestination(op); pixmaps[0].pPix = exaGetDrawablePixmap (pDst->pDrawable); if (pSrc->pDrawable) { pSrcPixmap = exaGetDrawablePixmap (pSrc->pDrawable); pixmaps[npixmaps].as_dst = FALSE; pixmaps[npixmaps].as_src = TRUE; pixmaps[npixmaps].pPix = pSrcPixmap; npixmaps++; } if (pMask && pMask->pDrawable) { pixmaps[npixmaps].as_dst = FALSE; pixmaps[npixmaps].as_src = TRUE; pixmaps[npixmaps].pPix = exaGetDrawablePixmap (pMask->pDrawable); npixmaps++; } /* We currently don't support acceleration of gradients, or other pictures * with a NULL pDrawable. */ if (pExaScr->swappedOut || pSrc->pDrawable == NULL || (pMask != NULL && pMask->pDrawable == NULL)) { goto fallback; } /* Remove repeat in source if useless */ if (pSrc->repeat && !pSrc->transform && xSrc >= 0 && (xSrc + width) <= pSrc->pDrawable->width && ySrc >= 0 && (ySrc + height) <= pSrc->pDrawable->height) pSrc->repeat = 0; if (!pMask) { if ((op == PictOpSrc && ((pSrc->format == pDst->format) || (pSrc->format==PICT_a8r8g8b8 && pDst->format==PICT_x8r8g8b8) || (pSrc->format==PICT_a8b8g8r8 && pDst->format==PICT_x8b8g8r8))) || (op == PictOpOver && !pSrc->alphaMap && !pDst->alphaMap && pSrc->format == pDst->format && (pSrc->format==PICT_x8r8g8b8 || pSrc->format==PICT_x8b8g8r8))) { if (pSrc->pDrawable->width == 1 && pSrc->pDrawable->height == 1 && pSrc->repeat) { ret = exaTryDriverSolidFill(pSrc, pDst, xSrc, ySrc, xDst, yDst, width, height); if (ret == 1) goto done; } else if (pSrcPixmap && !pSrc->repeat && !pSrc->transform) { RegionRec region; xDst += pDst->pDrawable->x; yDst += pDst->pDrawable->y; xSrc += pSrc->pDrawable->x; ySrc += pSrc->pDrawable->y; if (!miComputeCompositeRegion (®ion, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height)) goto done; exaCopyNtoN (pSrc->pDrawable, pDst->pDrawable, NULL, REGION_RECTS(®ion), REGION_NUM_RECTS(®ion), xSrc - xDst, ySrc - yDst, FALSE, FALSE, 0, NULL); REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); goto done; } else if (pSrcPixmap && !pSrc->transform && pSrc->repeatType == RepeatNormal) { RegionRec region; DDXPointRec srcOrg; /* Let's see if the driver can do the repeat in one go */ if (pExaScr->info->PrepareComposite && !pSrc->alphaMap && !pDst->alphaMap) { ret = exaTryDriverComposite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); if (ret == 1) goto done; } /* Now see if we can use exaFillRegionTiled() */ xDst += pDst->pDrawable->x; yDst += pDst->pDrawable->y; xSrc += pSrc->pDrawable->x; ySrc += pSrc->pDrawable->y; if (!miComputeCompositeRegion (®ion, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height)) goto done; srcOrg.x = (xSrc - xDst) % pSrcPixmap->drawable.width; srcOrg.y = (ySrc - yDst) % pSrcPixmap->drawable.height; ret = exaFillRegionTiled(pDst->pDrawable, ®ion, pSrcPixmap, &srcOrg, FB_ALLONES, GXcopy); REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); if (ret) goto done; } } } /* Remove repeat in mask if useless */ if (pMask && pMask->repeat && !pMask->transform && xMask >= 0 && (xMask + width) <= pMask->pDrawable->width && yMask >= 0 && (yMask + height) <= pMask->pDrawable->height) pMask->repeat = 0; if (pExaScr->info->PrepareComposite && (!pSrc->repeat || pSrc->repeatType == RepeatNormal) && (!pMask || !pMask->repeat || pMask->repeatType == RepeatNormal) && !pSrc->alphaMap && (!pMask || !pMask->alphaMap) && !pDst->alphaMap) { Bool isSrcSolid; ret = exaTryDriverComposite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); if (ret == 1) goto done; /* For generic masks and solid src pictures, mach64 can do Over in two * passes, similar to the component-alpha case. */ isSrcSolid = pSrc->pDrawable->width == 1 && pSrc->pDrawable->height == 1 && pSrc->repeat; /* If we couldn't do the Composite in a single pass, and it was a * component-alpha Over, see if we can do it in two passes with * an OutReverse and then an Add. */ if (ret == -1 && op == PictOpOver && pMask && (pMask->componentAlpha || isSrcSolid)) { ret = exaTryMagicTwoPassCompositeHelper(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); if (ret == 1) goto done; } } fallback: #if DEBUG_TRACE_FALL exaPrintCompositeFallback (op, pSrc, pMask, pDst); #endif exaDoMigration(pixmaps, npixmaps, FALSE); ExaCheckComposite (op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); done: pSrc->repeat = saveSrcRepeat; if (pMask) pMask->repeat = saveMaskRepeat; } #endif /** * Same as miCreateAlphaPicture, except it uses ExaCheckPolyFillRect instead * of PolyFillRect to initialize the pixmap after creating it, to prevent * the pixmap from being migrated. * * See the comments about exaTrapezoids. */ static PicturePtr exaCreateAlphaPicture (ScreenPtr pScreen, PicturePtr pDst, PictFormatPtr pPictFormat, CARD16 width, CARD16 height) { PixmapPtr pPixmap; PicturePtr pPicture; GCPtr pGC; int error; xRectangle rect; if (width > 32767 || height > 32767) return 0; if (!pPictFormat) { if (pDst->polyEdge == PolyEdgeSharp) pPictFormat = PictureMatchFormat (pScreen, 1, PICT_a1); else pPictFormat = PictureMatchFormat (pScreen, 8, PICT_a8); if (!pPictFormat) return 0; } pPixmap = (*pScreen->CreatePixmap) (pScreen, width, height, pPictFormat->depth); if (!pPixmap) return 0; pGC = GetScratchGC (pPixmap->drawable.depth, pScreen); if (!pGC) { (*pScreen->DestroyPixmap) (pPixmap); return 0; } ValidateGC (&pPixmap->drawable, pGC); rect.x = 0; rect.y = 0; rect.width = width; rect.height = height; ExaCheckPolyFillRect (&pPixmap->drawable, pGC, 1, &rect); exaPixmapDirty (pPixmap, 0, 0, width, height); FreeScratchGC (pGC); pPicture = CreatePicture (0, &pPixmap->drawable, pPictFormat, 0, 0, serverClient, &error); (*pScreen->DestroyPixmap) (pPixmap); return pPicture; } /** * exaTrapezoids is essentially a copy of miTrapezoids that uses * exaCreateAlphaPicture instead of miCreateAlphaPicture. * * The problem with miCreateAlphaPicture is that it calls PolyFillRect * to initialize the contents after creating the pixmap, which * causes the pixmap to be moved in for acceleration. The subsequent * call to RasterizeTrapezoid won't be accelerated however, which * forces the pixmap to be moved out again. * * exaCreateAlphaPicture avoids this roundtrip by using ExaCheckPolyFillRect * to initialize the contents. */ void exaTrapezoids (CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int ntrap, xTrapezoid *traps) { ScreenPtr pScreen = pDst->pDrawable->pScreen; PictureScreenPtr ps = GetPictureScreen(pScreen); /* * Check for solid alpha add */ if (op == PictOpAdd && miIsSolidAlpha (pSrc)) { for (; ntrap; ntrap--, traps++) (*ps->RasterizeTrapezoid) (pDst, traps, 0, 0); } else if (maskFormat) { PicturePtr pPicture; BoxRec bounds; INT16 xDst, yDst; INT16 xRel, yRel; xDst = traps[0].left.p1.x >> 16; yDst = traps[0].left.p1.y >> 16; miTrapezoidBounds (ntrap, traps, &bounds); if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2) return; pPicture = exaCreateAlphaPicture (pScreen, pDst, maskFormat, bounds.x2 - bounds.x1, bounds.y2 - bounds.y1); if (!pPicture) return; for (; ntrap; ntrap--, traps++) (*ps->RasterizeTrapezoid) (pPicture, traps, -bounds.x1, -bounds.y1); xRel = bounds.x1 + xSrc - xDst; yRel = bounds.y1 + ySrc - yDst; CompositePicture (op, pSrc, pPicture, pDst, xRel, yRel, 0, 0, bounds.x1, bounds.y1, bounds.x2 - bounds.x1, bounds.y2 - bounds.y1); FreePicture (pPicture, 0); } else { if (pDst->polyEdge == PolyEdgeSharp) maskFormat = PictureMatchFormat (pScreen, 1, PICT_a1); else maskFormat = PictureMatchFormat (pScreen, 8, PICT_a8); for (; ntrap; ntrap--, traps++) exaTrapezoids (op, pSrc, pDst, maskFormat, xSrc, ySrc, 1, traps); } } #define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0) /** * exaRasterizeTrapezoid is just a wrapper around the software implementation. * * The trapezoid specification is basically too hard to be done in hardware (at * the very least, without programmability), so we just do the appropriate * Prepare/FinishAccess for it before using fbtrap.c. */ void exaRasterizeTrapezoid (PicturePtr pPicture, xTrapezoid *trap, int x_off, int y_off) { DrawablePtr pDraw = pPicture->pDrawable; ExaMigrationRec pixmaps[1]; int xoff, yoff; pixmaps[0].as_dst = TRUE; pixmaps[0].as_src = TRUE; pixmaps[0].pPix = exaGetDrawablePixmap (pDraw); exaDoMigration(pixmaps, 1, FALSE); exaPrepareAccess(pDraw, EXA_PREPARE_DEST); fbRasterizeTrapezoid(pPicture, trap, x_off, y_off); exaGetDrawableDeltas(pDraw, pixmaps[0].pPix, &xoff, &yoff); exaPixmapDirty(pixmaps[0].pPix, pDraw->x + xoff, pDraw->y + yoff, pDraw->x + xoff + pDraw->width, pDraw->y + yoff + pDraw->height); exaFinishAccess(pDraw, EXA_PREPARE_DEST); } /** * exaAddTriangles does migration and syncing before dumping down to the * software implementation. */ void exaAddTriangles (PicturePtr pPicture, INT16 x_off, INT16 y_off, int ntri, xTriangle *tris) { DrawablePtr pDraw = pPicture->pDrawable; ExaMigrationRec pixmaps[1]; int xoff, yoff; pixmaps[0].as_dst = TRUE; pixmaps[0].as_src = TRUE; pixmaps[0].pPix = exaGetDrawablePixmap (pDraw); exaDoMigration(pixmaps, 1, FALSE); exaPrepareAccess(pDraw, EXA_PREPARE_DEST); fbAddTriangles(pPicture, x_off, y_off, ntri, tris); exaGetDrawableDeltas(pDraw, pixmaps[0].pPix, &xoff, &yoff); exaPixmapDirty(pixmaps[0].pPix, pDraw->x + xoff, pDraw->y + yoff, pDraw->x + xoff + pDraw->width, pDraw->y + yoff + pDraw->height); exaFinishAccess(pDraw, EXA_PREPARE_DEST); } /** * Returns TRUE if the glyphs in the lists intersect. Only checks based on * bounding box, which appears to be good enough to catch most cases at least. */ static Bool exaGlyphsIntersect(int nlist, GlyphListPtr list, GlyphPtr *glyphs) { int x1, x2, y1, y2; int n; GlyphPtr glyph; int x, y; BoxRec extents; Bool first = TRUE; x = 0; y = 0; while (nlist--) { x += list->xOff; y += list->yOff; n = list->len; list++; while (n--) { 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 { if (x1 < extents.x2 && x2 > extents.x1 && y1 < extents.y2 && y2 > extents.y1) { return TRUE; } 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 FALSE; } /* exaGlyphs is a slight variation on miGlyphs, to support acceleration. The * issue is that miGlyphs' use of ModifyPixmapHeader makes it impossible to * migrate these pixmaps. So, instead we create a pixmap at the beginning of * the loop and upload each glyph into the pixmap before compositing. */ void exaGlyphs (CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr *glyphs) { ExaScreenPriv (pDst->pDrawable->pScreen); PixmapPtr pPixmap = NULL; PicturePtr pPicture; PixmapPtr pMaskPixmap = NULL; PixmapPtr pDstPixmap = exaGetDrawablePixmap(pDst->pDrawable); PicturePtr pMask; ScreenPtr pScreen = pDst->pDrawable->pScreen; int width = 0, height = 0; int x, y, x1, y1, xoff, yoff; int xDst = list->xOff, yDst = list->yOff; int n; int error; BoxRec extents; CARD32 component_alpha; /* If we have a mask format but it's the same as all the glyphs and * the glyphs don't intersect, we can avoid accumulating the glyphs in the * temporary picture. */ if (maskFormat != NULL) { Bool sameFormat = TRUE; int i; for (i = 0; i < nlist; i++) { if (maskFormat->format != list[i].format->format) { sameFormat = FALSE; break; } } if (sameFormat) { if (!exaGlyphsIntersect(nlist, list, glyphs)) { maskFormat = NULL; } } } /* If the driver doesn't support accelerated composite, there's no point in * going to this extra work. Assume that any driver that supports Composite * will be able to support component alpha using the two-pass helper. */ if (!pExaScr->info->PrepareComposite) { miGlyphs(op, pSrc, pDst, maskFormat, xSrc, ySrc, nlist, list, glyphs); return; } if (maskFormat) { GCPtr pGC; xRectangle rect; miGlyphExtents (nlist, list, glyphs, &extents); extents.x1 = max(extents.x1, 0); extents.y1 = max(extents.y1, 0); extents.x2 = min(extents.x2, pDst->pDrawable->width); extents.y2 = min(extents.y2, pDst->pDrawable->height); if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1) return; width = extents.x2 - extents.x1; height = extents.y2 - extents.y1; pMaskPixmap = (*pScreen->CreatePixmap) (pScreen, width, height, maskFormat->depth); if (!pMaskPixmap) return; component_alpha = NeedsComponent(maskFormat->format); pMask = CreatePicture (0, &pMaskPixmap->drawable, maskFormat, CPComponentAlpha, &component_alpha, serverClient, &error); if (!pMask) { (*pScreen->DestroyPixmap) (pMaskPixmap); return; } ValidatePicture(pMask); pGC = GetScratchGC (pMaskPixmap->drawable.depth, pScreen); ValidateGC (&pMaskPixmap->drawable, pGC); rect.x = 0; rect.y = 0; rect.width = width; rect.height = height; (*pGC->ops->PolyFillRect) (&pMaskPixmap->drawable, pGC, 1, &rect); exaPixmapDirty(pMaskPixmap, 0, 0, width, height); FreeScratchGC (pGC); x = -extents.x1; y = -extents.y1; } else { pMask = pDst; x = 0; y = 0; } exaGetDrawableDeltas(pDst->pDrawable, pDstPixmap, &xoff, &yoff); while (nlist--) { GCPtr pGC = NULL; int maxwidth = 0, maxheight = 0, i; ExaMigrationRec pixmaps[1]; PixmapPtr pScratchPixmap = NULL; x += list->xOff; y += list->yOff; n = list->len; for (i = 0; i < n; i++) { if (glyphs[i]->info.width > maxwidth) maxwidth = glyphs[i]->info.width; if (glyphs[i]->info.height > maxheight) maxheight = glyphs[i]->info.height; } if (maxwidth == 0 || maxheight == 0) { while (n--) { GlyphPtr glyph; glyph = *glyphs++; x += glyph->info.xOff; y += glyph->info.yOff; } list++; continue; } /* Create the (real) temporary pixmap to store the current glyph in */ pPixmap = (*pScreen->CreatePixmap) (pScreen, maxwidth, maxheight, list->format->depth); if (!pPixmap) return; /* Create a temporary picture to wrap the temporary pixmap, so it can be * used as a source for Composite. */ component_alpha = NeedsComponent(list->format->format); pPicture = CreatePicture (0, &pPixmap->drawable, list->format, CPComponentAlpha, &component_alpha, serverClient, &error); if (!pPicture) { (*pScreen->DestroyPixmap) (pPixmap); return; } ValidatePicture(pPicture); /* Give the temporary pixmap an initial kick towards the screen, so * it'll stick there. */ pixmaps[0].as_dst = TRUE; pixmaps[0].as_src = TRUE; pixmaps[0].pPix = pPixmap; exaDoMigration (pixmaps, 1, pExaScr->info->PrepareComposite != NULL); while (n--) { GlyphPtr glyph = *glyphs++; pointer glyphdata = (pointer) (glyph + 1); DrawablePtr pCmpDrw = (maskFormat ? pMask : pDst)->pDrawable; x1 = x - glyph->info.x; y1 = y - glyph->info.y; if (x1 >= pCmpDrw->width || y1 >= pCmpDrw->height || (x1 + glyph->info.width) <= 0 || (y1 + glyph->info.height) <= 0) goto nextglyph; (*pScreen->ModifyPixmapHeader) (pScratchPixmap, glyph->info.width, glyph->info.height, 0, 0, -1, glyphdata); /* Copy the glyph data into the proper pixmap instead of a fake. * First we try to use UploadToScreen, if we can, then we fall back * to a plain exaCopyArea in case of failure. */ if (pExaScr->info->UploadToScreen && exaPixmapIsOffscreen(pPixmap) && (*pExaScr->info->UploadToScreen) (pPixmap, 0, 0, glyph->info.width, glyph->info.height, glyphdata, PixmapBytePad(glyph->info.width, list->format->depth))) { exaMarkSync (pScreen); } else { /* Set up the scratch pixmap/GC for doing a CopyArea. */ if (pScratchPixmap == NULL) { /* Get a scratch pixmap to wrap the original glyph data */ pScratchPixmap = GetScratchPixmapHeader (pScreen, glyph->info.width, glyph->info.height, list->format->depth, list->format->depth, -1, glyphdata); if (!pScratchPixmap) { FreePicture(pPicture, 0); (*pScreen->DestroyPixmap) (pPixmap); return; } /* Get a scratch GC with which to copy the glyph data from * scratch to temporary */ pGC = GetScratchGC (list->format->depth, pScreen); ValidateGC (&pPixmap->drawable, pGC); } else { (*pScreen->ModifyPixmapHeader) (pScratchPixmap, glyph->info.width, glyph->info.height, 0, 0, -1, glyphdata); pScratchPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; } exaCopyArea (&pScratchPixmap->drawable, &pPixmap->drawable, pGC, 0, 0, glyph->info.width, glyph->info.height, 0, 0); } exaPixmapDirty (pPixmap, 0, 0, glyph->info.width, glyph->info.height); if (maskFormat) { exaComposite (PictOpAdd, pPicture, NULL, pMask, 0, 0, 0, 0, x1, y1, glyph->info.width, glyph->info.height); exaPixmapDirty(pMaskPixmap, x1, y1, x1 + glyph->info.width, y1 + glyph->info.height); } else { exaComposite (op, pSrc, pPicture, pDst, xSrc + x1 - xDst, ySrc + y1 - yDst, 0, 0, x1, y1, glyph->info.width, glyph->info.height); x1 += pDst->pDrawable->x + xoff; y1 += pDst->pDrawable->y + yoff; exaPixmapDirty(pDstPixmap, x1, y1, x1 + glyph->info.width, y1 + glyph->info.height); } nextglyph: x += glyph->info.xOff; y += glyph->info.yOff; } list++; if (pGC != NULL) FreeScratchGC (pGC); FreePicture ((pointer) pPicture, 0); (*pScreen->DestroyPixmap) (pPixmap); if (pScratchPixmap != NULL) FreeScratchPixmapHeader (pScratchPixmap); } if (maskFormat) { x = extents.x1; y = extents.y1; exaComposite (op, pSrc, pMask, pDst, xSrc + x - xDst, ySrc + y - yDst, 0, 0, x, y, width, height); FreePicture ((pointer) pMask, (XID) 0); (*pScreen->DestroyPixmap) (pMaskPixmap); } }