1 /* 2 * Copyright 2006-2009, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 * Artur Wyszynski <harakash@gmail.com> 8 */ 9 10 #include "Gradient.h" 11 12 #include <algorithm> 13 #include <math.h> 14 #include <stdio.h> 15 16 #include <DataIO.h> 17 #include <Message.h> 18 19 #include <AutoDeleter.h> 20 #include <GradientLinear.h> 21 #include <GradientRadial.h> 22 #include <GradientRadialFocus.h> 23 #include <GradientDiamond.h> 24 #include <GradientConic.h> 25 26 27 // constructor 28 BGradient::ColorStop::ColorStop(const rgb_color c, float o) 29 { 30 color.red = c.red; 31 color.green = c.green; 32 color.blue = c.blue; 33 color.alpha = c.alpha; 34 offset = o; 35 } 36 37 38 // constructor 39 BGradient::ColorStop::ColorStop(uint8 r, uint8 g, uint8 b, uint8 a, float o) 40 { 41 color.red = r; 42 color.green = g; 43 color.blue = b; 44 color.alpha = a; 45 offset = o; 46 } 47 48 49 // constructor 50 BGradient::ColorStop::ColorStop(const ColorStop& other) 51 { 52 color.red = other.color.red; 53 color.green = other.color.green; 54 color.blue = other.color.blue; 55 color.alpha = other.color.alpha; 56 offset = other.offset; 57 } 58 59 60 // constructor 61 BGradient::ColorStop::ColorStop() 62 { 63 color.red = 0; 64 color.green = 0; 65 color.blue = 0; 66 color.alpha = 255; 67 offset = 0; 68 } 69 70 71 // operator!= 72 bool 73 BGradient::ColorStop::operator!=(const ColorStop& other) const 74 { 75 return color.red != other.color.red || 76 color.green != other.color.green || 77 color.blue != other.color.blue || 78 color.alpha != other.color.alpha || 79 offset != other.offset; 80 } 81 82 83 static bool 84 sort_color_stops_by_offset(const BGradient::ColorStop* left, 85 const BGradient::ColorStop* right) 86 { 87 return left->offset < right->offset; 88 } 89 90 91 // #pragma mark - 92 93 94 // constructor 95 BGradient::BGradient() 96 : BArchivable(), 97 fColorStops(4), 98 fType(TYPE_NONE) 99 { 100 } 101 102 103 BGradient::BGradient(const BGradient& other) 104 : BArchivable(), 105 fColorStops(std::max((int32)4, other.CountColorStops())) 106 { 107 *this = other; 108 } 109 110 111 // constructor 112 BGradient::BGradient(BMessage* archive) 113 : BArchivable(archive), 114 fColorStops(4), 115 fType(TYPE_NONE) 116 { 117 if (!archive) 118 return; 119 120 // color stops 121 ColorStop stop; 122 for (int32 i = 0; archive->FindFloat("offset", i, &stop.offset) >= B_OK; i++) { 123 if (archive->FindInt32("color", i, (int32*)&stop.color) >= B_OK) 124 AddColorStop(stop, i); 125 else 126 break; 127 } 128 if (archive->FindInt32("type", (int32*)&fType) < B_OK) 129 fType = TYPE_LINEAR; 130 131 // linear 132 if (archive->FindFloat("linear_x1", (float*)&fData.linear.x1) < B_OK) 133 fData.linear.x1 = 0.0f; 134 if (archive->FindFloat("linear_y1", (float*)&fData.linear.y1) < B_OK) 135 fData.linear.y1 = 0.0f; 136 if (archive->FindFloat("linear_x2", (float*)&fData.linear.x2) < B_OK) 137 fData.linear.x2 = 0.0f; 138 if (archive->FindFloat("linear_y2", (float*)&fData.linear.y2) < B_OK) 139 fData.linear.y2 = 0.0f; 140 141 // radial 142 if (archive->FindFloat("radial_cx", (float*)&fData.radial.cx) < B_OK) 143 fData.radial.cx = 0.0f; 144 if (archive->FindFloat("radial_cy", (float*)&fData.radial.cy) < B_OK) 145 fData.radial.cy = 0.0f; 146 if (archive->FindFloat("radial_radius", (float*)&fData.radial.radius) < B_OK) 147 fData.radial.radius = 0.0f; 148 149 // radial focus 150 if (archive->FindFloat("radial_f_cx", (float*)&fData.radial_focus.cx) < B_OK) 151 fData.radial_focus.cx = 0.0f; 152 if (archive->FindFloat("radial_f_cy", (float*)&fData.radial_focus.cy) < B_OK) 153 fData.radial_focus.cy = 0.0f; 154 if (archive->FindFloat("radial_f_fx", (float*)&fData.radial_focus.fx) < B_OK) 155 fData.radial_focus.fx = 0.0f; 156 if (archive->FindFloat("radial_f_fy", (float*)&fData.radial_focus.fy) < B_OK) 157 fData.radial_focus.fy = 0.0f; 158 if (archive->FindFloat("radial_f_radius", (float*)&fData.radial_focus.radius) < B_OK) 159 fData.radial_focus.radius = 0.0f; 160 161 // diamond 162 if (archive->FindFloat("diamond_cx", (float*)&fData.diamond.cx) < B_OK) 163 fData.diamond.cx = 0.0f; 164 if (archive->FindFloat("diamond_cy", (float*)&fData.diamond.cy) < B_OK) 165 fData.diamond.cy = 0.0f; 166 167 // conic 168 if (archive->FindFloat("conic_cx", (float*)&fData.conic.cx) < B_OK) 169 fData.conic.cx = 0.0f; 170 if (archive->FindFloat("conic_cy", (float*)&fData.conic.cy) < B_OK) 171 fData.conic.cy = 0.0f; 172 if (archive->FindFloat("conic_angle", (float*)&fData.conic.angle) < B_OK) 173 fData.conic.angle = 0.0f; 174 } 175 176 177 // destructor 178 BGradient::~BGradient() 179 { 180 MakeEmpty(); 181 } 182 183 184 // Archive 185 status_t 186 BGradient::Archive(BMessage* into, bool deep) const 187 { 188 status_t ret = BArchivable::Archive(into, deep); 189 190 // color steps 191 if (ret >= B_OK) { 192 for (int32 i = 0; ColorStop* stop = ColorStopAt(i); i++) { 193 ret = into->AddInt32("color", (const uint32&)stop->color); 194 if (ret < B_OK) 195 break; 196 ret = into->AddFloat("offset", stop->offset); 197 if (ret < B_OK) 198 break; 199 } 200 } 201 // gradient type 202 if (ret >= B_OK) 203 ret = into->AddInt32("type", (int32)fType); 204 205 // linear 206 if (ret >= B_OK) 207 ret = into->AddFloat("linear_x1", (float)fData.linear.x1); 208 if (ret >= B_OK) 209 ret = into->AddFloat("linear_y1", (float)fData.linear.y1); 210 if (ret >= B_OK) 211 ret = into->AddFloat("linear_x2", (float)fData.linear.x2); 212 if (ret >= B_OK) 213 ret = into->AddFloat("linear_y2", (float)fData.linear.y2); 214 215 // radial 216 if (ret >= B_OK) 217 ret = into->AddFloat("radial_cx", (float)fData.radial.cx); 218 if (ret >= B_OK) 219 ret = into->AddFloat("radial_cy", (float)fData.radial.cy); 220 if (ret >= B_OK) 221 ret = into->AddFloat("radial_radius", (float)fData.radial.radius); 222 223 // radial focus 224 if (ret >= B_OK) 225 ret = into->AddFloat("radial_f_cx", (float)fData.radial_focus.cx); 226 if (ret >= B_OK) 227 ret = into->AddFloat("radial_f_cy", (float)fData.radial_focus.cy); 228 if (ret >= B_OK) 229 ret = into->AddFloat("radial_f_fx", (float)fData.radial_focus.fx); 230 if (ret >= B_OK) 231 ret = into->AddFloat("radial_f_fy", (float)fData.radial_focus.fy); 232 if (ret >= B_OK) 233 ret = into->AddFloat("radial_f_radius", (float)fData.radial_focus.radius); 234 235 // diamond 236 if (ret >= B_OK) 237 ret = into->AddFloat("diamond_cx", (float)fData.diamond.cx); 238 if (ret >= B_OK) 239 ret = into->AddFloat("diamond_cy", (float)fData.diamond.cy); 240 241 // conic 242 if (ret >= B_OK) 243 ret = into->AddFloat("conic_cx", (float)fData.conic.cx); 244 if (ret >= B_OK) 245 ret = into->AddFloat("conic_cy", (float)fData.conic.cy); 246 if (ret >= B_OK) 247 ret = into->AddFloat("conic_angle", (float)fData.conic.angle); 248 249 // finish off 250 if (ret >= B_OK) 251 ret = into->AddString("class", "BGradient"); 252 253 return ret; 254 } 255 256 257 // operator= 258 BGradient& 259 BGradient::operator=(const BGradient& other) 260 { 261 if (&other == this) 262 return *this; 263 264 SetColorStops(other); 265 fType = other.fType; 266 switch (fType) { 267 case TYPE_LINEAR: 268 fData.linear = other.fData.linear; 269 break; 270 case TYPE_RADIAL: 271 fData.radial = other.fData.radial; 272 break; 273 case TYPE_RADIAL_FOCUS: 274 fData.radial_focus = other.fData.radial_focus; 275 break; 276 case TYPE_DIAMOND: 277 fData.diamond = other.fData.diamond; 278 break; 279 case TYPE_CONIC: 280 fData.conic = other.fData.conic; 281 break; 282 case TYPE_NONE: 283 break; 284 } 285 return *this; 286 } 287 288 289 // operator== 290 bool 291 BGradient::operator==(const BGradient& other) const 292 { 293 return ((other.GetType() == GetType()) && ColorStopsAreEqual(other)); 294 } 295 296 297 // operator!= 298 bool 299 BGradient::operator!=(const BGradient& other) const 300 { 301 return !(*this == other); 302 } 303 304 305 // ColorStopsAreEqual 306 bool 307 BGradient::ColorStopsAreEqual(const BGradient& other) const 308 { 309 int32 count = CountColorStops(); 310 if (count == other.CountColorStops() && 311 fType == other.fType) { 312 313 bool equal = true; 314 for (int32 i = 0; i < count; i++) { 315 ColorStop* ourStop = ColorStopAtFast(i); 316 ColorStop* otherStop = other.ColorStopAtFast(i); 317 if (*ourStop != *otherStop) { 318 equal = false; 319 break; 320 } 321 } 322 return equal; 323 } 324 return false; 325 } 326 327 328 // SetColorStops 329 void 330 BGradient::SetColorStops(const BGradient& other) 331 { 332 MakeEmpty(); 333 for (int32 i = 0; ColorStop* stop = other.ColorStopAt(i); i++) 334 AddColorStop(*stop, i); 335 } 336 337 338 // AddColor 339 int32 340 BGradient::AddColor(const rgb_color& color, float offset) 341 { 342 // Out of bounds stops would crash the app_server 343 if (offset < 0.f || offset > 255.f) 344 return -1; 345 346 // find the correct index (sorted by offset) 347 ColorStop* stop = new ColorStop(color, offset); 348 int32 index = 0; 349 int32 count = CountColorStops(); 350 for (; index < count; index++) { 351 ColorStop* s = ColorStopAtFast(index); 352 if (s->offset > stop->offset) 353 break; 354 } 355 if (!fColorStops.AddItem((void*)stop, index)) { 356 delete stop; 357 return -1; 358 } 359 return index; 360 } 361 362 363 // AddColorStop 364 bool 365 BGradient::AddColorStop(const ColorStop& colorStop, int32 index) 366 { 367 ColorStop* stop = new ColorStop(colorStop); 368 if (!fColorStops.AddItem((void*)stop, index)) { 369 delete stop; 370 return false; 371 } 372 return true; 373 } 374 375 376 // RemoveColor 377 bool 378 BGradient::RemoveColor(int32 index) 379 { 380 ColorStop* stop = (ColorStop*)fColorStops.RemoveItem(index); 381 if (!stop) { 382 return false; 383 } 384 delete stop; 385 return true; 386 } 387 388 389 // SetColorStop 390 bool 391 BGradient::SetColorStop(int32 index, const ColorStop& color) 392 { 393 if (ColorStop* stop = ColorStopAt(index)) { 394 if (*stop != color) { 395 stop->color = color.color; 396 stop->offset = color.offset; 397 return true; 398 } 399 } 400 return false; 401 } 402 403 404 // SetColor 405 bool 406 BGradient::SetColor(int32 index, const rgb_color& color) 407 { 408 ColorStop* stop = ColorStopAt(index); 409 if (stop && stop->color != color) { 410 stop->color = color; 411 return true; 412 } 413 return false; 414 } 415 416 417 // SetOffset 418 bool 419 BGradient::SetOffset(int32 index, float offset) 420 { 421 ColorStop* stop = ColorStopAt(index); 422 if (stop && stop->offset != offset) { 423 stop->offset = offset; 424 return true; 425 } 426 return false; 427 } 428 429 430 // CountColorStops 431 int32 432 BGradient::CountColorStops() const 433 { 434 return fColorStops.CountItems(); 435 } 436 437 438 // ColorStopAt 439 BGradient::ColorStop* 440 BGradient::ColorStopAt(int32 index) const 441 { 442 return (ColorStop*)fColorStops.ItemAt(index); 443 } 444 445 446 // ColorStopAtFast 447 BGradient::ColorStop* 448 BGradient::ColorStopAtFast(int32 index) const 449 { 450 return (ColorStop*)fColorStops.ItemAtFast(index); 451 } 452 453 454 // ColorStops 455 BGradient::ColorStop* 456 BGradient::ColorStops() const 457 { 458 if (CountColorStops() > 0) { 459 return (ColorStop*) fColorStops.Items(); 460 } 461 return NULL; 462 } 463 464 465 // SortColorStopsByOffset 466 void 467 BGradient::SortColorStopsByOffset() 468 { 469 // Use stable sort: stops with the same offset will retain their original 470 // order. This can be used to have sharp color changes in the gradient. 471 // BList.SortItems() uses qsort(), which isn't stable, and sometimes swaps 472 // such stops. 473 const BGradient::ColorStop** first = (const BGradient::ColorStop**)fColorStops.Items(); 474 const BGradient::ColorStop** last = first + fColorStops.CountItems(); 475 std::stable_sort(first, last, sort_color_stops_by_offset); 476 } 477 478 479 // MakeEmpty 480 void 481 BGradient::MakeEmpty() 482 { 483 int32 count = CountColorStops(); 484 for (int32 i = 0; i < count; i++) 485 delete ColorStopAtFast(i); 486 fColorStops.MakeEmpty(); 487 } 488 489 490 status_t 491 BGradient::Flatten(BDataIO* stream) const 492 { 493 int32 stopCount = CountColorStops(); 494 stream->Write(&fType, sizeof(Type)); 495 stream->Write(&stopCount, sizeof(int32)); 496 if (stopCount > 0) { 497 for (int i = 0; i < stopCount; i++) { 498 stream->Write(ColorStopAtFast(i), 499 sizeof(ColorStop)); 500 } 501 } 502 503 switch (fType) { 504 case TYPE_LINEAR: 505 stream->Write(&fData.linear.x1, sizeof(float)); 506 stream->Write(&fData.linear.y1, sizeof(float)); 507 stream->Write(&fData.linear.x2, sizeof(float)); 508 stream->Write(&fData.linear.y2, sizeof(float)); 509 break; 510 case TYPE_RADIAL: 511 stream->Write(&fData.radial.cx, sizeof(float)); 512 stream->Write(&fData.radial.cy, sizeof(float)); 513 stream->Write(&fData.radial.radius, sizeof(float)); 514 break; 515 case TYPE_RADIAL_FOCUS: 516 stream->Write(&fData.radial_focus.cx, sizeof(float)); 517 stream->Write(&fData.radial_focus.cy, sizeof(float)); 518 stream->Write(&fData.radial_focus.fx, sizeof(float)); 519 stream->Write(&fData.radial_focus.fy, sizeof(float)); 520 stream->Write(&fData.radial_focus.radius, sizeof(float)); 521 break; 522 case TYPE_DIAMOND: 523 stream->Write(&fData.diamond.cx, sizeof(float)); 524 stream->Write(&fData.diamond.cy, sizeof(float)); 525 break; 526 case TYPE_CONIC: 527 stream->Write(&fData.conic.cx, sizeof(float)); 528 stream->Write(&fData.conic.cy, sizeof(float)); 529 stream->Write(&fData.conic.angle, sizeof(float)); 530 break; 531 case TYPE_NONE: 532 break; 533 } 534 return B_OK; 535 } 536 537 538 static BGradient* 539 gradient_for_type(BGradient::Type type) 540 { 541 switch (type) { 542 case BGradient::TYPE_LINEAR: 543 return new (std::nothrow) BGradientLinear(); 544 case BGradient::TYPE_RADIAL: 545 return new (std::nothrow) BGradientRadial(); 546 case BGradient::TYPE_RADIAL_FOCUS: 547 return new (std::nothrow) BGradientRadialFocus(); 548 case BGradient::TYPE_DIAMOND: 549 return new (std::nothrow) BGradientDiamond(); 550 case BGradient::TYPE_CONIC: 551 return new (std::nothrow) BGradientConic(); 552 case BGradient::TYPE_NONE: 553 return new (std::nothrow) BGradient(); 554 } 555 return NULL; 556 } 557 558 559 status_t 560 BGradient::Unflatten(BGradient *&output, BDataIO* stream) 561 { 562 output = NULL; 563 Type gradientType; 564 int32 colorsCount; 565 stream->Read(&gradientType, sizeof(Type)); 566 status_t status = stream->Read(&colorsCount, sizeof(int32)); 567 if (status < B_OK) 568 return status; 569 570 ObjectDeleter<BGradient> gradient(gradient_for_type(gradientType)); 571 if (!gradient.IsSet()) 572 return B_NO_MEMORY; 573 574 if (colorsCount > 0) { 575 ColorStop stop; 576 for (int i = 0; i < colorsCount; i++) { 577 if ((status = stream->Read(&stop, sizeof(ColorStop))) < B_OK) 578 return status; 579 if (!gradient->AddColorStop(stop, i)) 580 return B_NO_MEMORY; 581 } 582 } 583 584 switch (gradientType) { 585 case TYPE_LINEAR: 586 stream->Read(&gradient->fData.linear.x1, sizeof(float)); 587 stream->Read(&gradient->fData.linear.y1, sizeof(float)); 588 stream->Read(&gradient->fData.linear.x2, sizeof(float)); 589 if ((status = stream->Read(&gradient->fData.linear.y2, sizeof(float))) < B_OK) 590 return status; 591 break; 592 case TYPE_RADIAL: 593 stream->Read(&gradient->fData.radial.cx, sizeof(float)); 594 stream->Read(&gradient->fData.radial.cy, sizeof(float)); 595 if ((stream->Read(&gradient->fData.radial.radius, sizeof(float))) < B_OK) 596 return status; 597 break; 598 case TYPE_RADIAL_FOCUS: 599 stream->Read(&gradient->fData.radial_focus.cx, sizeof(float)); 600 stream->Read(&gradient->fData.radial_focus.cy, sizeof(float)); 601 stream->Read(&gradient->fData.radial_focus.fx, sizeof(float)); 602 stream->Read(&gradient->fData.radial_focus.fy, sizeof(float)); 603 if ((stream->Read(&gradient->fData.radial_focus.radius, sizeof(float))) < B_OK) 604 return status; 605 break; 606 case TYPE_DIAMOND: 607 stream->Read(&gradient->fData.diamond.cx, sizeof(float)); 608 if ((stream->Read(&gradient->fData.diamond.cy, sizeof(float))) < B_OK) 609 return status; 610 break; 611 case TYPE_CONIC: 612 stream->Read(&gradient->fData.conic.cx, sizeof(float)); 613 stream->Read(&gradient->fData.conic.cy, sizeof(float)); 614 if ((stream->Read(&gradient->fData.conic.angle, sizeof(float))) < B_OK) 615 return status; 616 break; 617 case TYPE_NONE: 618 break; 619 } 620 621 output = gradient.Detach(); 622 return B_OK; 623 } 624