1096 lines
27 KiB
C
1096 lines
27 KiB
C
|
|
||
|
#ifdef HAVE_XORG_CONFIG_H
|
||
|
#include <xorg-config.h>
|
||
|
#endif
|
||
|
|
||
|
#include "misc.h"
|
||
|
#include "xf86.h"
|
||
|
#include "xf86_OSproc.h"
|
||
|
|
||
|
#include <X11/X.h>
|
||
|
#include "scrnintstr.h"
|
||
|
#include "pixmapstr.h"
|
||
|
#include "xf86str.h"
|
||
|
#include "xaa.h"
|
||
|
#include "xaalocal.h"
|
||
|
|
||
|
|
||
|
static void XAARenderSolidRects(GCPtr, int, BoxPtr, int, int);
|
||
|
static void XAARenderColor8x8Rects(GCPtr, int, BoxPtr, int, int);
|
||
|
static void XAARenderMono8x8Rects(GCPtr, int, BoxPtr, int, int);
|
||
|
static void XAARenderColorExpandRects(GCPtr, int, BoxPtr, int, int);
|
||
|
static void XAARenderCacheExpandRects(GCPtr, int, BoxPtr, int, int);
|
||
|
static void XAARenderCacheBltRects(GCPtr, int, BoxPtr, int, int);
|
||
|
static void XAARenderImageWriteRects(GCPtr, int, BoxPtr, int, int);
|
||
|
static void XAARenderPixmapCopyRects(GCPtr, int, BoxPtr, int, int);
|
||
|
|
||
|
void
|
||
|
XAAPolyFillRect(
|
||
|
DrawablePtr pDraw,
|
||
|
GCPtr pGC,
|
||
|
int nrectFill, /* number of rectangles to fill */
|
||
|
xRectangle *prectInit /* Pointer to first rectangle to fill */
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
|
||
|
int xorg = pDraw->x;
|
||
|
int yorg = pDraw->y;
|
||
|
int type = 0;
|
||
|
ClipAndRenderRectsFunc function;
|
||
|
|
||
|
if((nrectFill <= 0) || !pGC->planemask)
|
||
|
return;
|
||
|
|
||
|
if(!REGION_NUM_RECTS(pGC->pCompositeClip))
|
||
|
return;
|
||
|
|
||
|
switch(pGC->fillStyle) {
|
||
|
case FillSolid:
|
||
|
type = DO_SOLID;
|
||
|
break;
|
||
|
case FillStippled:
|
||
|
type = (*infoRec->StippledFillChooser)(pGC);
|
||
|
break;
|
||
|
case FillOpaqueStippled:
|
||
|
if((pGC->fgPixel == pGC->bgPixel) && infoRec->FillSolidRects &&
|
||
|
CHECK_PLANEMASK(pGC,infoRec->FillSolidRectsFlags) &&
|
||
|
CHECK_ROP(pGC,infoRec->FillSolidRectsFlags) &&
|
||
|
CHECK_ROPSRC(pGC,infoRec->FillSolidRectsFlags) &&
|
||
|
CHECK_FG(pGC,infoRec->FillSolidRectsFlags))
|
||
|
type = DO_SOLID;
|
||
|
else
|
||
|
type = (*infoRec->OpaqueStippledFillChooser)(pGC);
|
||
|
break;
|
||
|
case FillTiled:
|
||
|
type = (*infoRec->TiledFillChooser)(pGC);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch(type) {
|
||
|
case DO_SOLID:
|
||
|
function = XAARenderSolidRects;
|
||
|
break;
|
||
|
case DO_COLOR_8x8:
|
||
|
function = XAARenderColor8x8Rects;
|
||
|
break;
|
||
|
case DO_MONO_8x8:
|
||
|
function = XAARenderMono8x8Rects;
|
||
|
break;
|
||
|
case DO_CACHE_BLT:
|
||
|
function = XAARenderCacheBltRects;
|
||
|
break;
|
||
|
case DO_COLOR_EXPAND:
|
||
|
function = XAARenderColorExpandRects;
|
||
|
break;
|
||
|
case DO_CACHE_EXPAND:
|
||
|
function = XAARenderCacheExpandRects;
|
||
|
break;
|
||
|
case DO_IMAGE_WRITE:
|
||
|
function = XAARenderImageWriteRects;
|
||
|
break;
|
||
|
case DO_PIXMAP_COPY:
|
||
|
function = XAARenderPixmapCopyRects;
|
||
|
break;
|
||
|
default:
|
||
|
(*XAAFallbackOps.PolyFillRect)(pDraw, pGC, nrectFill, prectInit);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(xorg | yorg) {
|
||
|
int n = nrectFill;
|
||
|
xRectangle *prect = prectInit;
|
||
|
|
||
|
while(n--) {
|
||
|
prect->x += xorg;
|
||
|
prect->y += yorg;
|
||
|
prect++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
XAAClipAndRenderRects(pGC, function, nrectFill, prectInit, xorg, yorg);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*********************\
|
||
|
| Solid Rects |
|
||
|
\*********************/
|
||
|
|
||
|
static void
|
||
|
XAARenderSolidRects(
|
||
|
GCPtr pGC,
|
||
|
int nboxes,
|
||
|
BoxPtr pClipBoxes,
|
||
|
int xorg, int yorg
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
|
||
|
|
||
|
(*infoRec->FillSolidRects) (infoRec->pScrn,
|
||
|
pGC->fgPixel, pGC->alu, pGC->planemask, nboxes, pClipBoxes);
|
||
|
}
|
||
|
|
||
|
|
||
|
/************************\
|
||
|
| Mono 8x8 Rects |
|
||
|
\************************/
|
||
|
|
||
|
static void
|
||
|
XAARenderMono8x8Rects(
|
||
|
GCPtr pGC,
|
||
|
int nboxes,
|
||
|
BoxPtr pClipBoxes,
|
||
|
int xorg, int yorg
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
|
||
|
XAAPixmapPtr pPriv;
|
||
|
int fg, bg;
|
||
|
|
||
|
switch(pGC->fillStyle) {
|
||
|
case FillStippled:
|
||
|
pPriv = XAA_GET_PIXMAP_PRIVATE(pGC->stipple);
|
||
|
fg = pGC->fgPixel; bg = -1;
|
||
|
break;
|
||
|
case FillOpaqueStippled:
|
||
|
pPriv = XAA_GET_PIXMAP_PRIVATE(pGC->stipple);
|
||
|
fg = pGC->fgPixel; bg = pGC->bgPixel;
|
||
|
break;
|
||
|
case FillTiled:
|
||
|
pPriv = XAA_GET_PIXMAP_PRIVATE(pGC->tile.pixmap);
|
||
|
fg = pPriv->fg; bg = pPriv->bg;
|
||
|
break;
|
||
|
default: /* Muffle compiler */
|
||
|
pPriv = NULL; /* Kaboom */
|
||
|
fg = -1; bg = -1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
(*infoRec->FillMono8x8PatternRects) (infoRec->pScrn,
|
||
|
fg, bg, pGC->alu, pGC->planemask,
|
||
|
nboxes, pClipBoxes, pPriv->pattern0, pPriv->pattern1,
|
||
|
(xorg + pGC->patOrg.x), (yorg + pGC->patOrg.y));
|
||
|
}
|
||
|
|
||
|
/*************************\
|
||
|
| Color 8x8 Rects |
|
||
|
\*************************/
|
||
|
|
||
|
static void
|
||
|
XAARenderColor8x8Rects(
|
||
|
GCPtr pGC,
|
||
|
int nboxes,
|
||
|
BoxPtr pClipBoxes,
|
||
|
int xorg, int yorg
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
|
||
|
XAACacheInfoPtr pCache;
|
||
|
PixmapPtr pPix;
|
||
|
int fg, bg;
|
||
|
|
||
|
switch(pGC->fillStyle) {
|
||
|
case FillStippled:
|
||
|
pPix = pGC->stipple;
|
||
|
fg = pGC->fgPixel; bg = -1;
|
||
|
break;
|
||
|
case FillOpaqueStippled:
|
||
|
pPix = pGC->stipple;
|
||
|
fg = pGC->fgPixel; bg = pGC->bgPixel;
|
||
|
break;
|
||
|
case FillTiled:
|
||
|
pPix = pGC->tile.pixmap;
|
||
|
fg = -1; bg = -1;
|
||
|
break;
|
||
|
default: /* Muffle compiler */
|
||
|
pPix = NULL;
|
||
|
fg = -1; bg = -1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pCache = (*infoRec->CacheColor8x8Pattern)(infoRec->pScrn, pPix, fg, bg);
|
||
|
(*infoRec->FillColor8x8PatternRects) (infoRec->pScrn,
|
||
|
pGC->alu, pGC->planemask, nboxes, pClipBoxes,
|
||
|
(xorg + pGC->patOrg.x), (yorg + pGC->patOrg.y), pCache);
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************\
|
||
|
| Color Expand Rects |
|
||
|
\****************************/
|
||
|
|
||
|
static void
|
||
|
XAARenderColorExpandRects(
|
||
|
GCPtr pGC,
|
||
|
int nboxes,
|
||
|
BoxPtr pClipBoxes,
|
||
|
int xorg, int yorg
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
|
||
|
int fg, bg;
|
||
|
|
||
|
switch(pGC->fillStyle) {
|
||
|
case FillStippled:
|
||
|
fg = pGC->fgPixel; bg = -1;
|
||
|
break;
|
||
|
case FillOpaqueStippled:
|
||
|
fg = pGC->fgPixel; bg = pGC->bgPixel;
|
||
|
break;
|
||
|
default: /* Muffle compiler */
|
||
|
fg = -1; bg = -1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
(*infoRec->FillColorExpandRects) (infoRec->pScrn, fg, bg,
|
||
|
pGC->alu, pGC->planemask, nboxes, pClipBoxes,
|
||
|
(xorg + pGC->patOrg.x), (yorg + pGC->patOrg.y),
|
||
|
pGC->stipple);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*************************\
|
||
|
| Cache Blt Rects |
|
||
|
\*************************/
|
||
|
|
||
|
static void
|
||
|
XAARenderCacheBltRects(
|
||
|
GCPtr pGC,
|
||
|
int nboxes,
|
||
|
BoxPtr pClipBoxes,
|
||
|
int xorg, int yorg
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
|
||
|
XAACacheInfoPtr pCache;
|
||
|
|
||
|
switch(pGC->fillStyle) {
|
||
|
case FillStippled:
|
||
|
pCache = (*infoRec->CacheStipple)(infoRec->pScrn, pGC->stipple,
|
||
|
pGC->fgPixel, -1);
|
||
|
break;
|
||
|
case FillOpaqueStippled:
|
||
|
pCache = (*infoRec->CacheStipple)(infoRec->pScrn, pGC->stipple,
|
||
|
pGC->fgPixel, pGC->bgPixel);
|
||
|
break;
|
||
|
case FillTiled:
|
||
|
pCache = (*infoRec->CacheTile)(infoRec->pScrn, pGC->tile.pixmap);
|
||
|
break;
|
||
|
default: /* Muffle compiler */
|
||
|
pCache = NULL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
(*infoRec->FillCacheBltRects) (infoRec->pScrn, pGC->alu,
|
||
|
pGC->planemask, nboxes, pClipBoxes,
|
||
|
(xorg + pGC->patOrg.x), (yorg + pGC->patOrg.y), pCache);
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************\
|
||
|
| Cache Expand Rects |
|
||
|
\****************************/
|
||
|
|
||
|
static void
|
||
|
XAARenderCacheExpandRects(
|
||
|
GCPtr pGC,
|
||
|
int nboxes,
|
||
|
BoxPtr pClipBoxes,
|
||
|
int xorg, int yorg
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
|
||
|
int fg, bg;
|
||
|
|
||
|
switch(pGC->fillStyle) {
|
||
|
case FillStippled:
|
||
|
fg = pGC->fgPixel; bg = -1;
|
||
|
break;
|
||
|
case FillOpaqueStippled:
|
||
|
fg = pGC->fgPixel; bg = pGC->bgPixel;
|
||
|
break;
|
||
|
default: /* Muffle compiler */
|
||
|
fg = -1; bg = -1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
(*infoRec->FillCacheExpandRects) (infoRec->pScrn, fg, bg,
|
||
|
pGC->alu, pGC->planemask, nboxes, pClipBoxes,
|
||
|
(xorg + pGC->patOrg.x), (yorg + pGC->patOrg.y),
|
||
|
pGC->stipple);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/***************************\
|
||
|
| Image Write Rects |
|
||
|
\***************************/
|
||
|
|
||
|
static void
|
||
|
XAARenderImageWriteRects(
|
||
|
GCPtr pGC,
|
||
|
int nboxes,
|
||
|
BoxPtr pClipBoxes,
|
||
|
int xorg, int yorg
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
|
||
|
|
||
|
(*infoRec->FillImageWriteRects) (infoRec->pScrn, pGC->alu,
|
||
|
pGC->planemask, nboxes, pClipBoxes,
|
||
|
(xorg + pGC->patOrg.x), (yorg + pGC->patOrg.y),
|
||
|
pGC->tile.pixmap);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/***************************\
|
||
|
| Pixmap Copy Rects |
|
||
|
\***************************/
|
||
|
|
||
|
static void
|
||
|
XAARenderPixmapCopyRects(
|
||
|
GCPtr pGC,
|
||
|
int nboxes,
|
||
|
BoxPtr pClipBoxes,
|
||
|
int xorg, int yorg
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
|
||
|
XAACacheInfoPtr pCache = &(infoRec->ScratchCacheInfoRec);
|
||
|
XAAPixmapPtr pPriv = XAA_GET_PIXMAP_PRIVATE(pGC->tile.pixmap);
|
||
|
|
||
|
pCache->x = pPriv->offscreenArea->box.x1;
|
||
|
pCache->y = pPriv->offscreenArea->box.y1;
|
||
|
pCache->w = pCache->orig_w =
|
||
|
pPriv->offscreenArea->box.x2 - pCache->x;
|
||
|
pCache->h = pCache->orig_h =
|
||
|
pPriv->offscreenArea->box.y2 - pCache->y;
|
||
|
pCache->trans_color = -1;
|
||
|
|
||
|
(*infoRec->FillCacheBltRects) (infoRec->pScrn, pGC->alu,
|
||
|
pGC->planemask, nboxes, pClipBoxes,
|
||
|
(xorg + pGC->patOrg.x), (yorg + pGC->patOrg.y),
|
||
|
pCache);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/************\
|
||
|
| Solid |
|
||
|
\************/
|
||
|
|
||
|
void
|
||
|
XAAFillSolidRects(
|
||
|
ScrnInfoPtr pScrn,
|
||
|
int fg, int rop,
|
||
|
unsigned int planemask,
|
||
|
int nBox, /* number of rectangles to fill */
|
||
|
BoxPtr pBox /* Pointer to first rectangle to fill */
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
|
||
|
|
||
|
(*infoRec->SetupForSolidFill)(pScrn, fg, rop, planemask);
|
||
|
while(nBox--) {
|
||
|
(*infoRec->SubsequentSolidFillRect)(pScrn, pBox->x1, pBox->y1,
|
||
|
pBox->x2 - pBox->x1, pBox->y2 - pBox->y1);
|
||
|
pBox++;
|
||
|
}
|
||
|
SET_SYNC_FLAG(infoRec);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*********************\
|
||
|
| 8x8 Mono Patterns |
|
||
|
\*********************/
|
||
|
|
||
|
|
||
|
void
|
||
|
XAAFillMono8x8PatternRectsScreenOrigin(
|
||
|
ScrnInfoPtr pScrn,
|
||
|
int fg, int bg, int rop,
|
||
|
unsigned int planemask,
|
||
|
int nBox,
|
||
|
BoxPtr pBox,
|
||
|
int pattern0, int pattern1,
|
||
|
int xorigin, int yorigin
|
||
|
)
|
||
|
{
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
|
||
|
int patx = pattern0, paty = pattern1;
|
||
|
int xorg = (-xorigin) & 0x07;
|
||
|
int yorg = (-yorigin) & 0x07;
|
||
|
|
||
|
|
||
|
if(infoRec->Mono8x8PatternFillFlags & HARDWARE_PATTERN_PROGRAMMED_BITS) {
|
||
|
if(!(infoRec->Mono8x8PatternFillFlags &
|
||
|
HARDWARE_PATTERN_PROGRAMMED_ORIGIN)){
|
||
|
XAARotateMonoPattern(&patx, &paty, xorg, yorg,
|
||
|
(infoRec->Mono8x8PatternFillFlags &
|
||
|
BIT_ORDER_IN_BYTE_MSBFIRST));
|
||
|
xorg = patx; yorg = paty;
|
||
|
}
|
||
|
} else {
|
||
|
XAACacheInfoPtr pCache =
|
||
|
(*infoRec->CacheMono8x8Pattern)(pScrn, pattern0, pattern1);
|
||
|
patx = pCache->x; paty = pCache->y;
|
||
|
if(!(infoRec->Mono8x8PatternFillFlags &
|
||
|
HARDWARE_PATTERN_PROGRAMMED_ORIGIN)){
|
||
|
int slot = (yorg << 3) + xorg;
|
||
|
patx += pCache->offsets[slot].x;
|
||
|
paty += pCache->offsets[slot].y;
|
||
|
xorg = patx; yorg = paty;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
(*infoRec->SetupForMono8x8PatternFill)(pScrn, patx, paty,
|
||
|
fg, bg, rop, planemask);
|
||
|
|
||
|
while(nBox--) {
|
||
|
(*infoRec->SubsequentMono8x8PatternFillRect)(pScrn,
|
||
|
xorg, yorg, pBox->x1, pBox->y1,
|
||
|
pBox->x2 - pBox->x1, pBox->y2 - pBox->y1);
|
||
|
pBox++;
|
||
|
}
|
||
|
SET_SYNC_FLAG(infoRec);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
XAAFillMono8x8PatternRects(
|
||
|
ScrnInfoPtr pScrn,
|
||
|
int fg, int bg, int rop,
|
||
|
unsigned int planemask,
|
||
|
int nBox,
|
||
|
BoxPtr pBox,
|
||
|
int pattern0, int pattern1,
|
||
|
int xorigin, int yorigin
|
||
|
)
|
||
|
{
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
|
||
|
int patx = pattern0, paty = pattern1;
|
||
|
int xorg, yorg;
|
||
|
XAACacheInfoPtr pCache = NULL;
|
||
|
|
||
|
|
||
|
if(!(infoRec->Mono8x8PatternFillFlags & HARDWARE_PATTERN_PROGRAMMED_BITS)){
|
||
|
pCache = (*infoRec->CacheMono8x8Pattern)(pScrn, pattern0, pattern1);
|
||
|
patx = pCache->x; paty = pCache->y;
|
||
|
}
|
||
|
|
||
|
|
||
|
(*infoRec->SetupForMono8x8PatternFill)(pScrn, patx, paty,
|
||
|
fg, bg, rop, planemask);
|
||
|
|
||
|
|
||
|
while(nBox--) {
|
||
|
xorg = (pBox->x1 - xorigin) & 0x07;
|
||
|
yorg = (pBox->y1 - yorigin) & 0x07;
|
||
|
|
||
|
if(!(infoRec->Mono8x8PatternFillFlags &
|
||
|
HARDWARE_PATTERN_PROGRAMMED_ORIGIN)){
|
||
|
if(infoRec->Mono8x8PatternFillFlags &
|
||
|
HARDWARE_PATTERN_PROGRAMMED_BITS) {
|
||
|
patx = pattern0; paty = pattern1;
|
||
|
XAARotateMonoPattern(&patx, &paty, xorg, yorg,
|
||
|
(infoRec->Mono8x8PatternFillFlags &
|
||
|
BIT_ORDER_IN_BYTE_MSBFIRST));
|
||
|
xorg = patx; yorg = paty;
|
||
|
} else {
|
||
|
int slot = (yorg << 3) + xorg;
|
||
|
xorg = patx + pCache->offsets[slot].x;
|
||
|
yorg = paty + pCache->offsets[slot].y;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
(*infoRec->SubsequentMono8x8PatternFillRect)(pScrn,
|
||
|
xorg, yorg, pBox->x1, pBox->y1,
|
||
|
pBox->x2 - pBox->x1, pBox->y2 - pBox->y1);
|
||
|
pBox++;
|
||
|
}
|
||
|
|
||
|
SET_SYNC_FLAG(infoRec);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**********************\
|
||
|
| 8x8 Color Patterns |
|
||
|
\**********************/
|
||
|
|
||
|
|
||
|
void
|
||
|
XAAFillColor8x8PatternRectsScreenOrigin(
|
||
|
ScrnInfoPtr pScrn,
|
||
|
int rop,
|
||
|
unsigned int planemask,
|
||
|
int nBox,
|
||
|
BoxPtr pBox,
|
||
|
int xorigin, int yorigin,
|
||
|
XAACacheInfoPtr pCache
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
|
||
|
int patx = pCache->x, paty = pCache->y;
|
||
|
int xorg = (-xorigin) & 0x07;
|
||
|
int yorg = (-yorigin) & 0x07;
|
||
|
|
||
|
if(!(infoRec->Color8x8PatternFillFlags &
|
||
|
HARDWARE_PATTERN_PROGRAMMED_ORIGIN)){
|
||
|
int slot = (yorg << 3) + xorg;
|
||
|
paty += pCache->offsets[slot].y;
|
||
|
patx += pCache->offsets[slot].x;
|
||
|
xorg = patx; yorg = paty;
|
||
|
}
|
||
|
|
||
|
(*infoRec->SetupForColor8x8PatternFill)(pScrn, patx, paty,
|
||
|
rop, planemask, pCache->trans_color);
|
||
|
|
||
|
while(nBox--) {
|
||
|
(*infoRec->SubsequentColor8x8PatternFillRect)(pScrn,
|
||
|
xorg, yorg, pBox->x1, pBox->y1,
|
||
|
pBox->x2 - pBox->x1, pBox->y2 - pBox->y1);
|
||
|
pBox++;
|
||
|
}
|
||
|
SET_SYNC_FLAG(infoRec);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
XAAFillColor8x8PatternRects(
|
||
|
ScrnInfoPtr pScrn,
|
||
|
int rop,
|
||
|
unsigned int planemask,
|
||
|
int nBox,
|
||
|
BoxPtr pBox,
|
||
|
int xorigin, int yorigin,
|
||
|
XAACacheInfoPtr pCache
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
|
||
|
int xorg, yorg;
|
||
|
|
||
|
(*infoRec->SetupForColor8x8PatternFill)(pScrn, pCache->x, pCache->y,
|
||
|
rop, planemask, pCache->trans_color);
|
||
|
|
||
|
while(nBox--) {
|
||
|
xorg = (pBox->x1 - xorigin) & 0x07;
|
||
|
yorg = (pBox->y1 - yorigin) & 0x07;
|
||
|
|
||
|
if(!(infoRec->Color8x8PatternFillFlags &
|
||
|
HARDWARE_PATTERN_PROGRAMMED_ORIGIN)){
|
||
|
int slot = (yorg << 3) + xorg;
|
||
|
yorg = pCache->y + pCache->offsets[slot].y;
|
||
|
xorg = pCache->x + pCache->offsets[slot].x;
|
||
|
}
|
||
|
|
||
|
(*infoRec->SubsequentColor8x8PatternFillRect)(pScrn,
|
||
|
xorg, yorg, pBox->x1, pBox->y1,
|
||
|
pBox->x2 - pBox->x1, pBox->y2 - pBox->y1);
|
||
|
pBox++;
|
||
|
}
|
||
|
|
||
|
SET_SYNC_FLAG(infoRec);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************\
|
||
|
| Cache Blits |
|
||
|
\***************/
|
||
|
|
||
|
void
|
||
|
XAAFillCacheBltRects(
|
||
|
ScrnInfoPtr pScrn,
|
||
|
int rop,
|
||
|
unsigned int planemask,
|
||
|
int nBox,
|
||
|
BoxPtr pBox,
|
||
|
int xorg, int yorg,
|
||
|
XAACacheInfoPtr pCache
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
|
||
|
int x, y, phaseY, phaseX, skipleft, height, width, w, blit_w, blit_h;
|
||
|
|
||
|
(*infoRec->SetupForScreenToScreenCopy)(pScrn, 1, 1, rop, planemask,
|
||
|
pCache->trans_color);
|
||
|
|
||
|
while(nBox--) {
|
||
|
y = pBox->y1;
|
||
|
phaseY = (y - yorg) % pCache->orig_h;
|
||
|
if(phaseY < 0) phaseY += pCache->orig_h;
|
||
|
phaseX = (pBox->x1 - xorg) % pCache->orig_w;
|
||
|
if(phaseX < 0) phaseX += pCache->orig_w;
|
||
|
height = pBox->y2 - y;
|
||
|
width = pBox->x2 - pBox->x1;
|
||
|
|
||
|
#if 0
|
||
|
if (rop == GXcopy) {
|
||
|
while(1) {
|
||
|
w = width; skipleft = phaseX; x = pBox->x1;
|
||
|
blit_h = pCache->h - phaseY;
|
||
|
if(blit_h > height) blit_h = height;
|
||
|
|
||
|
while(1) {
|
||
|
blit_w = pCache->w - skipleft;
|
||
|
if(blit_w > w) blit_w = w;
|
||
|
(*infoRec->SubsequentScreenToScreenCopy)(pScrn,
|
||
|
pCache->x + skipleft, pCache->y + phaseY,
|
||
|
x, y, blit_w, blit_h);
|
||
|
w -= blit_w;
|
||
|
if(!w) break;
|
||
|
x += blit_w;
|
||
|
skipleft = (skipleft + blit_w) % pCache->orig_w;
|
||
|
if(blit_w >= pCache->orig_w) break;
|
||
|
}
|
||
|
|
||
|
/* Expand horizontally */
|
||
|
if (w) {
|
||
|
skipleft -= phaseX;
|
||
|
if (skipleft < 0) skipleft += pCache->orig_w;
|
||
|
blit_w = x - pBox->x1 - skipleft;
|
||
|
while(w) {
|
||
|
if (blit_w > w) blit_w = w;
|
||
|
(*infoRec->SubsequentScreenToScreenCopy)(pScrn,
|
||
|
pBox->x1 + skipleft, y, x, y, blit_w, blit_h);
|
||
|
w -= blit_w;
|
||
|
x += blit_w;
|
||
|
blit_w <<= 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
height -= blit_h;
|
||
|
if(!height) break;
|
||
|
y += blit_h;
|
||
|
phaseY = (phaseY + blit_h) % pCache->orig_h;
|
||
|
if(blit_h >= pCache->orig_h) break;
|
||
|
}
|
||
|
|
||
|
/* Expand vertically */
|
||
|
if (height) {
|
||
|
blit_w = pBox->x2 - pBox->x1;
|
||
|
phaseY -= (pBox->y1 - yorg) % pCache->orig_h;
|
||
|
if (phaseY < 0) phaseY += pCache->orig_h;
|
||
|
blit_h = y - pBox->y1 - phaseY;
|
||
|
while(height) {
|
||
|
if (blit_h > height) blit_h = height;
|
||
|
(*infoRec->SubsequentScreenToScreenCopy)(pScrn, pBox->x1,
|
||
|
pBox->y1 + phaseY, pBox->x1, y, blit_w, blit_h);
|
||
|
height -= blit_h;
|
||
|
y += blit_h;
|
||
|
blit_h <<= 1;
|
||
|
}
|
||
|
}
|
||
|
} else
|
||
|
#endif
|
||
|
{
|
||
|
while(1) {
|
||
|
w = width; skipleft = phaseX; x = pBox->x1;
|
||
|
blit_h = pCache->h - phaseY;
|
||
|
if(blit_h > height) blit_h = height;
|
||
|
|
||
|
while(1) {
|
||
|
blit_w = pCache->w - skipleft;
|
||
|
if(blit_w > w) blit_w = w;
|
||
|
(*infoRec->SubsequentScreenToScreenCopy)(pScrn,
|
||
|
pCache->x + skipleft, pCache->y + phaseY,
|
||
|
x, y, blit_w, blit_h);
|
||
|
w -= blit_w;
|
||
|
if(!w) break;
|
||
|
x += blit_w;
|
||
|
skipleft = (skipleft + blit_w) % pCache->orig_w;
|
||
|
}
|
||
|
height -= blit_h;
|
||
|
if(!height) break;
|
||
|
y += blit_h;
|
||
|
phaseY = (phaseY + blit_h) % pCache->orig_h;
|
||
|
}
|
||
|
}
|
||
|
pBox++;
|
||
|
}
|
||
|
|
||
|
SET_SYNC_FLAG(infoRec);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*******************\
|
||
|
| Cache Expansion |
|
||
|
\*******************/
|
||
|
|
||
|
|
||
|
|
||
|
void
|
||
|
XAAFillCacheExpandRects(
|
||
|
ScrnInfoPtr pScrn,
|
||
|
int fg, int bg, int rop,
|
||
|
unsigned int planemask,
|
||
|
int nBox,
|
||
|
BoxPtr pBox,
|
||
|
int xorg, int yorg,
|
||
|
PixmapPtr pPix
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
|
||
|
int x, y, phaseY, phaseX, skipleft, height, width, w, blit_w, blit_h;
|
||
|
int cacheWidth;
|
||
|
XAACacheInfoPtr pCache;
|
||
|
|
||
|
pCache = (*infoRec->CacheMonoStipple)(pScrn, pPix);
|
||
|
|
||
|
cacheWidth = (pCache->w * pScrn->bitsPerPixel) /
|
||
|
infoRec->CacheColorExpandDensity;
|
||
|
|
||
|
(*infoRec->SetupForScreenToScreenColorExpandFill)(pScrn, fg, bg, rop,
|
||
|
planemask);
|
||
|
|
||
|
while(nBox--) {
|
||
|
y = pBox->y1;
|
||
|
phaseY = (y - yorg) % pCache->orig_h;
|
||
|
if(phaseY < 0) phaseY += pCache->orig_h;
|
||
|
phaseX = (pBox->x1 - xorg) % pCache->orig_w;
|
||
|
if(phaseX < 0) phaseX += pCache->orig_w;
|
||
|
height = pBox->y2 - y;
|
||
|
width = pBox->x2 - pBox->x1;
|
||
|
|
||
|
while(1) {
|
||
|
w = width; skipleft = phaseX; x = pBox->x1;
|
||
|
blit_h = pCache->h - phaseY;
|
||
|
if(blit_h > height) blit_h = height;
|
||
|
|
||
|
while(1) {
|
||
|
blit_w = cacheWidth - skipleft;
|
||
|
if(blit_w > w) blit_w = w;
|
||
|
(*infoRec->SubsequentScreenToScreenColorExpandFill)(
|
||
|
pScrn, x, y, blit_w, blit_h,
|
||
|
pCache->x, pCache->y + phaseY, skipleft);
|
||
|
w -= blit_w;
|
||
|
if(!w) break;
|
||
|
x += blit_w;
|
||
|
skipleft = (skipleft + blit_w) % pCache->orig_w;
|
||
|
}
|
||
|
height -= blit_h;
|
||
|
if(!height) break;
|
||
|
y += blit_h;
|
||
|
phaseY = (phaseY + blit_h) % pCache->orig_h;
|
||
|
}
|
||
|
pBox++;
|
||
|
}
|
||
|
|
||
|
SET_SYNC_FLAG(infoRec);
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************\
|
||
|
| Image Writes |
|
||
|
\******************/
|
||
|
|
||
|
|
||
|
|
||
|
/* This requires all LEFT_EDGE clipping. You get too many problems
|
||
|
with reading past the edge of the pattern otherwise */
|
||
|
|
||
|
static void
|
||
|
WriteColumn(
|
||
|
ScrnInfoPtr pScrn,
|
||
|
unsigned char *pSrc,
|
||
|
int x, int y, int w, int h,
|
||
|
int xoff, int yoff,
|
||
|
int pHeight,
|
||
|
int srcwidth,
|
||
|
int Bpp
|
||
|
) {
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
|
||
|
unsigned char *src;
|
||
|
Bool PlusOne = FALSE;
|
||
|
int skipleft, dwords;
|
||
|
|
||
|
pSrc += (Bpp * xoff);
|
||
|
|
||
|
if((skipleft = (long)pSrc & 0x03L)) {
|
||
|
if(Bpp == 3)
|
||
|
skipleft = 4 - skipleft;
|
||
|
else
|
||
|
skipleft /= Bpp;
|
||
|
|
||
|
x -= skipleft;
|
||
|
w += skipleft;
|
||
|
|
||
|
if(Bpp == 3)
|
||
|
pSrc -= 3 * skipleft;
|
||
|
else /* is this Alpha friendly ? */
|
||
|
pSrc = (unsigned char*)((long)pSrc & ~0x03L);
|
||
|
}
|
||
|
|
||
|
src = pSrc + (yoff * srcwidth);
|
||
|
|
||
|
dwords = ((w * Bpp) + 3) >> 2;
|
||
|
|
||
|
if((infoRec->ImageWriteFlags & CPU_TRANSFER_PAD_QWORD) &&
|
||
|
((dwords * h) & 0x01)) {
|
||
|
PlusOne = TRUE;
|
||
|
}
|
||
|
|
||
|
(*infoRec->SubsequentImageWriteRect)(pScrn, x, y, w, h, skipleft);
|
||
|
|
||
|
if(dwords > infoRec->ImageWriteRange) {
|
||
|
while(h--) {
|
||
|
XAAMoveDWORDS_FixedBase((CARD32*)infoRec->ImageWriteBase,
|
||
|
(CARD32*)src, dwords);
|
||
|
src += srcwidth;
|
||
|
yoff++;
|
||
|
if(yoff >= pHeight) {
|
||
|
yoff = 0;
|
||
|
src = pSrc;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
if(srcwidth == (dwords << 2)) {
|
||
|
int maxLines = infoRec->ImageWriteRange/dwords;
|
||
|
int step;
|
||
|
|
||
|
while(h) {
|
||
|
step = pHeight - yoff;
|
||
|
if(step > maxLines) step = maxLines;
|
||
|
if(step > h) step = h;
|
||
|
|
||
|
XAAMoveDWORDS((CARD32*)infoRec->ImageWriteBase,
|
||
|
(CARD32*)src, dwords * step);
|
||
|
|
||
|
src += (srcwidth * step);
|
||
|
yoff += step;
|
||
|
if(yoff >= pHeight) {
|
||
|
yoff = 0;
|
||
|
src = pSrc;
|
||
|
}
|
||
|
h -= step;
|
||
|
}
|
||
|
} else {
|
||
|
while(h--) {
|
||
|
XAAMoveDWORDS((CARD32*)infoRec->ImageWriteBase,
|
||
|
(CARD32*)src, dwords);
|
||
|
src += srcwidth;
|
||
|
yoff++;
|
||
|
if(yoff >= pHeight) {
|
||
|
yoff = 0;
|
||
|
src = pSrc;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(PlusOne) {
|
||
|
CARD32* base = (CARD32*)infoRec->ImageWriteBase;
|
||
|
*base = 0x00000000;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
XAAFillImageWriteRects(
|
||
|
ScrnInfoPtr pScrn,
|
||
|
int rop,
|
||
|
unsigned int planemask,
|
||
|
int nBox,
|
||
|
BoxPtr pBox,
|
||
|
int xorg, int yorg,
|
||
|
PixmapPtr pPix
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
|
||
|
int x, phaseY, phaseX, height, width, blit_w;
|
||
|
int pHeight = pPix->drawable.height;
|
||
|
int pWidth = pPix->drawable.width;
|
||
|
int Bpp = pPix->drawable.bitsPerPixel >> 3;
|
||
|
int srcwidth = pPix->devKind;
|
||
|
|
||
|
(*infoRec->SetupForImageWrite)(pScrn, rop, planemask, -1,
|
||
|
pPix->drawable.bitsPerPixel, pPix->drawable.depth);
|
||
|
|
||
|
while(nBox--) {
|
||
|
x = pBox->x1;
|
||
|
phaseY = (pBox->y1 - yorg) % pHeight;
|
||
|
if(phaseY < 0) phaseY += pHeight;
|
||
|
phaseX = (x - xorg) % pWidth;
|
||
|
if(phaseX < 0) phaseX += pWidth;
|
||
|
height = pBox->y2 - pBox->y1;
|
||
|
width = pBox->x2 - x;
|
||
|
|
||
|
while(1) {
|
||
|
blit_w = pWidth - phaseX;
|
||
|
if(blit_w > width) blit_w = width;
|
||
|
|
||
|
WriteColumn(pScrn, pPix->devPrivate.ptr, x, pBox->y1,
|
||
|
blit_w, height, phaseX, phaseY, pHeight, srcwidth, Bpp);
|
||
|
|
||
|
width -= blit_w;
|
||
|
if(!width) break;
|
||
|
x += blit_w;
|
||
|
phaseX = (phaseX + blit_w) % pWidth;
|
||
|
}
|
||
|
pBox++;
|
||
|
}
|
||
|
|
||
|
if(infoRec->ImageWriteFlags & SYNC_AFTER_IMAGE_WRITE)
|
||
|
(*infoRec->Sync)(pScrn);
|
||
|
else SET_SYNC_FLAG(infoRec);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*************\
|
||
|
| Utilities |
|
||
|
\*************/
|
||
|
|
||
|
|
||
|
void
|
||
|
XAAClipAndRenderRects(
|
||
|
GCPtr pGC,
|
||
|
ClipAndRenderRectsFunc BoxFunc,
|
||
|
int nrectFill,
|
||
|
xRectangle *prect,
|
||
|
int xorg, int yorg
|
||
|
){
|
||
|
XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
|
||
|
int Right, Bottom, MaxBoxes;
|
||
|
BoxPtr pextent, pboxClipped, pboxClippedBase;
|
||
|
|
||
|
MaxBoxes = infoRec->PreAllocSize/sizeof(BoxRec);
|
||
|
pboxClippedBase = (BoxPtr)infoRec->PreAllocMem;
|
||
|
pboxClipped = pboxClippedBase;
|
||
|
|
||
|
if (REGION_NUM_RECTS(pGC->pCompositeClip) == 1) {
|
||
|
pextent = REGION_RECTS(pGC->pCompositeClip);
|
||
|
while (nrectFill--) {
|
||
|
pboxClipped->x1 = max(pextent->x1, prect->x);
|
||
|
pboxClipped->y1 = max(pextent->y1, prect->y);
|
||
|
|
||
|
Right = (int)prect->x + (int)prect->width;
|
||
|
pboxClipped->x2 = min(pextent->x2, Right);
|
||
|
|
||
|
Bottom = (int)prect->y + (int)prect->height;
|
||
|
pboxClipped->y2 = min(pextent->y2, Bottom);
|
||
|
|
||
|
prect++;
|
||
|
if ((pboxClipped->x1 < pboxClipped->x2) &&
|
||
|
(pboxClipped->y1 < pboxClipped->y2)) {
|
||
|
pboxClipped++;
|
||
|
if(pboxClipped >= (pboxClippedBase + MaxBoxes)) {
|
||
|
(*BoxFunc)(pGC, MaxBoxes, pboxClippedBase, xorg, yorg);
|
||
|
pboxClipped = pboxClippedBase;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
pextent = REGION_EXTENTS(pGC->pScreen, pGC->pCompositeClip);
|
||
|
while (nrectFill--) {
|
||
|
int n;
|
||
|
BoxRec box, *pbox;
|
||
|
|
||
|
box.x1 = max(pextent->x1, prect->x);
|
||
|
box.y1 = max(pextent->y1, prect->y);
|
||
|
|
||
|
Right = (int)prect->x + (int)prect->width;
|
||
|
box.x2 = min(pextent->x2, Right);
|
||
|
|
||
|
Bottom = (int)prect->y + (int)prect->height;
|
||
|
box.y2 = min(pextent->y2, Bottom);
|
||
|
|
||
|
prect++;
|
||
|
|
||
|
if ((box.x1 >= box.x2) || (box.y1 >= box.y2))
|
||
|
continue;
|
||
|
|
||
|
n = REGION_NUM_RECTS (pGC->pCompositeClip);
|
||
|
pbox = REGION_RECTS(pGC->pCompositeClip);
|
||
|
|
||
|
/* clip the rectangle to each box in the clip region
|
||
|
this is logically equivalent to calling Intersect()
|
||
|
*/
|
||
|
while(n--) {
|
||
|
pboxClipped->x1 = max(box.x1, pbox->x1);
|
||
|
pboxClipped->y1 = max(box.y1, pbox->y1);
|
||
|
pboxClipped->x2 = min(box.x2, pbox->x2);
|
||
|
pboxClipped->y2 = min(box.y2, pbox->y2);
|
||
|
pbox++;
|
||
|
|
||
|
/* see if clipping left anything */
|
||
|
if(pboxClipped->x1 < pboxClipped->x2 &&
|
||
|
pboxClipped->y1 < pboxClipped->y2) {
|
||
|
pboxClipped++;
|
||
|
if(pboxClipped >= (pboxClippedBase + MaxBoxes)) {
|
||
|
(*BoxFunc)(pGC, MaxBoxes, pboxClippedBase, xorg, yorg);
|
||
|
pboxClipped = pboxClippedBase;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(pboxClipped != pboxClippedBase)
|
||
|
(*BoxFunc)(pGC, pboxClipped - pboxClippedBase, pboxClippedBase,
|
||
|
xorg, yorg);
|
||
|
}
|
||
|
|
||
|
|
||
|
int
|
||
|
XAAGetRectClipBoxes(
|
||
|
GCPtr pGC,
|
||
|
BoxPtr pboxClippedBase,
|
||
|
int nrectFill,
|
||
|
xRectangle *prectInit
|
||
|
){
|
||
|
int Right, Bottom;
|
||
|
BoxPtr pextent, pboxClipped = pboxClippedBase;
|
||
|
xRectangle *prect = prectInit;
|
||
|
RegionPtr prgnClip = pGC->pCompositeClip;
|
||
|
|
||
|
if (REGION_NUM_RECTS(prgnClip) == 1) {
|
||
|
pextent = REGION_RECTS(prgnClip);
|
||
|
while (nrectFill--) {
|
||
|
pboxClipped->x1 = max(pextent->x1, prect->x);
|
||
|
pboxClipped->y1 = max(pextent->y1, prect->y);
|
||
|
|
||
|
Right = (int)prect->x + (int)prect->width;
|
||
|
pboxClipped->x2 = min(pextent->x2, Right);
|
||
|
|
||
|
Bottom = (int)prect->y + (int)prect->height;
|
||
|
pboxClipped->y2 = min(pextent->y2, Bottom);
|
||
|
|
||
|
prect++;
|
||
|
if ((pboxClipped->x1 < pboxClipped->x2) &&
|
||
|
(pboxClipped->y1 < pboxClipped->y2)) {
|
||
|
pboxClipped++;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
pextent = REGION_EXTENTS(pGC->pScreen, prgnClip);
|
||
|
while (nrectFill--) {
|
||
|
int n;
|
||
|
BoxRec box, *pbox;
|
||
|
|
||
|
box.x1 = max(pextent->x1, prect->x);
|
||
|
box.y1 = max(pextent->y1, prect->y);
|
||
|
|
||
|
Right = (int)prect->x + (int)prect->width;
|
||
|
box.x2 = min(pextent->x2, Right);
|
||
|
|
||
|
Bottom = (int)prect->y + (int)prect->height;
|
||
|
box.y2 = min(pextent->y2, Bottom);
|
||
|
|
||
|
prect++;
|
||
|
|
||
|
if ((box.x1 >= box.x2) || (box.y1 >= box.y2))
|
||
|
continue;
|
||
|
|
||
|
n = REGION_NUM_RECTS (prgnClip);
|
||
|
pbox = REGION_RECTS(prgnClip);
|
||
|
|
||
|
/* clip the rectangle to each box in the clip region
|
||
|
this is logically equivalent to calling Intersect()
|
||
|
*/
|
||
|
while(n--) {
|
||
|
pboxClipped->x1 = max(box.x1, pbox->x1);
|
||
|
pboxClipped->y1 = max(box.y1, pbox->y1);
|
||
|
pboxClipped->x2 = min(box.x2, pbox->x2);
|
||
|
pboxClipped->y2 = min(box.y2, pbox->y2);
|
||
|
pbox++;
|
||
|
|
||
|
/* see if clipping left anything */
|
||
|
if(pboxClipped->x1 < pboxClipped->x2 &&
|
||
|
pboxClipped->y1 < pboxClipped->y2) {
|
||
|
pboxClipped++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(pboxClipped - pboxClippedBase);
|
||
|
}
|
||
|
|