00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "filters.h"
00016 #include "graphic.h"
00017 #include "goom_tools.h"
00018 #include <stdlib.h>
00019 #include <math.h>
00020 #include <stdio.h>
00021 #include <inttypes.h>
00022
00023 #ifdef MMX
00024 #define USE_ASM
00025 #endif
00026 #ifdef POWERPC
00027 #define USE_ASM
00028 #endif
00029
00030 #define EFFECT_DISTORS 4
00031 #define EFFECT_DISTORS_SL 2
00032
00033 extern volatile guint32 resolx;
00034 extern volatile guint32 c_resoly;
00035
00036 void c_zoom (unsigned int *expix1, unsigned int *expix2, unsigned int prevX, unsigned int prevY, signed int *brutS, signed int *brutD);
00037
00038 #ifdef MMX
00039
00040 void zoom_filter_xmmx (int prevX, int prevY, unsigned int *expix1, unsigned int *expix2, int *brutS, int *brutD, int buffratio, int precalCoef[16][16]);
00041 int zoom_filter_xmmx_supported ();
00042 void zoom_filter_mmx (int prevX, int prevY, unsigned int *expix1, unsigned int *expix2, int *brutS, int *brutD, int buffratio, int precalCoef[16][16]);
00043 int zoom_filter_mmx_supported ();
00044
00045 static int zf_use_xmmx = 0;
00046 static int zf_use_mmx = 0;
00047
00048 static void select_zoom_filter () {
00049 static int firsttime = 1;
00050 if (firsttime){
00051 if (zoom_filter_xmmx_supported()) {
00052 zf_use_xmmx = 1;
00053 printf ("Extended MMX detected. Using the fastest method !\n");
00054 }
00055 else if (zoom_filter_mmx_supported()) {
00056 zf_use_mmx = 1;
00057 printf ("MMX detected. Using fast method !\n");
00058 }
00059 else {
00060 printf ("Too bad ! No MMX detected.\n");
00061 }
00062 firsttime = 0;
00063 }
00064 }
00065
00066 #else
00067
00068 static void select_zoom_filter () {
00069 static int firsttime = 1;
00070 if (firsttime) {
00071 printf ("No MMX support compiled in\n");
00072 firsttime = 0;
00073 }
00074 }
00075
00076 #endif
00077
00078
00079 guint32 mmx_zoom_size;
00080
00081 #ifdef USE_ASM
00082
00083 #ifdef POWERPC
00084 #include "altivec.h"
00085 extern unsigned int useAltivec;
00086 extern const void ppc_zoom (unsigned int *frompixmap, unsigned int *topixmap, unsigned int sizex, unsigned int sizey, unsigned int *brutS, unsigned int *brutD, unsigned int buffratio, int precalCoef[16][16]);
00087
00088 #endif
00089
00090 #endif
00091
00092
00093 unsigned int *coeffs = 0, *freecoeffs = 0;
00094
00095 signed int *brutS = 0, *freebrutS = 0;
00096 signed int *brutD = 0, *freebrutD = 0;
00097 signed int *brutT = 0, *freebrutT = 0;
00098
00099
00100 guint32 *expix1 = 0;
00101 guint32 *expix2 = 0;
00102
00103
00104 guint32 zoom_width;
00105
00106 unsigned int prevX = 0, prevY = 0;
00107
00108 static int sintable[0x10000];
00109 static int vitesse = 127;
00110 static char theMode = AMULETTE_MODE;
00111 static int waveEffect = 0;
00112 static int hypercosEffect = 0;
00113 static int vPlaneEffect = 0;
00114 static int hPlaneEffect = 0;
00115 static char noisify = 2;
00116 static int middleX, middleY;
00117
00118
00119
00121
00122 int buffratio = 0;
00123
00124 #define BUFFPOINTNB 16
00125 #define BUFFPOINTMASK 0xffff
00126 #define BUFFINCR 0xff
00127
00128 #define sqrtperte 16
00129
00130 #define PERTEMASK 0xf
00131
00132 #define PERTEDEC 4
00133
00134 static int *firedec = 0;
00135
00136
00137
00138 #define ShiftRight(_x,_s) (((_x)<0) ? -(-(_x)>>(_s)) : ((_x)>>(_s)))
00139
00141 int precalCoef[16][16];
00142
00143 void
00144 generatePrecalCoef ()
00145 {
00146 static int firstime = 1;
00147
00148 if (firstime) {
00149 int coefh, coefv;
00150
00151 firstime = 0;
00152
00153 for (coefh = 0; coefh < 16; coefh++) {
00154
00155 for (coefv = 0; coefv < 16; coefv++) {
00156 int i;
00157 int diffcoeffh;
00158 int diffcoeffv;
00159
00160 diffcoeffh = sqrtperte - coefh;
00161 diffcoeffv = sqrtperte - coefv;
00162
00163
00164
00165 if (!(coefh || coefv))
00166 i = 255;
00167 else {
00168 int i1, i2, i3, i4;
00169
00170 i1 = diffcoeffh * diffcoeffv;
00171 i2 = coefh * diffcoeffv;
00172 i3 = diffcoeffh * coefv;
00173 i4 = coefh * coefv;
00174 if (i1)
00175 i1--;
00176 if (i2)
00177 i2--;
00178 if (i3)
00179 i3--;
00180 if (i4)
00181 i4--;
00182
00183 i = (i1) | (i2 << 8) | (i3 << 16) | (i4 << 24);
00184 }
00185 precalCoef[coefh][coefv] = i;
00186 }
00187 }
00188 }
00189 }
00190
00191
00192
00193
00194
00195
00196 inline void
00197 calculatePXandPY (int x, int y, int *px, int *py)
00198 {
00199 if (theMode == WATER_MODE) {
00200 static int wave = 0;
00201 static int wavesp = 0;
00202 int yy;
00203
00204 yy = y + RAND () % 4 - RAND () % 4 + wave / 10;
00205 if (yy < 0)
00206 yy = 0;
00207 if (yy >= (int)c_resoly)
00208 yy = c_resoly - 1;
00209
00210 *px = (x << 4) + firedec[yy] + (wave / 10);
00211 *py = (y << 4) + 132 - ((vitesse < 131) ? vitesse : 130);
00212
00213 wavesp += RAND () % 3 - RAND () % 3;
00214 if (wave < -10)
00215 wavesp += 2;
00216 if (wave > 10)
00217 wavesp -= 2;
00218 wave += (wavesp / 10) + RAND () % 3 - RAND () % 3;
00219 if (wavesp > 100)
00220 wavesp = (wavesp * 9) / 10;
00221 }
00222 else {
00223 int dist = 0, vx9, vy9;
00224 int vx, vy;
00225 int ppx, ppy;
00226 int fvitesse = vitesse << 4;
00227
00228 if (noisify) {
00229 x += RAND () % noisify - RAND () % noisify;
00230 y += RAND () % noisify - RAND () % noisify;
00231 }
00232 vx = (x - middleX) << 9;
00233 vy = (y - middleY) << 9;
00234
00235 if (hPlaneEffect)
00236 vx += hPlaneEffect * (y - middleY);
00237
00238
00239 if (vPlaneEffect)
00240 vy += vPlaneEffect * (x - middleX);
00241
00242
00243 if (waveEffect) {
00244 fvitesse *=
00245 1024 +
00246 ShiftRight (sintable
00247 [(unsigned short) (dist * 0xffff + EFFECT_DISTORS)], 6);
00248 fvitesse /= 1024;
00249 }
00250
00251 if (hypercosEffect) {
00252 vx += ShiftRight (sintable[(-vy + dist) & 0xffff], 1);
00253 vy += ShiftRight (sintable[(vx + dist) & 0xffff], 1);
00254 }
00255
00256 vx9 = ShiftRight (vx, 9);
00257 vy9 = ShiftRight (vy, 9);
00258 dist = vx9 * vx9 + vy9 * vy9;
00259
00260 switch (theMode) {
00261 case WAVE_MODE:
00262 fvitesse *=
00263 1024 +
00264 ShiftRight (sintable
00265 [(unsigned short) (dist * 0xffff * EFFECT_DISTORS)], 6);
00266 fvitesse>>=10;
00267 break;
00268 case CRYSTAL_BALL_MODE:
00269 fvitesse += (dist >> (10-EFFECT_DISTORS_SL));
00270 break;
00271 case AMULETTE_MODE:
00272 fvitesse -= (dist >> (4 - EFFECT_DISTORS_SL));
00273 break;
00274 case SCRUNCH_MODE:
00275 fvitesse -= (dist >> (10 - EFFECT_DISTORS_SL));
00276 break;
00277 case HYPERCOS1_MODE:
00278 vx = vx + ShiftRight (sintable[(-vy + dist) & 0xffff], 1);
00279 vy = vy + ShiftRight (sintable[(vx + dist) & 0xffff], 1);
00280 break;
00281 case HYPERCOS2_MODE:
00282 vx =
00283 vx + ShiftRight (sintable[(-ShiftRight (vy, 1) + dist) & 0xffff], 0);
00284 vy =
00285 vy + ShiftRight (sintable[(ShiftRight (vx, 1) + dist) & 0xffff], 0);
00286 fvitesse = 128 << 4;
00287 break;
00288 case YONLY_MODE:
00289 fvitesse *= 1024 + ShiftRight (sintable[vy & 0xffff], 6);
00290 fvitesse >>= 10;
00291 break;
00292 case SPEEDWAY_MODE:
00293 fvitesse -= (ShiftRight(vy,10-EFFECT_DISTORS_SL));
00294 break;
00295 }
00296
00297 if (fvitesse < -3024)
00298 fvitesse = -3024;
00299
00300 if (vx < 0)
00301 ppx = -(-(vx * fvitesse) >> 16);
00302
00303
00304 else
00305 ppx = ((vx * fvitesse) >> 16);
00306
00307 if (vy < 0)
00308 ppy = -(-(vy * fvitesse) >> 16);
00309 else
00310 ppy = ((vy * fvitesse) >> 16);
00311
00312 *px = (middleX << 4) + ppx;
00313 *py = (middleY << 4) + ppy;
00314 }
00315 }
00316
00317
00318
00319 inline void
00320 setPixelRGB (Uint * buffer, Uint x, Uint y, Color c)
00321 {
00322
00323 #ifdef _DEBUG_PIXEL
00324 if (x + y * resolx >= resolx * resoly) {
00325 fprintf (stderr, "setPixel ERROR : hors du tableau... %i, %i\n", x, y);
00326
00327 }
00328 #endif
00329
00330 buffer[y * resolx + x] =
00331 (c.b << (BLEU * 8)) | (c.v << (VERT * 8)) | (c.r << (ROUGE * 8));
00332 }
00333
00334
00335 inline void
00336 setPixelRGB_ (Uint * buffer, Uint x, Color c)
00337 {
00338 #ifdef _DEBUG
00339 if (x >= resolx * c_resoly) {
00340 printf ("setPixel ERROR : hors du tableau... %i\n", x);
00341
00342 }
00343 #endif
00344
00345 buffer[x] = (c.r << (ROUGE * 8)) | (c.v << (VERT * 8)) | c.b << (BLEU * 8);
00346 }
00347
00348
00349
00350 inline void
00351 getPixelRGB (Uint * buffer, Uint x, Uint y, Color * c)
00352 {
00353
00354 unsigned int i;
00355
00356 #ifdef _DEBUG
00357 if (x + y * resolx >= resolx * c_resoly) {
00358 printf ("getPixel ERROR : hors du tableau... %i, %i\n", x, y);
00359
00360 }
00361 #endif
00362
00363
00364 i = *(buffer + (x + y * resolx));
00365 c->b = (i >> (BLEU * 8)) & 0xff;
00366 c->v = (i >> (VERT * 8)) & 0xff;
00367 c->r = (i >> (ROUGE * 8)) & 0xff;
00368 }
00369
00370
00371 inline void
00372 getPixelRGB_ (Uint * buffer, Uint x, Color * c)
00373 {
00374 register unsigned char *tmp8;
00375
00376 #ifdef _DEBUG
00377 if (x >= resolx * c_resoly) {
00378 printf ("getPixel ERROR : hors du tableau... %i\n", x);
00379
00380 }
00381 #endif
00382
00383 #ifdef __BIG_ENDIAN__
00384 c->b = *(unsigned char *) (tmp8 = (unsigned char *) (buffer + x));
00385 c->r = *(unsigned char *) (++tmp8);
00386 c->v = *(unsigned char *) (++tmp8);
00387 c->b = *(unsigned char *) (++tmp8);
00388
00389 #else
00390
00391 c->b = *(unsigned char *) (tmp8 = (unsigned char *) (buffer + x));
00392 c->v = *(unsigned char *) (++tmp8);
00393 c->r = *(unsigned char *) (++tmp8);
00394
00395 #endif
00396 }
00397
00398
00399 void c_zoom (unsigned int *expix1, unsigned int *expix2, unsigned int prevX, unsigned int prevY, signed int *brutS, signed int *brutD)
00400 {
00401 int myPos, myPos2;
00402 Color couleur;
00403
00404
00405 unsigned int ax = (prevX - 1) << PERTEDEC, ay = (prevY - 1) << PERTEDEC;
00406
00407 int bufsize = prevX * prevY * 2;
00408 int bufwidth = prevX;
00409
00410 expix1[0]=expix1[prevX-1]=expix1[prevX*prevY-1]=expix1[prevX*prevY-prevX]=0;
00411
00412 for (myPos = 0; myPos < bufsize; myPos += 2) {
00413 Color col1, col2, col3, col4;
00414 int c1, c2, c3, c4, px, py;
00415 int pos;
00416 int coeffs;
00417
00418 int brutSmypos = brutS[myPos];
00419
00420 myPos2 = myPos + 1;
00421
00422 px = brutSmypos + (((brutD[myPos] - brutSmypos) * buffratio) >> BUFFPOINTNB);
00423 brutSmypos = brutS[myPos2];
00424 py = brutSmypos + (((brutD[myPos2] - brutSmypos) * buffratio) >> BUFFPOINTNB);
00425
00426 if (px < 0)
00427 px = 0;
00428 if (py < 0)
00429 py = 0;
00430
00431 pos = ((px >> PERTEDEC) + prevX * (py >> PERTEDEC));
00432
00433 coeffs = precalCoef[px & PERTEMASK][py & PERTEMASK];
00434
00435 if ((py >= (int)ay) || (px >= (int)ax)) {
00436 pos = coeffs = 0;
00437 }
00438
00439 getPixelRGB_ (expix1, pos, &col1);
00440 getPixelRGB_ (expix1, pos + 1, &col2);
00441 getPixelRGB_ (expix1, pos + bufwidth, &col3);
00442 getPixelRGB_ (expix1, pos + bufwidth + 1, &col4);
00443
00444 c1 = coeffs;
00445 c2 = (c1 & 0x0000ff00) >> 8;
00446 c3 = (c1 & 0x00ff0000) >> 16;
00447 c4 = (c1 & 0xff000000) >> 24;
00448 c1 = c1 & 0xff;
00449
00450 couleur.r = col1.r * c1 + col2.r * c2 + col3.r * c3 + col4.r * c4;
00451 if (couleur.r > 5)
00452 couleur.r -= 5;
00453 couleur.r >>= 8;
00454
00455 couleur.v = col1.v * c1 + col2.v * c2 + col3.v * c3 + col4.v * c4;
00456 if (couleur.v > 5)
00457 couleur.v -= 5;
00458 couleur.v >>= 8;
00459
00460 couleur.b = col1.b * c1 + col2.b * c2 + col3.b * c3 + col4.b * c4;
00461 if (couleur.b > 5)
00462 couleur.b -= 5;
00463 couleur.b >>= 8;
00464
00465 setPixelRGB_ (expix2, myPos >> 1, couleur);
00466 }
00467 }
00468
00469 #ifdef USE_ASM
00470 static int use_asm = 1;
00471 void
00472 setAsmUse (int useIt)
00473 {
00474 use_asm = useIt;
00475 }
00476
00477 int
00478 getAsmUse ()
00479 {
00480 return use_asm;
00481 }
00482 #endif
00483
00484
00485 void
00486 zoomFilterFastRGB (Uint * pix1, Uint * pix2, ZoomFilterData * zf, Uint resx, Uint resy, int switchIncr, float switchMult)
00487 {
00488 register Uint x, y;
00489
00490 static char reverse = 0;
00491 static unsigned char pertedec = 8;
00492 static char firstTime = 1;
00493
00494 #define INTERLACE_INCR 16
00495 #define INTERLACE_ADD 9
00496 #define INTERLACE_AND 0xf
00497 static int interlace_start = -2;
00498
00499 expix1 = pix1;
00500 expix2 = pix2;
00501
00503 if ((prevX != resx) || (prevY != resy)) {
00504 prevX = resx;
00505 prevY = resy;
00506
00507 if (brutS)
00508 free (freebrutS);
00509 brutS = 0;
00510 if (brutD)
00511 free (freebrutD);
00512 brutD = 0;
00513 if (brutT)
00514 free (freebrutT);
00515 brutT = 0;
00516
00517 middleX = resx / 2;
00518 middleY = resy - 1;
00519 firstTime = 1;
00520 if (firedec)
00521 free (firedec);
00522 firedec = 0;
00523 }
00524
00525 if (interlace_start != -2)
00526 zf = NULL;
00527
00529 if (zf) {
00530 reverse = zf->reverse;
00531 vitesse = zf->vitesse;
00532 if (reverse)
00533 vitesse = 256 - vitesse;
00534 pertedec = zf->pertedec;
00535 middleX = zf->middleX;
00536 middleY = zf->middleY;
00537 theMode = zf->mode;
00538 hPlaneEffect = zf->hPlaneEffect;
00539 vPlaneEffect = zf->vPlaneEffect;
00540 waveEffect = zf->waveEffect;
00541 hypercosEffect = zf->hypercosEffect;
00542 noisify = zf->noisify;
00543 }
00544
00546 if (firstTime || zf) {
00547
00548
00549 if (firstTime) {
00550 unsigned short us;
00551 int yofs;
00552
00553 firstTime = 0;
00554 generatePrecalCoef ();
00555 select_zoom_filter ();
00556
00557 freebrutS = (signed int *) calloc (resx * resy * 2 + 128, sizeof(unsigned int));
00558 brutS = (signed int *) ((1 + ((uintptr_t) (freebrutS)) / 128) * 128);
00559
00560 freebrutD = (signed int *) calloc (resx * resy * 2 + 128, sizeof(unsigned int));
00561 brutD = (signed int *) ((1 + ((uintptr_t) (freebrutD)) / 128) * 128);
00562
00563 freebrutT = (signed int *) calloc (resx * resy * 2 + 128, sizeof(unsigned int));
00564 brutT = (signed int *) ((1 + ((uintptr_t) (freebrutT)) / 128) * 128);
00565
00567 {
00568 int yperte = 0;
00569
00570 for (y = 0, yofs = 0; y < resy; y++, yofs += resx) {
00571 int xofs = yofs << 1;
00572 int xperte = 0;
00573
00574 for (x = 0; x < resx; x++) {
00575 brutS[xofs++] = xperte;
00576 brutS[xofs++] = yperte;
00577 xperte += sqrtperte;
00578 }
00579 yperte += sqrtperte;
00580 }
00581 buffratio = 0;
00582 }
00583
00584 for (us = 0; us < 0xffff; us++) {
00585 sintable[us] = (int) (1024 * sin ((double) us * 360 / (sizeof (sintable) / sizeof (sintable[0]) - 1) * 3.141592 / 180) + .5);
00586 }
00587
00588 {
00589 int loopv;
00590 firedec = (int *) malloc (prevY * sizeof (int));
00591
00592 for (loopv = prevY; loopv != 0;) {
00593 static int decc = 0;
00594 static int spdc = 0;
00595 static int accel = 0;
00596
00597 loopv--;
00598 firedec[loopv] = decc;
00599 decc += spdc / 10;
00600 spdc += RAND () % 3 - RAND () % 3;
00601
00602 if (decc > 4)
00603 spdc -= 1;
00604 if (decc < -4)
00605 spdc += 1;
00606
00607 if (spdc > 30)
00608 spdc = spdc - RAND () % 3 + accel / 10;
00609 if (spdc < -30)
00610 spdc = spdc + RAND () % 3 + accel / 10;
00611
00612 if (decc > 8 && spdc > 1)
00613 spdc -= RAND () % 3 - 2;
00614
00615 if (decc < -8 && spdc < -1)
00616 spdc += RAND () % 3 + 2;
00617
00618 if (decc > 8 || decc < -8)
00619 decc = decc * 8 / 9;
00620
00621 accel += RAND () % 2 - RAND () % 2;
00622 if (accel > 20)
00623 accel -= 2;
00624 if (accel < -20)
00625 accel += 2;
00626 }
00627 }
00628 }
00629
00630 interlace_start = 0;
00631 }
00632
00633 if (interlace_start==-1) {
00634
00635
00636
00637 y = prevX * prevY * 2;
00638 for (x = 0; x < y; x += 2) {
00639 int brutSmypos = brutS[x];
00640 int x2 = x + 1;
00641
00642 brutS[x] =
00643 brutSmypos + (((brutD[x] - brutSmypos) * buffratio) >> BUFFPOINTNB);
00644 brutSmypos = brutS[x2];
00645 brutS[x2] =
00646 brutSmypos +
00647 (((brutD[x2] - brutSmypos) * buffratio) >> BUFFPOINTNB);
00648 }
00649 buffratio = 0;
00650 }
00651
00652 if (interlace_start==-1) {
00653 signed int * tmp;
00654 tmp = brutD;
00655 brutD=brutT;
00656 brutT=tmp;
00657 tmp = freebrutD;
00658 freebrutD=freebrutT;
00659 freebrutT=tmp;
00660 interlace_start = -2;
00661 }
00662
00663 if (interlace_start>=0) {
00664 int maxEnd = (interlace_start+INTERLACE_INCR);
00665
00666 for (y = (Uint)interlace_start; (y < (Uint)prevY) && (y < (Uint)maxEnd); y++) {
00667 Uint premul_y_prevX = y * prevX * 2;
00668 for (x = 0; x < prevX; x++) {
00669 int px, py;
00670
00671 calculatePXandPY (x, y, &px, &py);
00672
00673 brutT[premul_y_prevX] = px;
00674 brutT[premul_y_prevX + 1] = py;
00675 premul_y_prevX += 2;
00676 }
00677 }
00678 interlace_start += INTERLACE_INCR;
00679 if (y >= prevY-1) interlace_start = -1;
00680 }
00681
00682 if (switchIncr != 0) {
00683 buffratio += switchIncr;
00684 if (buffratio > BUFFPOINTMASK)
00685 buffratio = BUFFPOINTMASK;
00686 }
00687
00688 if (switchMult != 1.0f) {
00689 buffratio =
00690 (int) ((float) BUFFPOINTMASK * (1.0f - switchMult) +
00691 (float) buffratio * switchMult);
00692 }
00693
00694 zoom_width = prevX;
00695 mmx_zoom_size = prevX * prevY;
00696
00697 #ifdef USE_ASM
00698 #ifdef MMX
00699 if (zf_use_xmmx)
00700 zoom_filter_xmmx (prevX, prevY,expix1, expix2,
00701 brutS, brutD, buffratio, precalCoef);
00702 else if (zf_use_mmx)
00703 zoom_filter_mmx (prevX, prevY,expix1, expix2,
00704 brutS, brutD, buffratio, precalCoef);
00705 else c_zoom (expix1, expix2, prevX, prevY, brutS, brutD);
00706 #endif
00707
00708 #ifdef POWERPC
00709 if (useAltivec)
00710 {
00711 ppc_zoom (expix1, expix2, prevX, prevY, brutS, brutD, buffratio,precalCoef);
00712 }
00713 else
00714 ppc_zoom (expix1, expix2, prevX, prevY, brutS, brutD, buffratio,precalCoef);
00715 #endif
00716 #else
00717 c_zoom (expix1, expix2, prevX, prevY, brutS, brutD);
00718 #endif
00719 }
00720
00721 void
00722 pointFilter (Uint * pix1, Color c, float t1, float t2, float t3, float t4, Uint cycle)
00723 {
00724 Uint x = (Uint) ((int) (resolx/2) + (int) (t1 * cos ((float) cycle / t3)));
00725 Uint y = (Uint) ((int) (c_resoly/2) + (int) (t2 * sin ((float) cycle / t4)));
00726
00727 if ((x > 1) && (y > 1) && (x < resolx - 2) && (y < c_resoly - 2)) {
00728 setPixelRGB (pix1, x + 1, y, c);
00729 setPixelRGB (pix1, x, y + 1, c);
00730 setPixelRGB (pix1, x + 1, y + 1, WHITE);
00731 setPixelRGB (pix1, x + 2, y + 1, c);
00732 setPixelRGB (pix1, x + 1, y + 2, c);
00733 }
00734 }