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 24 Gradient::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 46 Gradient::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 95 Gradient::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 116 Gradient::~Gradient() 117 { 118 _MakeEmpty(); 119 } 120 121 #ifdef ICON_O_MATIC 122 // Archive 123 status_t 124 Gradient::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= 167 Gradient& 168 Gradient::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== 184 bool 185 Gradient::operator==(const Gradient& other) const 186 { 187 if (Transformable::operator==(other)) 188 return ColorStepsAreEqual(other); 189 return false; 190 } 191 192 // operator!= 193 bool 194 Gradient::operator!=(const Gradient& other) const 195 { 196 return !(*this == other); 197 } 198 199 // ColorStepsAreEqual 200 bool 201 Gradient::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 224 void 225 Gradient::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 241 int32 242 Gradient::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 262 bool 263 Gradient::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 275 bool 276 Gradient::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 291 bool 292 Gradient::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 306 bool 307 Gradient::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 320 bool 321 Gradient::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 335 int32 336 Gradient::CountColors() const 337 { 338 return fColors.CountItems(); 339 } 340 341 // ColorAt 342 BGradient::ColorStop* 343 Gradient::ColorAt(int32 index) const 344 { 345 return (BGradient::ColorStop*)fColors.ItemAt(index); 346 } 347 348 // ColorAtFast 349 BGradient::ColorStop* 350 Gradient::ColorAtFast(int32 index) const 351 { 352 return (BGradient::ColorStop*)fColors.ItemAtFast(index); 353 } 354 355 // #pragma mark - 356 357 // SetType 358 void 359 Gradient::SetType(gradients_type type) 360 { 361 if (fType != type) { 362 fType = type; 363 Notify(); 364 } 365 } 366 367 // SetInterpolation 368 void 369 Gradient::SetInterpolation(interpolation_type type) 370 { 371 if (fInterpolation != type) { 372 fInterpolation = type; 373 Notify(); 374 } 375 } 376 377 // SetInheritTransformation 378 void 379 Gradient::SetInheritTransformation(bool inherit) 380 { 381 if (fInheritTransformation != inherit) { 382 fInheritTransformation = inherit; 383 Notify(); 384 } 385 } 386 387 // #pragma mark - 388 389 // gauss 390 inline double 391 gauss(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 405 void 406 Gradient::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 521 void 522 Gradient::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 536 static const char* 537 string_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 557 static const char* 558 string_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 570 BRect 571 Gradient::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() 587 void 588 Gradient::TransformationChanged() 589 { 590 Notify(); 591 } 592 593 // PrintToStream 594 void 595 Gradient::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 614 void 615 Gradient::_MakeEmpty() 616 { 617 int32 count = CountColors(); 618 for (int32 i = 0; i < count; i++) 619 delete ColorAtFast(i); 620 fColors.MakeEmpty(); 621 } 622