/* * 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_CONFIG_H #include #endif #include "kdrive.h" #include "kaa.h" #ifdef RENDER #include "mipict.h" #define KAA_DEBUG_FALLBACKS 0 #if KAA_DEBUG_FALLBACKS static void kaaCompositeFallbackPictDesc(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 = kaaGetOffscreenPixmap(pict->pDrawable, &temp, &temp) ? 's' : 'm'; snprintf(size, 20, "%dx%d%s", pict->pDrawable->width, pict->pDrawable->height, pict->repeat ? " R" : ""); snprintf(string, n, "0x%lx:%c fmt %s (%s)", (long)pict, loc, format, size); } static void kaaPrintCompositeFallback(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; } kaaCompositeFallbackPictDesc(pSrc, srcdesc, 40); kaaCompositeFallbackPictDesc(pMask, maskdesc, 40); kaaCompositeFallbackPictDesc(pDst, dstdesc, 40); ErrorF("Composite fallback: op %s, \n" " src %s, \n" " mask %s, \n" " dst %s, \n", sop, srcdesc, maskdesc, dstdesc); } static void kaaPrintTrapezoidFallback(PicturePtr pDst) { char dstdesc[40]; kaaCompositeFallbackPictDesc(pDst, dstdesc, 40); ErrorF("Trapezoid fallback: dst %s, %c/%s\n", dstdesc, (pDst->polyMode == PolyModePrecise) ? 'p' : 'i', (pDst->polyEdge == PolyEdgeSharp) ? "a" : "aa"); } #endif static Bool kaaGetPixelFromRGBA(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 kaaGetRGBAFromPixel(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 kaaTryDriverSolidFill(PicturePtr pSrc, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { KaaScreenPriv (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; 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; if (pSrc->pDrawable->type == DRAWABLE_PIXMAP) kaaPixmapUseMemory ((PixmapPtr) pSrc->pDrawable); if (pDst->pDrawable->type == DRAWABLE_PIXMAP) kaaPixmapUseScreen ((PixmapPtr) pDst->pDrawable); pDstPix = kaaGetOffscreenPixmap (pDst->pDrawable, &dst_off_x, &dst_off_y); if (!pDstPix) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 0; } if (pSrc->pDrawable->type == DRAWABLE_WINDOW) pSrcPix = (*pSrc->pDrawable->pScreen->GetWindowPixmap)( (WindowPtr) (pSrc->pDrawable)); else pSrcPix = (PixmapPtr) (pSrc->pDrawable); /* If source is offscreen, we need to sync the accelerator * before accessing it. We'd prefer for it to be in memory. */ if (kaaPixmapIsOffscreen(pSrcPix)) { kaaWaitSync(pDst->pDrawable->pScreen); } pixel = *(CARD32 *)(pSrcPix->devPrivate.ptr); if (!kaaGetRGBAFromPixel(pixel, &red, &green, &blue, &alpha, pSrc->format)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return -1; } kaaGetPixelFromRGBA(&pixel, red, green, blue, alpha, pDst->format); if (!(*pKaaScr->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--) { (*pKaaScr->info->Solid) (pbox->x1 + dst_off_x, pbox->y1 + dst_off_y, pbox->x2 + dst_off_x, pbox->y2 + dst_off_y); pbox++; } (*pKaaScr->info->DoneSolid) (); kaaMarkSync (pDst->pDrawable->pScreen); kaaDrawableDirty (pDst->pDrawable); REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 1; } static int kaaTryDriverBlend(CARD8 op, PicturePtr pSrc, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { KaaScreenPriv (pDst->pDrawable->pScreen); RegionRec region; BoxPtr pbox; int nbox; int src_off_x, src_off_y, dst_off_x, dst_off_y; PixmapPtr pSrcPix, pDstPix; struct _Pixmap srcScratch; 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; if (pSrc->pDrawable->type == DRAWABLE_PIXMAP) kaaPixmapUseScreen ((PixmapPtr) pSrc->pDrawable); if (pDst->pDrawable->type == DRAWABLE_PIXMAP) kaaPixmapUseScreen ((PixmapPtr) pDst->pDrawable); pSrcPix = kaaGetOffscreenPixmap (pSrc->pDrawable, &src_off_x, &src_off_y); pDstPix = kaaGetOffscreenPixmap (pDst->pDrawable, &dst_off_x, &dst_off_y); if (!pDstPix) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 0; } if (!pSrcPix && pKaaScr->info->UploadToScratch) { if ((*pKaaScr->info->UploadToScratch) ((PixmapPtr) pSrc->pDrawable, &srcScratch)) pSrcPix = &srcScratch; } if (!pSrcPix) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 0; } if (!(*pKaaScr->info->PrepareBlend) (op, pSrc, pDst, pSrcPix, pDstPix)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return -1; } nbox = REGION_NUM_RECTS(®ion); pbox = REGION_RECTS(®ion); xSrc -= xDst; ySrc -= yDst; while (nbox--) { (*pKaaScr->info->Blend) (pbox->x1 + xSrc + src_off_x, pbox->y1 + ySrc + src_off_y, pbox->x1 + dst_off_x, pbox->y1 + dst_off_y, pbox->x2 - pbox->x1, pbox->y2 - pbox->y1); pbox++; } (*pKaaScr->info->DoneBlend) (); kaaMarkSync (pDst->pDrawable->pScreen); kaaDrawableDirty (pDst->pDrawable); REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 1; } static int kaaTryDriverComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { KaaScreenPriv (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; 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 (pKaaScr->info->CheckComposite && !(*pKaaScr->info->CheckComposite) (op, pSrc, pMask, pDst)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return -1; } if (pSrc->pDrawable->type == DRAWABLE_PIXMAP) kaaPixmapUseScreen ((PixmapPtr) pSrc->pDrawable); if (pMask && pMask->pDrawable->type == DRAWABLE_PIXMAP) kaaPixmapUseScreen ((PixmapPtr) pMask->pDrawable); if (pDst->pDrawable->type == DRAWABLE_PIXMAP) kaaPixmapUseScreen ((PixmapPtr) pDst->pDrawable); pSrcPix = kaaGetOffscreenPixmap (pSrc->pDrawable, &src_off_x, &src_off_y); if (pMask) pMaskPix = kaaGetOffscreenPixmap (pMask->pDrawable, &mask_off_x, &mask_off_y); pDstPix = kaaGetOffscreenPixmap (pDst->pDrawable, &dst_off_x, &dst_off_y); if (!pDstPix) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 0; } if (!pSrcPix && (!pMask || pMaskPix) && pKaaScr->info->UploadToScratch) { if (pSrc->pDrawable->type == DRAWABLE_WINDOW) pSrcPix = (*pSrc->pDrawable->pScreen->GetWindowPixmap) ( (WindowPtr) pSrc->pDrawable); else pSrcPix = (PixmapPtr) pSrc->pDrawable; if ((*pKaaScr->info->UploadToScratch) (pSrcPix, &scratch)) pSrcPix = &scratch; } else if (pSrcPix && pMask && !pMaskPix && pKaaScr->info->UploadToScratch) { if (pMask->pDrawable->type == DRAWABLE_WINDOW) pMaskPix = (*pMask->pDrawable->pScreen->GetWindowPixmap) ( (WindowPtr) pMask->pDrawable); else pMaskPix = (PixmapPtr) pMask->pDrawable; if ((*pKaaScr->info->UploadToScratch) (pMaskPix, &scratch)) pMaskPix = &scratch; } if (!pSrcPix || (pMask && !pMaskPix)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 0; } if (!(*pKaaScr->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--) { (*pKaaScr->info->Composite) (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++; } (*pKaaScr->info->DoneComposite) (); kaaMarkSync (pDst->pDrawable->pScreen); kaaDrawableDirty (pDst->pDrawable); REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 1; } void kaaComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { KdScreenPriv (pDst->pDrawable->pScreen); KaaScreenPriv (pDst->pDrawable->pScreen); int ret = -1; if (!pMask && pSrc->pDrawable) { if (op == PictOpSrc) { if (pScreenPriv->enabled && pSrc->pDrawable && pSrc->pDrawable->width == 1 && pSrc->pDrawable->height == 1 && pSrc->repeat) { ret = kaaTryDriverSolidFill(pSrc, pDst, xSrc, ySrc, xDst, yDst, width, height); if (ret == 1) return; } else if (!pSrc->repeat && !pSrc->transform && pSrc->format == pDst->format) { 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)) return; kaaCopyNtoN (pSrc->pDrawable, pDst->pDrawable, 0, REGION_RECTS(®ion), REGION_NUM_RECTS(®ion), xSrc - xDst, ySrc - yDst, FALSE, FALSE, 0, 0); return; } } if (pScreenPriv->enabled && pKaaScr->info->PrepareBlend && !pSrc->alphaMap && !pDst->alphaMap) { ret = kaaTryDriverBlend(op, pSrc, pDst, xSrc, ySrc, xDst, yDst, width, height); if (ret == 1) return; } } if (pSrc->pDrawable && (!pMask || pMask->pDrawable) && pScreenPriv->enabled && pKaaScr->info->PrepareComposite && !pSrc->alphaMap && (!pMask || !pMask->alphaMap) && !pDst->alphaMap) { ret = kaaTryDriverComposite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); if (ret == 1) return; } if (ret != 0) { /* failure to accelerate was not due to pixmaps being in the wrong * locations. */ if (pSrc->pDrawable->type == DRAWABLE_PIXMAP) kaaPixmapUseMemory ((PixmapPtr) pSrc->pDrawable); if (pMask && pMask->pDrawable->type == DRAWABLE_PIXMAP) kaaPixmapUseMemory ((PixmapPtr) pMask->pDrawable); if (pDst->pDrawable->type == DRAWABLE_PIXMAP) kaaPixmapUseMemory ((PixmapPtr) pDst->pDrawable); } #if KAA_DEBUG_FALLBACKS kaaPrintCompositeFallback (op, pSrc, pMask, pDst); #endif KdCheckComposite (op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); } #endif static xFixed miLineFixedX (xLineFixed *l, xFixed y, Bool ceil) { xFixed dx = l->p2.x - l->p1.x; xFixed_32_32 ex = (xFixed_32_32) (y - l->p1.y) * dx; xFixed dy = l->p2.y - l->p1.y; if (ceil) ex += (dy - 1); return l->p1.x + (xFixed) (ex / dy); } /* Need to decide just how much to trim, to maintain translation independence * when converted to floating point. */ #define XFIXED_TO_FLOAT(x) (((float)((x) & 0xffffff00)) / 65536.0) /* This is just to allow us to work on the hardware side of the problem while * waiting for cairo to get a new tesselator. We may not be able to support * RasterizeTrapezoid at all due to the abutting edges requirement, but it might * be technically legal if we widened the trap by some epsilon, so that alpha * values at abutting edges were a little too big and capped at one, rather than * a little too small and looked bad. */ void kaaRasterizeTrapezoid(PicturePtr pDst, xTrapezoid *trap, int xoff, int yoff) { KdScreenPriv (pDst->pDrawable->pScreen); KaaScreenPriv (pDst->pDrawable->pScreen); KaaTrapezoid ktrap; PixmapPtr pPix; xFixed x1, x2; if (!pScreenPriv->enabled || !pKaaScr->info->PrepareTrapezoids || pDst->pDrawable->type != DRAWABLE_PIXMAP || pDst->alphaMap || pDst->format != PICT_a8) { KdCheckRasterizeTrapezoid (pDst, trap, xoff, yoff); #if KAA_DEBUG_FALLBACKS kaaPrintTrapezoidFallback (pDst); #endif return; } pPix = (PixmapPtr)pDst->pDrawable; kaaPixmapUseScreen (pPix); if (!kaaPixmapIsOffscreen (pPix) || !(*pKaaScr->info->PrepareTrapezoids) (pDst, pPix)) { #if KAA_DEBUG_FALLBACKS kaaPrintTrapezoidFallback (pDst); #endif KdCheckRasterizeTrapezoid (pDst, trap, xoff, yoff); return; } ktrap.ty = XFIXED_TO_FLOAT(trap->top) + yoff; x1 = miLineFixedX (&trap->left, trap->top, FALSE); x2 = miLineFixedX (&trap->right, trap->top, TRUE); ktrap.tl = XFIXED_TO_FLOAT(x1) + xoff; ktrap.tr = XFIXED_TO_FLOAT(x2) + xoff; ktrap.by = XFIXED_TO_FLOAT(trap->bottom) + yoff; x1 = miLineFixedX (&trap->left, trap->bottom, FALSE); x2 = miLineFixedX (&trap->right, trap->bottom, TRUE); ktrap.bl = XFIXED_TO_FLOAT(x1) + xoff; ktrap.br = XFIXED_TO_FLOAT(x2) + xoff; (*pKaaScr->info->Trapezoids) (&ktrap, 1); (*pKaaScr->info->DoneTrapezoids) (); } void kaaInitTrapOffsets(int grid_order, float *x_offsets, float *y_offsets, float x_offset, float y_offset) { int i = 0; float x, y, x_count, y_count; x_count = (1 << (grid_order / 2)) + 1; y_count = (1 << (grid_order / 2)) - 1; x_offset += 1.0 / x_count / 2.0; y_offset += 1.0 / y_count / 2.0; for (x = 0; x < x_count; x++) { for (y = 0; y < y_count; y++) { x_offsets[i] = x / x_count + x_offset; y_offsets[i] = y / y_count + y_offset; i++; } } }