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