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