1 /* 2 * Copyright (c) 1997 by Massimino Pascal <Pascal.Massimon@ens.fr> 3 * Copyright 2006-2014, Haiku, Inc. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 * 7 * Authors: 8 * Stephan Aßmus, superstippi@gmx.de 9 * Massimino Pascal, Pascal.Massimon@ens.fr 10 * John Scipione, jscipione@gmail.com 11 */ 12 13 /*! When shown ifs, Diana Rose (4 years old) said, "It looks like dancing." 14 */ 15 16 #include "IFS.h" 17 18 #include <new> 19 #include <malloc.h> 20 #include <stdio.h> 21 #include <string.h> 22 23 #include <Bitmap.h> 24 #include <OS.h> 25 #include <Screen.h> 26 #include <View.h> 27 28 #include <unistd.h> 29 // for getpid() 30 #include <sys/time.h> 31 // for gettimeofday() 32 33 34 #define HALF 0 35 #define random() ya_random() 36 #define RAND_MAX 0xFFFFFFFF 37 38 #define FLOAT_TO_INT(x) (int32)((float)(UNIT)*(x)) 39 40 #define LRAND() ((long) (random() & 0x7fffffff)) 41 #define NRAND(n) ((int) (LRAND() % (n))) 42 #define MAXRAND (2147483648.0) 43 // unsigned 1<<31 as a float 44 #define SRAND(n) 45 // already seeded by screenhack.c TODO: ?!? is it? 46 47 // The following 'random' numbers are taken from CRC, 18th Edition, page 622. 48 // Each array element was taken from the corresponding line in the table, 49 // except that a[0] was from line 100. 8s and 9s in the table were simply 50 // skipped. The high order digit was taken mod 4. 51 52 #define VECTOR_SIZE 55 53 54 static unsigned int a[VECTOR_SIZE] = { 55 035340171546, 010401501101, 022364657325, 024130436022, 002167303062, // 5 56 037570375137, 037210607110, 016272055420, 023011770546, 017143426366, // 10 57 014753657433, 021657231332, 023553406142, 004236526362, 010365611275, // 14 58 007117336710, 011051276551, 002362132524, 001011540233, 012162531646, // 20 59 007056762337, 006631245521, 014164542224, 032633236305, 023342700176, // 25 60 002433062234, 015257225043, 026762051606, 000742573230, 005366042132, // 30 61 012126416411, 000520471171, 000725646277, 020116577576, 025765742604, // 35 62 007633473735, 015674255275, 017555634041, 006503154145, 021576344247, // 40 63 014577627653, 002707523333, 034146376720, 030060227734, 013765414060, // 45 64 036072251540, 007255221037, 024364674123, 006200353166, 010126373326, // 50 65 015664104320, 016401041535, 016215305520, 033115351014, 017411670323 // 55 66 }; 67 68 69 static int i1; 70 static int i2; 71 72 73 unsigned int 74 ya_random(void) 75 { 76 register int ret = a[i1] + a[i2]; 77 a[i1] = ret; 78 if (++i1 >= VECTOR_SIZE) 79 i1 = 0; 80 81 if (++i2 >= VECTOR_SIZE) 82 i2 = 0; 83 84 return ret; 85 } 86 87 88 void 89 ya_rand_init(unsigned int seed) 90 { 91 int i; 92 if (seed == 0) { 93 struct timeval tp; 94 struct timezone tzp; 95 gettimeofday(&tp, &tzp); 96 // ignore overflow 97 seed = (999*tp.tv_sec) + (1001*tp.tv_usec) + (1003 * getpid()); 98 } 99 100 a[0] += seed; 101 for (i = 1; i < VECTOR_SIZE; i++) { 102 seed = a[i-1]*1001 + seed*999; 103 a[i] += seed; 104 } 105 106 i1 = a[0] % VECTOR_SIZE; 107 i2 = (i1 + 024) % VECTOR_SIZE; 108 } 109 110 111 112 static float 113 gauss_rand(float c, float A, float S) 114 { 115 float y = (float) LRAND() / MAXRAND; 116 y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S)); 117 if (NRAND(2)) 118 return (c + y); 119 120 return (c - y); 121 } 122 123 124 static float 125 half_gauss_rand(float c, float A, float S) 126 { 127 float y = (float) LRAND() / MAXRAND; 128 y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S)); 129 130 return (c + y); 131 } 132 133 134 inline void 135 transform(SIMILITUDE* Similitude, int32 xo, int32 yo, int32* x, int32* y) 136 { 137 int32 xx; 138 int32 yy; 139 140 xo = xo - Similitude->Cx; 141 xo = (xo * Similitude->R) / UNIT; 142 yo = yo - Similitude->Cy; 143 yo = (yo * Similitude->R) / UNIT; 144 145 xx = xo - Similitude->Cx; 146 xx = (xx * Similitude->R2) / UNIT; 147 yy = -yo - Similitude->Cy; 148 yy = (yy * Similitude->R2) / UNIT; 149 150 *x = ((xo * Similitude->Ct - yo * Similitude->St + xx * Similitude->Ct2 151 - yy * Similitude->St2) / UNIT) + Similitude->Cx; 152 *y = ((xo * Similitude->St + yo * Similitude->Ct + xx * Similitude->St2 153 + yy * Similitude->Ct2) / UNIT) + Similitude->Cy; 154 } 155 156 157 158 IFS::IFS(BRect bounds) 159 : 160 fRoot(NULL), 161 fCurrentFractal(NULL), 162 fPointBuffer(NULL), 163 fCurrentPoint(0), 164 fAdditive(false), 165 fCurrentMarkValue(1) 166 { 167 if (!bounds.IsValid()) 168 return; 169 170 ya_rand_init(system_time()); 171 172 int i; 173 FRACTAL* Fractal; 174 175 if (fRoot == NULL) { 176 fRoot = (FRACTAL*)calloc(1, sizeof(FRACTAL)); 177 if (fRoot == NULL) 178 return; 179 } 180 Fractal = fRoot; 181 182 _FreeBuffers(Fractal); 183 i = (NRAND(4)) + 2; 184 // Number of centers 185 switch (i) { 186 case 2: 187 default: 188 Fractal->Depth = fAdditive ? MAX_DEPTH_2 + 1 : MAX_DEPTH_2; 189 Fractal->r_mean = 0.7; 190 Fractal->dr_mean = 0.3; 191 Fractal->dr2_mean = 0.4; 192 break; 193 194 case 3: 195 Fractal->Depth = fAdditive ? MAX_DEPTH_3 + 1 : MAX_DEPTH_3; 196 Fractal->r_mean = 0.6; 197 Fractal->dr_mean = 0.4; 198 Fractal->dr2_mean = 0.3; 199 break; 200 201 case 4: 202 Fractal->Depth = MAX_DEPTH_4; 203 Fractal->r_mean = 0.5; 204 Fractal->dr_mean = 0.4; 205 Fractal->dr2_mean = 0.3; 206 break; 207 208 case 5: 209 Fractal->Depth = MAX_DEPTH_5; 210 Fractal->r_mean = 0.5; 211 Fractal->dr_mean = 0.4; 212 Fractal->dr2_mean = 0.3; 213 break; 214 } 215 216 Fractal->SimilitudeCount = i; 217 Fractal->MaxPoint = Fractal->SimilitudeCount - 1; 218 for (i = 0; i <= Fractal->Depth + 2; ++i) 219 Fractal->MaxPoint *= Fractal->SimilitudeCount; 220 221 if ((Fractal->buffer1 = (Point *)calloc(Fractal->MaxPoint, 222 sizeof(Point))) == NULL) { 223 _FreeIFS(Fractal); 224 return; 225 } 226 if ((Fractal->buffer2 = (Point *)calloc(Fractal->MaxPoint, 227 sizeof(Point))) == NULL) { 228 _FreeIFS(Fractal); 229 return; 230 } 231 Fractal->Speed = 6; 232 #if HALF 233 Fractal->Width = bounds.IntegerWidth() / 2 + 1; 234 Fractal->Height = bounds.IntegerHeight() / 2 + 1; 235 #else 236 Fractal->Width = bounds.IntegerWidth() + 1; 237 Fractal->Height = bounds.IntegerHeight() + 1; 238 #endif 239 Fractal->CurrentPoint = 0; 240 Fractal->Count = 0; 241 Fractal->Lx = (Fractal->Width - 1) / 2; 242 Fractal->Ly = (Fractal->Height - 1) / 2; 243 Fractal->Col = NRAND(Fractal->Width * Fractal->Height - 1) + 1; 244 245 _RandomSimilitudes(Fractal, Fractal->Components, 5 * MAX_SIMILITUDE); 246 247 delete Fractal->bitmap; 248 Fractal->bitmap = new BBitmap(BRect(0.0, 0.0, 249 Fractal->Width - 1, Fractal->Height - 1), 0, B_RGB32); 250 delete Fractal->markBitmap; 251 Fractal->markBitmap = new BBitmap(BRect(0.0, 0.0, 252 Fractal->Width - 1, Fractal->Height - 1), 0, B_GRAY8); 253 254 // allocation checked 255 if (Fractal->bitmap != NULL && Fractal->bitmap->IsValid()) 256 memset(Fractal->bitmap->Bits(), 0, Fractal->bitmap->BitsLength()); 257 else { 258 delete Fractal->bitmap; 259 Fractal->bitmap = NULL; 260 } 261 262 if (Fractal->markBitmap != NULL && Fractal->markBitmap->IsValid()) { 263 memset(Fractal->markBitmap->Bits(), 0, 264 Fractal->markBitmap->BitsLength()); 265 } else { 266 delete Fractal->markBitmap; 267 Fractal->markBitmap = NULL; 268 } 269 } 270 271 272 IFS::~IFS() 273 { 274 if (fRoot != NULL) { 275 _FreeIFS(fRoot); 276 free((void*)fRoot); 277 } 278 } 279 280 281 void 282 IFS::Draw(BView* view, const buffer_info* info, int32 frames) 283 { 284 int i; 285 float u; 286 float uu; 287 float v; 288 float vv; 289 float u0; 290 float u1; 291 float u2; 292 float u3; 293 SIMILITUDE* S; 294 SIMILITUDE* S1; 295 SIMILITUDE* S2; 296 SIMILITUDE* S3; 297 SIMILITUDE* S4; 298 FRACTAL* F; 299 300 if (fRoot == NULL) 301 return; 302 303 F = fRoot; 304 if (F->buffer1 == NULL) 305 return; 306 307 // do this as many times as necessary to calculate the missing frames 308 // so the animation doesn't jerk when we miss a few frames 309 for (int32 frame = 0; frame < frames; frame++) { 310 u = (float) (F->Count) * (float) (F->Speed) / 1000.0; 311 uu = u * u; 312 v = 1.0 - u; 313 vv = v * v; 314 u0 = vv * v; 315 u1 = 3.0 * vv * u; 316 u2 = 3.0 * v * uu; 317 u3 = u * uu; 318 319 S = F->Components; 320 S1 = S + F->SimilitudeCount; 321 S2 = S1 + F->SimilitudeCount; 322 S3 = S2 + F->SimilitudeCount; 323 S4 = S3 + F->SimilitudeCount; 324 325 for (i = F->SimilitudeCount; i; --i, S++, S1++, S2++, S3++, S4++) { 326 S->c_x = u0 * S1->c_x + u1 * S2->c_x + u2 * S3->c_x + u3 * S4->c_x; 327 S->c_y = u0 * S1->c_y + u1 * S2->c_y + u2 * S3->c_y + u3 * S4->c_y; 328 S->r = u0 * S1->r + u1 * S2->r + u2 * S3->r + u3 * S4->r; 329 S->r2 = u0 * S1->r2 + u1 * S2->r2 + u2 * S3->r2 + u3 * S4->r2; 330 S->A = u0 * S1->A + u1 * S2->A + u2 * S3->A + u3 * S4->A; 331 S->A2 = u0 * S1->A2 + u1 * S2->A2 + u2 * S3->A2 + u3 * S4->A2; 332 } 333 334 if (frame == frames - 1) 335 _DrawFractal(view, info); 336 337 if (F->Count >= 1000 / F->Speed) { 338 S = F->Components; 339 S1 = S + F->SimilitudeCount; 340 S2 = S1 + F->SimilitudeCount; 341 S3 = S2 + F->SimilitudeCount; 342 S4 = S3 + F->SimilitudeCount; 343 344 for (i = F->SimilitudeCount; i; --i, S++, S1++, S2++, S3++, S4++) { 345 S2->c_x = 2.0 * S4->c_x - S3->c_x; 346 S2->c_y = 2.0 * S4->c_y - S3->c_y; 347 S2->r = 2.0 * S4->r - S3->r; 348 S2->r2 = 2.0 * S4->r2 - S3->r2; 349 S2->A = 2.0 * S4->A - S3->A; 350 S2->A2 = 2.0 * S4->A2 - S3->A2; 351 352 *S1 = *S4; 353 } 354 _RandomSimilitudes(F, F->Components + 3 * F->SimilitudeCount, 355 F->SimilitudeCount); 356 _RandomSimilitudes(F, F->Components + 4 * F->SimilitudeCount, 357 F->SimilitudeCount); 358 359 F->Count = 0; 360 } else 361 F->Count++; 362 } 363 } 364 365 366 void 367 IFS::SetAdditive(bool additive) 368 { 369 fAdditive = additive; 370 } 371 372 373 void 374 IFS::SetSpeed(int32 speed) 375 { 376 if (fRoot && speed > 0 && speed <= 12) 377 fRoot->Speed = speed; 378 } 379 380 381 void 382 IFS::_DrawFractal(BView* view, const buffer_info* info) 383 { 384 FRACTAL* F = fRoot; 385 int i; 386 int j; 387 int32 x; 388 int32 y; 389 int32 xo; 390 int32 yo; 391 SIMILITUDE* Current; 392 SIMILITUDE* Similitude; 393 394 for (Current = F->Components, i = F->SimilitudeCount; i; --i, Current++) { 395 Current->Cx = FLOAT_TO_INT(Current->c_x); 396 Current->Cy = FLOAT_TO_INT(Current->c_y); 397 398 Current->Ct = FLOAT_TO_INT(cos(Current->A)); 399 Current->St = FLOAT_TO_INT(sin(Current->A)); 400 Current->Ct2 = FLOAT_TO_INT(cos(Current->A2)); 401 Current->St2 = FLOAT_TO_INT(sin(Current->A2)); 402 403 Current->R = FLOAT_TO_INT(Current->r); 404 Current->R2 = FLOAT_TO_INT(Current->r2); 405 } 406 407 fCurrentPoint = 0; 408 fCurrentFractal = F; 409 fPointBuffer = F->buffer2; 410 for (Current = F->Components, i = F->SimilitudeCount; i; --i, Current++) { 411 xo = Current->Cx; 412 yo = Current->Cy; 413 for (Similitude = F->Components, j = F->SimilitudeCount; j; 414 --j, Similitude++) { 415 if (Similitude == Current) 416 continue; 417 418 transform(Similitude, xo, yo, &x, &y); 419 _Trace(F, x, y); 420 } 421 } 422 423 if (F->bitmap != NULL && F->markBitmap != NULL) { 424 uint8* bits = (uint8*)F->bitmap->Bits(); 425 uint32 bpr = F->bitmap->BytesPerRow(); 426 uint8* markBits = (uint8*)F->markBitmap->Bits(); 427 uint32 markBPR = F->markBitmap->BytesPerRow(); 428 int32 minX = F->Width; 429 int32 minY = F->Height; 430 int32 maxX = 0; 431 int32 maxY = 0; 432 433 // Erase previous dots from bitmap, 434 // but only if we're not in BDirectWindow mode, 435 // since the dots will have been erased already 436 if (info == NULL) { 437 if (F->CurrentPoint) { 438 for (int32 i = 0; i < F->CurrentPoint; i++) { 439 Point p = F->buffer1[i]; 440 if (p.x >= 0 && p.x < F->Width 441 && p.y >= 0 && p.y < F->Height) { 442 int32 offset = bpr * p.y + p.x * 4; 443 *(uint32*)&bits[offset] = 0; 444 if (minX > p.x) 445 minX = p.x; 446 447 if (minY > p.y) 448 minY = p.y; 449 450 if (maxX < p.x) 451 maxX = p.x; 452 453 if (maxY < p.y) 454 maxY = p.y; 455 } 456 } 457 } 458 } 459 460 // draw the new dots into the bitmap 461 if (fCurrentPoint != 0) { 462 if (info != NULL) { 463 for (int32 i = 0; i < fCurrentPoint; i++) { 464 Point p = F->buffer2[i]; 465 if (p.x >= 0 && p.x < F->Width 466 && p.y >= 0 && p.y < F->Height) { 467 int32 offset = bpr * p.y + p.x * 4; 468 if (fAdditive) { 469 if (bits[offset + 0] < 255) { 470 bits[offset + 0] += 51; 471 bits[offset + 1] += 51; 472 bits[offset + 2] += 51; 473 } 474 } else 475 *(uint32*)&bits[offset] = 0xffffffff; 476 } 477 } 478 } else { 479 // in this version, remember the bounds rectangle 480 for (int32 i = 0; i < fCurrentPoint; i++) { 481 Point p = F->buffer2[i]; 482 if (p.x >= 0 && p.x < F->Width 483 && p.y >= 0 && p.y < F->Height) { 484 int32 offset = bpr * p.y + p.x * 4; 485 if (fAdditive) { 486 if (bits[offset + 0] < 255) { 487 bits[offset + 0] += 15; 488 bits[offset + 1] += 15; 489 bits[offset + 2] += 15; 490 } 491 } else 492 *(uint32*)&bits[offset] = 0xffffffff; 493 494 if (minX > p.x) 495 minX = p.x; 496 497 if (minY > p.y) 498 minY = p.y; 499 500 if (maxX < p.x) 501 maxX = p.x; 502 503 if (maxY < p.y) 504 maxY = p.y; 505 } 506 } 507 } 508 } 509 510 if (info != NULL && info->bits != NULL) { 511 uint8* screenBits = (uint8*)info->bits; 512 uint32 screenBPR = info->bytesPerRow; 513 int32 left = info->bounds.left; 514 int32 top = info->bounds.top; 515 int32 bpp = info->bits_per_pixel; 516 screenBits += left * bpp + top * bpr; 517 518 int32 screenWidth = info->bounds.right - left; 519 int32 screenHeight = info->bounds.bottom - top; 520 521 // redraw the previous points on screen 522 // with the contents of the current bitmap 523 // 524 // draw the new points, erasing the bitmap as we go 525 int32 maxPoints = max_c(F->CurrentPoint, fCurrentPoint); 526 if (maxPoints > 0) { 527 for (int32 i = 0; i < maxPoints; i++) { 528 // copy previous points (black) 529 if (i < F->CurrentPoint) { 530 Point p = F->buffer1[i]; 531 if (p.x >= 0 && p.x < F->Width && p.x < screenWidth 532 && p.y >= 0 && p.y < F->Height 533 && p.y < screenHeight) { 534 int32 markOffset = markBPR * p.y + p.x; 535 if (markBits[markOffset] != fCurrentMarkValue) { 536 int32 offset = bpr * p.y + p.x * 4; 537 // copy the pixel to the screen 538 uint32* src = (uint32*)&bits[offset]; 539 if (bpp == 32) { 540 int32 screenOffset = screenBPR * p.y 541 + p.x * 4; 542 *(uint32*)&screenBits[screenOffset] = *src; 543 } else if (bpp == 16) { 544 int32 screenOffset = screenBPR * p.y 545 + p.x * 2; 546 *(uint16*)&screenBits[screenOffset] = 547 (uint16)(((bits[offset + 2] & 0xf8) 548 << 8) 549 | ((bits[offset + 1] & 0xfc) << 3) 550 | (bits[offset] >> 3)); 551 } else if (bpp == 15) { 552 int32 screenOffset = screenBPR * p.y 553 + p.x * 2; 554 *(uint16*)&screenBits[screenOffset] = 555 (uint16)(((bits[offset + 2] & 0xf8) 556 << 7) 557 | ((bits[offset + 1] & 0xf8) << 2) 558 | (bits[offset] >> 3)); 559 } else if (bpp == 8) { 560 int32 screenOffset = screenBPR * p.y + p.x; 561 screenBits[screenOffset] = bits[offset]; 562 } 563 *src = 0; 564 markBits[markOffset] = fCurrentMarkValue; 565 } 566 // else it means the pixel has been copied already 567 } 568 } 569 570 // copy current points (white) and erase them from the 571 // bitmap 572 if (i < fCurrentPoint) { 573 Point p = F->buffer2[i]; 574 if (p.x >= 0 && p.x < F->Width && p.x < screenWidth 575 && p.y >= 0 && p.y < F->Height 576 && p.y < screenHeight) { 577 int32 markOffset = markBPR * p.y + p.x; 578 int32 offset = bpr * p.y + p.x * 4; 579 580 // copy the pixel to the screen 581 uint32* src = (uint32*)&bits[offset]; 582 if (markBits[markOffset] != fCurrentMarkValue) { 583 if (bpp == 32) { 584 int32 screenOffset = screenBPR * p.y 585 + p.x * 4; 586 *(uint32*)&screenBits[screenOffset] = *src; 587 } else if (bpp == 16) { 588 int32 screenOffset = screenBPR * p.y 589 + p.x * 2; 590 *(uint16*)&screenBits[screenOffset] = 591 (uint16)(((bits[offset + 2] & 0xf8) 592 << 8) 593 | ((bits[offset + 1] & 0xfc) << 3) 594 | (bits[offset] >> 3)); 595 } else if (bpp == 15) { 596 int32 screenOffset = screenBPR * p.y 597 + p.x * 2; 598 *(uint16*)&screenBits[screenOffset] = 599 (uint16)(((bits[offset + 2] & 0xf8) 600 << 7) 601 | ((bits[offset + 1] & 0xf8) << 2) 602 | (bits[offset] >> 3)); 603 } else if (bpp == 1) { 604 int32 screenOffset = screenBPR * p.y + p.x; 605 screenBits[screenOffset] = bits[offset]; 606 } 607 markBits[markOffset] = fCurrentMarkValue; 608 } 609 // else it means the pixel has been copied already 610 *src = 0; 611 } 612 } 613 } 614 } 615 } else { 616 // if not in BDirectWindow mode, draw the bitmap 617 BRect b(minX, minY, maxX, maxY); 618 view->DrawBitmapAsync(F->bitmap, b, b); 619 } 620 } 621 622 // flip buffers 623 F->CurrentPoint = fCurrentPoint; 624 fPointBuffer = F->buffer1; 625 F->buffer1 = F->buffer2; 626 F->buffer2 = fPointBuffer; 627 628 if (fCurrentMarkValue == 255) 629 fCurrentMarkValue = 0; 630 else 631 fCurrentMarkValue++; 632 } 633 634 635 void 636 IFS::_Trace(FRACTAL* F, int32 xo, int32 yo) 637 { 638 int32 x; 639 int32 y; 640 SIMILITUDE* Current; 641 642 Current = fCurrentFractal->Components; 643 for (int32 i = fCurrentFractal->SimilitudeCount; i; --i, Current++) { 644 transform(Current, xo, yo, &x, &y); 645 fPointBuffer->x = (UNIT * 2 + x) * F->Lx / (UNIT * 2); 646 fPointBuffer->y = (UNIT * 2 - y) * F->Ly / (UNIT * 2); 647 fPointBuffer++; 648 fCurrentPoint++; 649 650 if (F->Depth && ((x - xo) >> 4) && ((y - yo) >> 4)) { 651 F->Depth--; 652 _Trace(F, x, y); 653 F->Depth++; 654 } 655 } 656 } 657 658 659 void 660 IFS::_RandomSimilitudes(FRACTAL* fractal, SIMILITUDE* current, int i) const 661 { 662 while (i-- > 0) { 663 current->c_x = gauss_rand(0.0, .8, 4.0); 664 current->c_y = gauss_rand(0.0, .8, 4.0); 665 current->r = gauss_rand(fractal->r_mean, fractal->dr_mean, 3.0); 666 current->r2 = half_gauss_rand(0.0,fractal->dr2_mean, 2.0); 667 current->A = gauss_rand(0.0, 360.0, 4.0) * (M_PI / 180.0); 668 current->A2 = gauss_rand(0.0, 360.0, 4.0) * (M_PI / 180.0); 669 current++; 670 } 671 } 672 673 674 void 675 IFS::_FreeBuffers(FRACTAL* f) 676 { 677 if (f->buffer1) { 678 free((void*)f->buffer1); 679 f->buffer1 = (Point*)NULL; 680 } 681 682 if (f->buffer2) { 683 free((void*)f->buffer2); 684 f->buffer2 = (Point*)NULL; 685 } 686 } 687 688 689 void 690 IFS::_FreeIFS(FRACTAL* f) 691 { 692 _FreeBuffers(f); 693 delete f->bitmap; 694 f->bitmap = NULL; 695 delete f->markBitmap; 696 f->markBitmap = NULL; 697 } 698