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