2006-11-26 11:13:41 -07:00
|
|
|
/*
|
|
|
|
* Copyright © 2004 Eric Anholt
|
|
|
|
*
|
|
|
|
* 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 Eric Anholt not be used in
|
|
|
|
* advertising or publicity pertaining to distribution of the software without
|
|
|
|
* specific, written prior permission. Eric Anholt makes no
|
|
|
|
* representations about the suitability of this software for any purpose. It
|
|
|
|
* is provided "as is" without express or implied warranty.
|
|
|
|
*
|
|
|
|
* ERIC ANHOLT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
|
|
* EVENT SHALL ERIC ANHOLT 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 <dix-config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "gcstruct.h"
|
|
|
|
#include "windowstr.h"
|
|
|
|
#include "cw.h"
|
|
|
|
|
|
|
|
#define cwPsDecl(pScreen) \
|
|
|
|
PictureScreenPtr ps = GetPictureScreen (pScreen); \
|
|
|
|
cwScreenPtr pCwScreen = getCwScreen (pScreen)
|
|
|
|
|
|
|
|
#define cwPicturePrivate \
|
|
|
|
cwPicturePtr pPicturePrivate = getCwPicture(pPicture)
|
|
|
|
|
|
|
|
#define cwSrcPictureDecl \
|
|
|
|
int src_picture_x_off, src_picture_y_off; \
|
|
|
|
PicturePtr pBackingSrcPicture = cwGetBackingPicture(pSrcPicture, \
|
|
|
|
&src_picture_x_off,\
|
|
|
|
&src_picture_y_off)
|
|
|
|
|
|
|
|
#define cwDstPictureDecl \
|
|
|
|
int dst_picture_x_off, dst_picture_y_off; \
|
|
|
|
PicturePtr pBackingDstPicture = cwGetBackingPicture(pDstPicture, \
|
|
|
|
&dst_picture_x_off,\
|
|
|
|
&dst_picture_y_off)
|
|
|
|
|
|
|
|
#define cwMskPictureDecl \
|
|
|
|
int msk_picture_x_off = 0, msk_picture_y_off = 0; \
|
|
|
|
PicturePtr pBackingMskPicture = (!pMskPicture ? 0 : \
|
|
|
|
cwGetBackingPicture(pMskPicture, \
|
|
|
|
&msk_picture_x_off,\
|
|
|
|
&msk_picture_y_off))
|
|
|
|
|
|
|
|
#define cwPsUnwrap(elt) { \
|
|
|
|
ps->elt = pCwScreen->elt; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define cwPsWrap(elt,func) { \
|
|
|
|
pCwScreen->elt = ps->elt; \
|
|
|
|
ps->elt = func; \
|
|
|
|
}
|
|
|
|
|
|
|
|
static cwPicturePtr
|
2012-06-10 07:21:05 -06:00
|
|
|
cwCreatePicturePrivate(PicturePtr pPicture)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
WindowPtr pWindow = (WindowPtr) pPicture->pDrawable;
|
|
|
|
PixmapPtr pPixmap = getCwPixmap(pWindow);
|
|
|
|
int error;
|
|
|
|
cwPicturePtr pPicturePrivate;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
pPicturePrivate = malloc(sizeof(cwPictureRec));
|
2006-11-26 11:13:41 -07:00
|
|
|
if (!pPicturePrivate)
|
2012-06-10 07:21:05 -06:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pPicturePrivate->pBackingPicture = CreatePicture(0, &pPixmap->drawable,
|
|
|
|
pPicture->pFormat,
|
|
|
|
0, 0, serverClient,
|
|
|
|
&error);
|
|
|
|
if (!pPicturePrivate->pBackingPicture) {
|
|
|
|
free(pPicturePrivate);
|
|
|
|
return NULL;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure that this serial number does not match the window's
|
|
|
|
*/
|
|
|
|
pPicturePrivate->serialNumber = pPixmap->drawable.serialNumber;
|
|
|
|
pPicturePrivate->stateChanges = (1 << (CPLastBit + 1)) - 1;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
setCwPicture(pPicture, pPicturePrivate);
|
|
|
|
|
|
|
|
return pPicturePrivate;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-10 07:21:05 -06:00
|
|
|
cwDestroyPicturePrivate(PicturePtr pPicture)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
cwPicturePrivate;
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (pPicturePrivate) {
|
|
|
|
if (pPicturePrivate->pBackingPicture)
|
|
|
|
FreePicture(pPicturePrivate->pBackingPicture, 0);
|
|
|
|
free(pPicturePrivate);
|
|
|
|
setCwPicture(pPicture, NULL);
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static PicturePtr
|
2012-06-10 07:21:05 -06:00
|
|
|
cwGetBackingPicture(PicturePtr pPicture, int *x_off, int *y_off)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
cwPicturePrivate;
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (pPicturePrivate) {
|
|
|
|
DrawablePtr pDrawable = pPicture->pDrawable;
|
|
|
|
WindowPtr pWindow = (WindowPtr) pDrawable;
|
|
|
|
PixmapPtr pPixmap = getCwPixmap(pWindow);
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
*x_off = pDrawable->x - pPixmap->screen_x;
|
|
|
|
*y_off = pDrawable->y - pPixmap->screen_y;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
return pPicturePrivate->pBackingPicture;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
else {
|
|
|
|
*x_off = *y_off = 0;
|
|
|
|
return pPicture;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
static void
|
2012-06-10 07:21:05 -06:00
|
|
|
cwDestroyPicture(PicturePtr pPicture)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
ScreenPtr pScreen = pPicture->pDrawable->pScreen;
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsDecl(pScreen);
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsUnwrap(DestroyPicture);
|
2012-06-10 07:21:05 -06:00
|
|
|
cwDestroyPicturePrivate(pPicture);
|
2006-11-26 11:13:41 -07:00
|
|
|
(*ps->DestroyPicture) (pPicture);
|
|
|
|
cwPsWrap(DestroyPicture, cwDestroyPicture);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-10 07:21:05 -06:00
|
|
|
cwChangePicture(PicturePtr pPicture, Mask mask)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
ScreenPtr pScreen = pPicture->pDrawable->pScreen;
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsDecl(pScreen);
|
2012-06-10 07:21:05 -06:00
|
|
|
cwPicturePtr pPicturePrivate = getCwPicture(pPicture);
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsUnwrap(ChangePicture);
|
|
|
|
(*ps->ChangePicture) (pPicture, mask);
|
|
|
|
if (pPicturePrivate)
|
2012-06-10 07:21:05 -06:00
|
|
|
pPicturePrivate->stateChanges |= mask;
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsWrap(ChangePicture, cwChangePicture);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-10 07:21:05 -06:00
|
|
|
cwValidatePicture(PicturePtr pPicture, Mask mask)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
DrawablePtr pDrawable = pPicture->pDrawable;
|
|
|
|
ScreenPtr pScreen = pDrawable->pScreen;
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsDecl(pScreen);
|
|
|
|
cwPicturePrivate;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsUnwrap(ValidatePicture);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Must call ValidatePicture to ensure pPicture->pCompositeClip is valid
|
|
|
|
*/
|
|
|
|
(*ps->ValidatePicture) (pPicture, mask);
|
2012-06-10 07:21:05 -06:00
|
|
|
|
|
|
|
if (!cwDrawableIsRedirWindow(pDrawable)) {
|
|
|
|
if (pPicturePrivate)
|
|
|
|
cwDestroyPicturePrivate(pPicture);
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
else {
|
|
|
|
PicturePtr pBackingPicture;
|
|
|
|
DrawablePtr pBackingDrawable;
|
|
|
|
int x_off, y_off;
|
|
|
|
|
|
|
|
pBackingDrawable = cwGetBackingDrawable(pDrawable, &x_off, &y_off);
|
|
|
|
|
|
|
|
if (pPicturePrivate &&
|
|
|
|
pPicturePrivate->pBackingPicture->pDrawable != pBackingDrawable) {
|
|
|
|
cwDestroyPicturePrivate(pPicture);
|
|
|
|
pPicturePrivate = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pPicturePrivate) {
|
|
|
|
pPicturePrivate = cwCreatePicturePrivate(pPicture);
|
|
|
|
if (!pPicturePrivate) {
|
|
|
|
cwPsWrap(ValidatePicture, cwValidatePicture);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pBackingPicture = pPicturePrivate->pBackingPicture;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Always copy transform and filters because there's no
|
|
|
|
* indication of when they've changed
|
|
|
|
*/
|
|
|
|
SetPictureTransform(pBackingPicture, pPicture->transform);
|
|
|
|
|
|
|
|
if (pBackingPicture->filter != pPicture->filter ||
|
|
|
|
pPicture->filter_nparams > 0) {
|
|
|
|
char *filter = PictureGetFilterName(pPicture->filter);
|
|
|
|
|
|
|
|
SetPictureFilter(pBackingPicture,
|
|
|
|
filter, strlen(filter),
|
|
|
|
pPicture->filter_params, pPicture->filter_nparams);
|
|
|
|
}
|
|
|
|
|
|
|
|
pPicturePrivate->stateChanges |= mask;
|
|
|
|
|
|
|
|
if (pPicturePrivate->serialNumber != pDrawable->serialNumber ||
|
|
|
|
(pPicturePrivate->
|
|
|
|
stateChanges & (CPClipXOrigin | CPClipYOrigin | CPClipMask))) {
|
|
|
|
SetPictureClipRegion(pBackingPicture, x_off - pDrawable->x,
|
|
|
|
y_off - pDrawable->y,
|
|
|
|
pPicture->pCompositeClip);
|
|
|
|
|
|
|
|
pPicturePrivate->serialNumber = pDrawable->serialNumber;
|
|
|
|
pPicturePrivate->stateChanges &=
|
|
|
|
~(CPClipXOrigin | CPClipYOrigin | CPClipMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
CopyPicture(pPicture, pPicturePrivate->stateChanges, pBackingPicture);
|
|
|
|
|
|
|
|
ValidatePicture(pBackingPicture);
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
cwPsWrap(ValidatePicture, cwValidatePicture);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-10 07:21:05 -06:00
|
|
|
cwComposite(CARD8 op,
|
|
|
|
PicturePtr pSrcPicture,
|
|
|
|
PicturePtr pMskPicture,
|
|
|
|
PicturePtr pDstPicture,
|
|
|
|
INT16 xSrc,
|
|
|
|
INT16 ySrc,
|
|
|
|
INT16 xMsk,
|
|
|
|
INT16 yMsk, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
ScreenPtr pScreen = pDstPicture->pDrawable->pScreen;
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsDecl(pScreen);
|
|
|
|
cwSrcPictureDecl;
|
|
|
|
cwMskPictureDecl;
|
|
|
|
cwDstPictureDecl;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsUnwrap(Composite);
|
2012-06-10 07:21:05 -06:00
|
|
|
(*ps->Composite) (op, pBackingSrcPicture, pBackingMskPicture,
|
|
|
|
pBackingDstPicture, xSrc + src_picture_x_off,
|
|
|
|
ySrc + src_picture_y_off, xMsk + msk_picture_x_off,
|
|
|
|
yMsk + msk_picture_y_off, xDst + dst_picture_x_off,
|
|
|
|
yDst + dst_picture_y_off, width, height);
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsWrap(Composite, cwComposite);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-10 07:21:05 -06:00
|
|
|
cwCompositeRects(CARD8 op,
|
|
|
|
PicturePtr pDstPicture,
|
|
|
|
xRenderColor * color, int nRect, xRectangle *rects)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
ScreenPtr pScreen = pDstPicture->pDrawable->pScreen;
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsDecl(pScreen);
|
|
|
|
cwDstPictureDecl;
|
|
|
|
int i;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsUnwrap(CompositeRects);
|
2012-06-10 07:21:05 -06:00
|
|
|
for (i = 0; i < nRect; i++) {
|
|
|
|
rects[i].x += dst_picture_x_off;
|
|
|
|
rects[i].y += dst_picture_y_off;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
(*ps->CompositeRects) (op, pBackingDstPicture, color, nRect, rects);
|
|
|
|
cwPsWrap(CompositeRects, cwCompositeRects);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-10 07:21:05 -06:00
|
|
|
cwTrapezoids(CARD8 op,
|
|
|
|
PicturePtr pSrcPicture,
|
|
|
|
PicturePtr pDstPicture,
|
|
|
|
PictFormatPtr maskFormat,
|
|
|
|
INT16 xSrc, INT16 ySrc, int ntrap, xTrapezoid * traps)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
ScreenPtr pScreen = pDstPicture->pDrawable->pScreen;
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsDecl(pScreen);
|
|
|
|
cwSrcPictureDecl;
|
|
|
|
cwDstPictureDecl;
|
|
|
|
int i;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsUnwrap(Trapezoids);
|
|
|
|
if (dst_picture_x_off || dst_picture_y_off) {
|
2012-06-10 07:21:05 -06:00
|
|
|
for (i = 0; i < ntrap; i++) {
|
|
|
|
traps[i].top += dst_picture_y_off << 16;
|
|
|
|
traps[i].bottom += dst_picture_y_off << 16;
|
|
|
|
traps[i].left.p1.x += dst_picture_x_off << 16;
|
|
|
|
traps[i].left.p1.y += dst_picture_y_off << 16;
|
|
|
|
traps[i].left.p2.x += dst_picture_x_off << 16;
|
|
|
|
traps[i].left.p2.y += dst_picture_y_off << 16;
|
|
|
|
traps[i].right.p1.x += dst_picture_x_off << 16;
|
|
|
|
traps[i].right.p1.y += dst_picture_y_off << 16;
|
|
|
|
traps[i].right.p2.x += dst_picture_x_off << 16;
|
|
|
|
traps[i].right.p2.y += dst_picture_y_off << 16;
|
|
|
|
}
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
(*ps->Trapezoids) (op, pBackingSrcPicture, pBackingDstPicture, maskFormat,
|
2012-06-10 07:21:05 -06:00
|
|
|
xSrc + src_picture_x_off, ySrc + src_picture_y_off,
|
|
|
|
ntrap, traps);
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsWrap(Trapezoids, cwTrapezoids);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-10 07:21:05 -06:00
|
|
|
cwTriangles(CARD8 op,
|
|
|
|
PicturePtr pSrcPicture,
|
|
|
|
PicturePtr pDstPicture,
|
|
|
|
PictFormatPtr maskFormat,
|
|
|
|
INT16 xSrc, INT16 ySrc, int ntri, xTriangle * tris)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
ScreenPtr pScreen = pDstPicture->pDrawable->pScreen;
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsDecl(pScreen);
|
|
|
|
cwSrcPictureDecl;
|
|
|
|
cwDstPictureDecl;
|
|
|
|
int i;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsUnwrap(Triangles);
|
|
|
|
if (dst_picture_x_off || dst_picture_y_off) {
|
2012-06-10 07:21:05 -06:00
|
|
|
for (i = 0; i < ntri; i++) {
|
|
|
|
tris[i].p1.x += dst_picture_x_off << 16;
|
|
|
|
tris[i].p1.y += dst_picture_y_off << 16;
|
|
|
|
tris[i].p2.x += dst_picture_x_off << 16;
|
|
|
|
tris[i].p2.y += dst_picture_y_off << 16;
|
|
|
|
tris[i].p3.x += dst_picture_x_off << 16;
|
|
|
|
tris[i].p3.y += dst_picture_y_off << 16;
|
|
|
|
}
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
(*ps->Triangles) (op, pBackingSrcPicture, pBackingDstPicture, maskFormat,
|
2012-06-10 07:21:05 -06:00
|
|
|
xSrc + src_picture_x_off, ySrc + src_picture_y_off,
|
|
|
|
ntri, tris);
|
2006-11-26 11:13:41 -07:00
|
|
|
cwPsWrap(Triangles, cwTriangles);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-06-10 07:21:05 -06:00
|
|
|
cwInitializeRender(ScreenPtr pScreen)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
cwPsDecl(pScreen);
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
cwPsWrap(DestroyPicture, cwDestroyPicture);
|
|
|
|
cwPsWrap(ChangePicture, cwChangePicture);
|
|
|
|
cwPsWrap(ValidatePicture, cwValidatePicture);
|
|
|
|
cwPsWrap(Composite, cwComposite);
|
|
|
|
cwPsWrap(CompositeRects, cwCompositeRects);
|
|
|
|
cwPsWrap(Trapezoids, cwTrapezoids);
|
|
|
|
cwPsWrap(Triangles, cwTriangles);
|
|
|
|
/* There is no need to wrap AddTraps as far as we can tell. AddTraps can
|
|
|
|
* only be done on alpha-only pictures, and we won't be getting
|
|
|
|
* alpha-only window pictures, so there's no need to translate.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-06-10 07:21:05 -06:00
|
|
|
cwFiniRender(ScreenPtr pScreen)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
cwPsDecl(pScreen);
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
cwPsUnwrap(DestroyPicture);
|
|
|
|
cwPsUnwrap(ChangePicture);
|
|
|
|
cwPsUnwrap(ValidatePicture);
|
|
|
|
cwPsUnwrap(Composite);
|
|
|
|
cwPsUnwrap(CompositeRects);
|
|
|
|
cwPsUnwrap(Trapezoids);
|
|
|
|
cwPsUnwrap(Triangles);
|
|
|
|
}
|