1 /* 2 * Copyright 2006, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 #include "GradientTransformable.h" 10 11 #include <math.h> 12 #include <stdio.h> 13 14 #include <Message.h> 15 16 #ifdef ICON_O_MATIC 17 # include "support.h" 18 #endif 19 20 // constructor 21 Gradient::Gradient(bool empty) 22 #ifdef ICON_O_MATIC 23 : BArchivable(), 24 Observable(), 25 BReferenceable(), 26 Transformable(), 27 #else 28 : Transformable(), 29 #endif 30 31 fColors(4), 32 fType(GRADIENT_LINEAR), 33 fInterpolation(INTERPOLATION_SMOOTH), 34 fInheritTransformation(true) 35 { 36 if (!empty) { 37 AddColor(BGradient::ColorStop(0, 0, 0, 255, 0.0), 0); 38 AddColor(BGradient::ColorStop(255, 255, 255, 255, 1.0), 1); 39 } 40 } 41 42 // constructor 43 Gradient::Gradient(BMessage* archive) 44 #ifdef ICON_O_MATIC 45 : BArchivable(archive), 46 Observable(), 47 BReferenceable(), 48 Transformable(), 49 #else 50 : Transformable(), 51 #endif 52 53 fColors(4), 54 fType(GRADIENT_LINEAR), 55 fInterpolation(INTERPOLATION_SMOOTH), 56 fInheritTransformation(true) 57 { 58 if (!archive) 59 return; 60 61 // read transformation 62 int32 size = Transformable::matrix_size; 63 const void* matrix; 64 ssize_t dataSize = size * sizeof(double); 65 if (archive->FindData("transformation", B_DOUBLE_TYPE, 66 &matrix, &dataSize) == B_OK 67 && dataSize == (ssize_t)(size * sizeof(double))) 68 LoadFrom((const double*)matrix); 69 70 // color steps 71 BGradient::ColorStop step; 72 for (int32 i = 0; archive->FindFloat("offset", i, &step.offset) >= B_OK; i++) { 73 if (archive->FindInt32("color", i, (int32*)&step.color) >= B_OK) 74 AddColor(step, i); 75 else 76 break; 77 } 78 if (archive->FindInt32("type", (int32*)&fType) < B_OK) 79 fType = GRADIENT_LINEAR; 80 81 if (archive->FindInt32("interpolation", (int32*)&fInterpolation) < B_OK) 82 fInterpolation = INTERPOLATION_SMOOTH; 83 84 if (archive->FindBool("inherit transformation", 85 &fInheritTransformation) < B_OK) 86 fInheritTransformation = true; 87 } 88 89 // constructor 90 Gradient::Gradient(const Gradient& other) 91 #ifdef ICON_O_MATIC 92 : BArchivable(other), 93 Observable(), 94 BReferenceable(), 95 Transformable(other), 96 #else 97 : Transformable(other), 98 #endif 99 100 fColors(4), 101 fType(other.fType), 102 fInterpolation(other.fInterpolation), 103 fInheritTransformation(other.fInheritTransformation) 104 { 105 for (int32 i = 0; BGradient::ColorStop* step = other.ColorAt(i); i++) { 106 AddColor(*step, i); 107 } 108 } 109 110 // destructor 111 Gradient::~Gradient() 112 { 113 _MakeEmpty(); 114 } 115 116 #ifdef ICON_O_MATIC 117 // Archive 118 status_t 119 Gradient::Archive(BMessage* into, bool deep) const 120 { 121 status_t ret = BArchivable::Archive(into, deep); 122 123 // transformation 124 if (ret == B_OK) { 125 int32 size = Transformable::matrix_size; 126 double matrix[size]; 127 StoreTo(matrix); 128 ret = into->AddData("transformation", B_DOUBLE_TYPE, 129 matrix, size * sizeof(double)); 130 } 131 132 // color steps 133 if (ret >= B_OK) { 134 for (int32 i = 0; BGradient::ColorStop* step = ColorAt(i); i++) { 135 ret = into->AddInt32("color", (const uint32&)step->color); 136 if (ret < B_OK) 137 break; 138 ret = into->AddFloat("offset", step->offset); 139 if (ret < B_OK) 140 break; 141 } 142 } 143 // gradient and interpolation type 144 if (ret >= B_OK) 145 ret = into->AddInt32("type", (int32)fType); 146 if (ret >= B_OK) 147 ret = into->AddInt32("interpolation", (int32)fInterpolation); 148 if (ret >= B_OK) 149 ret = into->AddBool("inherit transformation", fInheritTransformation); 150 151 // finish off 152 if (ret >= B_OK) 153 ret = into->AddString("class", "Gradient"); 154 155 return ret; 156 } 157 #endif // ICON_O_MATIC 158 159 // #pragma mark - 160 161 // operator= 162 Gradient& 163 Gradient::operator=(const Gradient& other) 164 { 165 #ifdef ICON_O_MATIC 166 AutoNotificationSuspender _(this); 167 #endif 168 169 SetTransform(other); 170 SetColors(other); 171 SetType(other.fType); 172 SetInterpolation(other.fInterpolation); 173 SetInheritTransformation(other.fInheritTransformation); 174 175 return *this; 176 } 177 178 // operator== 179 bool 180 Gradient::operator==(const Gradient& other) const 181 { 182 if (Transformable::operator==(other)) 183 return ColorStepsAreEqual(other); 184 return false; 185 } 186 187 // operator!= 188 bool 189 Gradient::operator!=(const Gradient& other) const 190 { 191 return !(*this == other); 192 } 193 194 // ColorStepsAreEqual 195 bool 196 Gradient::ColorStepsAreEqual(const Gradient& other) const 197 { 198 int32 count = CountColors(); 199 if (count == other.CountColors() && 200 fType == other.fType && 201 fInterpolation == other.fInterpolation && 202 fInheritTransformation == other.fInheritTransformation) { 203 204 bool equal = true; 205 for (int32 i = 0; i < count; i++) { 206 BGradient::ColorStop* ourStep = ColorAtFast(i); 207 BGradient::ColorStop* otherStep = other.ColorAtFast(i); 208 if (*ourStep != *otherStep) { 209 equal = false; 210 break; 211 } 212 } 213 return equal; 214 } 215 return false; 216 } 217 218 // SetColors 219 void 220 Gradient::SetColors(const Gradient& other) 221 { 222 #ifdef ICON_O_MATIC 223 AutoNotificationSuspender _(this); 224 #endif 225 226 _MakeEmpty(); 227 for (int32 i = 0; BGradient::ColorStop* step = other.ColorAt(i); i++) 228 AddColor(*step, i); 229 230 Notify(); 231 } 232 233 // #pragma mark - 234 235 // AddColor 236 int32 237 Gradient::AddColor(const rgb_color& color, float offset) 238 { 239 // find the correct index (sorted by offset) 240 BGradient::ColorStop* step = new BGradient::ColorStop(color, offset); 241 int32 index = 0; 242 int32 count = CountColors(); 243 for (; index < count; index++) { 244 BGradient::ColorStop* s = ColorAtFast(index); 245 if (s->offset > step->offset) 246 break; 247 } 248 if (!fColors.AddItem((void*)step, index)) { 249 delete step; 250 return -1; 251 } 252 Notify(); 253 return index; 254 } 255 256 // AddColor 257 bool 258 Gradient::AddColor(const BGradient::ColorStop& color, int32 index) 259 { 260 BGradient::ColorStop* step = new BGradient::ColorStop(color); 261 if (!fColors.AddItem((void*)step, index)) { 262 delete step; 263 return false; 264 } 265 Notify(); 266 return true; 267 } 268 269 // RemoveColor 270 bool 271 Gradient::RemoveColor(int32 index) 272 { 273 BGradient::ColorStop* step 274 = (BGradient::ColorStop*)fColors.RemoveItem(index); 275 if (!step) { 276 return false; 277 } 278 delete step; 279 Notify(); 280 return true; 281 } 282 283 // #pragma mark - 284 285 // SetColor 286 bool 287 Gradient::SetColor(int32 index, const BGradient::ColorStop& color) 288 { 289 if (BGradient::ColorStop* step = ColorAt(index)) { 290 if (*step != color) { 291 step->color = color.color; 292 step->offset = color.offset; 293 Notify(); 294 return true; 295 } 296 } 297 return false; 298 } 299 300 // SetColor 301 bool 302 Gradient::SetColor(int32 index, const rgb_color& color) 303 { 304 if (BGradient::ColorStop* step = ColorAt(index)) { 305 if ((uint32&)step->color != (uint32&)color) { 306 step->color = color; 307 Notify(); 308 return true; 309 } 310 } 311 return false; 312 } 313 314 // SetOffset 315 bool 316 Gradient::SetOffset(int32 index, float offset) 317 { 318 BGradient::ColorStop* step = ColorAt(index); 319 if (step && step->offset != offset) { 320 step->offset = offset; 321 Notify(); 322 return true; 323 } 324 return false; 325 } 326 327 // #pragma mark - 328 329 // CountColors 330 int32 331 Gradient::CountColors() const 332 { 333 return fColors.CountItems(); 334 } 335 336 // ColorAt 337 BGradient::ColorStop* 338 Gradient::ColorAt(int32 index) const 339 { 340 return (BGradient::ColorStop*)fColors.ItemAt(index); 341 } 342 343 // ColorAtFast 344 BGradient::ColorStop* 345 Gradient::ColorAtFast(int32 index) const 346 { 347 return (BGradient::ColorStop*)fColors.ItemAtFast(index); 348 } 349 350 // #pragma mark - 351 352 // SetType 353 void 354 Gradient::SetType(gradients_type type) 355 { 356 if (fType != type) { 357 fType = type; 358 Notify(); 359 } 360 } 361 362 // SetInterpolation 363 void 364 Gradient::SetInterpolation(interpolation_type type) 365 { 366 if (fInterpolation != type) { 367 fInterpolation = type; 368 Notify(); 369 } 370 } 371 372 // SetInheritTransformation 373 void 374 Gradient::SetInheritTransformation(bool inherit) 375 { 376 if (fInheritTransformation != inherit) { 377 fInheritTransformation = inherit; 378 Notify(); 379 } 380 } 381 382 // #pragma mark - 383 384 // gauss 385 inline double 386 gauss(double f) 387 { 388 // this aint' a real gauss function 389 if (f > 0.0) { 390 if (f < 0.5) 391 return (1.0 - 2.0 * f*f); 392 393 f = 1.0 - f; 394 return (2.0 * f*f); 395 } 396 return 1.0; 397 } 398 399 // MakeGradient 400 void 401 Gradient::MakeGradient(uint32* colors, int32 count) const 402 { 403 BGradient::ColorStop* from = ColorAt(0); 404 405 if (!from) 406 return; 407 408 // find the step with the lowest offset 409 for (int32 i = 0; BGradient::ColorStop* step = ColorAt(i); i++) { 410 if (step->offset < from->offset) 411 from = step; 412 } 413 414 // current index into "colors" array 415 int32 index = (int32)floorf(count * from->offset + 0.5); 416 if (index < 0) 417 index = 0; 418 if (index > count) 419 index = count; 420 // make sure we fill the entire array 421 if (index > 0) { 422 uint8* c = (uint8*)&colors[0]; 423 for (int32 i = 0; i < index; i++) { 424 c[0] = from->color.red; 425 c[1] = from->color.green; 426 c[2] = from->color.blue; 427 c[3] = from->color.alpha; 428 c += 4; 429 } 430 } 431 432 // put all steps that we need to interpolate to into a list 433 BList nextSteps(fColors.CountItems() - 1); 434 for (int32 i = 0; BGradient::ColorStop* step = ColorAt(i); i++) { 435 if (step != from) 436 nextSteps.AddItem((void*)step); 437 } 438 439 // interpolate "from" to "to" 440 while (!nextSteps.IsEmpty()) { 441 442 // find the step with the next offset 443 BGradient::ColorStop* to = NULL; 444 float nextOffsetDist = 2.0; 445 for (int32 i = 0; BGradient::ColorStop* step 446 = (BGradient::ColorStop*)nextSteps.ItemAt(i); i++) { 447 float d = step->offset - from->offset; 448 if (d < nextOffsetDist && d >= 0) { 449 to = step; 450 nextOffsetDist = d; 451 } 452 } 453 if (!to) 454 break; 455 456 nextSteps.RemoveItem((void*)to); 457 458 // interpolate 459 int32 offset = (int32)floorf((count - 1) * to->offset + 0.5); 460 if (offset >= count) 461 offset = count - 1; 462 int32 dist = offset - index; 463 if (dist >= 0) { 464 uint8* c = (uint8*)&colors[index]; 465 #if GAMMA_BLEND 466 uint16 fromRed = kGammaTable[from->color.red]; 467 uint16 fromGreen = kGammaTable[from->color.green]; 468 uint16 fromBlue = kGammaTable[from->color.blue]; 469 uint16 toRed = kGammaTable[to->color.red]; 470 uint16 toGreen = kGammaTable[to->color.green]; 471 uint16 toBlue = kGammaTable[to->color.blue]; 472 473 for (int32 i = index; i <= offset; i++) { 474 float f = (float)(offset - i) / (float)(dist + 1); 475 if (fInterpolation == INTERPOLATION_SMOOTH) 476 f = gauss(1.0 - f); 477 float t = 1.0 - f; 478 c[0] = kInverseGammaTable[(uint16)floor(fromBlue * f + toBlue * t + 0.5)]; 479 c[1] = kInverseGammaTable[(uint16)floor(fromGreen * f + toGreen * t + 0.5)]; 480 c[2] = kInverseGammaTable[(uint16)floor(fromRed * f + toRed * t + 0.5)]; 481 c[3] = (uint8)floor(from->color.alpha * f + to->color.alpha * t + 0.5); 482 c += 4; 483 } 484 #else // GAMMA_BLEND 485 for (int32 i = index; i <= offset; i++) { 486 float f = (float)(offset - i) / (float)(dist + 1); 487 if (fInterpolation == INTERPOLATION_SMOOTH) 488 f = gauss(1.0 - f); 489 float t = 1.0 - f; 490 c[0] = (uint8)floor(from->color.red * f + to->color.red * t + 0.5); 491 c[1] = (uint8)floor(from->color.green * f + to->color.green * t + 0.5); 492 c[2] = (uint8)floor(from->color.blue * f + to->color.blue * t + 0.5); 493 c[3] = (uint8)floor(from->color.alpha * f + to->color.alpha * t + 0.5); 494 c += 4; 495 } 496 #endif // GAMMA_BLEND 497 } 498 index = offset + 1; 499 // the current "to" will be the "from" in the next interpolation 500 from = to; 501 } 502 // make sure we fill the entire array 503 if (index < count) { 504 uint8* c = (uint8*)&colors[index]; 505 for (int32 i = index; i < count; i++) { 506 c[0] = from->color.red; 507 c[1] = from->color.green; 508 c[2] = from->color.blue; 509 c[3] = from->color.alpha; 510 c += 4; 511 } 512 } 513 } 514 515 // FitToBounds 516 void 517 Gradient::FitToBounds(const BRect& bounds) 518 { 519 double parl[6]; 520 parl[0] = bounds.left; 521 parl[1] = bounds.top; 522 parl[2] = bounds.right; 523 parl[3] = bounds.top; 524 parl[4] = bounds.right; 525 parl[5] = bounds.bottom; 526 agg::trans_affine transform(-200.0, -200.0, 200.0, 200.0, parl); 527 multiply(transform); 528 } 529 530 // string_for_type 531 static const char* 532 string_for_type(gradients_type type) 533 { 534 switch (type) { 535 case GRADIENT_LINEAR: 536 return "GRADIENT_LINEAR"; 537 case GRADIENT_CIRCULAR: 538 return "GRADIENT_CIRCULAR"; 539 case GRADIENT_DIAMOND: 540 return "GRADIENT_DIAMOND"; 541 case GRADIENT_CONIC: 542 return "GRADIENT_CONIC"; 543 case GRADIENT_XY: 544 return "GRADIENT_XY"; 545 case GRADIENT_SQRT_XY: 546 return "GRADIENT_SQRT_XY"; 547 } 548 return "<unkown>"; 549 } 550 551 //string_for_interpolation 552 static const char* 553 string_for_interpolation(interpolation_type type) 554 { 555 switch (type) { 556 case INTERPOLATION_LINEAR: 557 return "INTERPOLATION_LINEAR"; 558 case INTERPOLATION_SMOOTH: 559 return "INTERPOLATION_SMOOTH"; 560 } 561 return "<unkown>"; 562 } 563 564 // GradientArea 565 BRect 566 Gradient::GradientArea() const 567 { 568 BRect area(0, 0, 64, 64); 569 switch (fType) { 570 case GRADIENT_LINEAR: 571 case GRADIENT_CIRCULAR: 572 case GRADIENT_DIAMOND: 573 case GRADIENT_CONIC: 574 case GRADIENT_XY: 575 case GRADIENT_SQRT_XY: 576 break; 577 } 578 return area; 579 } 580 581 // TransformationChanged() 582 void 583 Gradient::TransformationChanged() 584 { 585 Notify(); 586 } 587 588 // PrintToStream 589 void 590 Gradient::PrintToStream() const 591 { 592 printf("Gradient: type: %s, interpolation: %s, inherits transform: %d\n", 593 string_for_type(fType), 594 string_for_interpolation(fInterpolation), 595 fInheritTransformation); 596 for (int32 i = 0; BGradient::ColorStop* step = ColorAt(i); i++) { 597 printf(" %" B_PRId32 ": offset: %.1f -> color(%d, %d, %d, %d)\n", 598 i, step->offset, 599 step->color.red, 600 step->color.green, 601 step->color.blue, 602 step->color.alpha); 603 } 604 605 Transformable::PrintToStream(); 606 } 607 608 // _MakeEmpty 609 void 610 Gradient::_MakeEmpty() 611 { 612 int32 count = CountColors(); 613 for (int32 i = 0; i < count; i++) 614 delete ColorAtFast(i); 615 fColors.MakeEmpty(); 616 } 617