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 28BGradient::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 39BGradient::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 50BGradient::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 61BGradient::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!= 72bool 73BGradient::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 83static bool 84sort_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 95BGradient::BGradient() 96 : BArchivable(), 97 fColorStops(4), 98 fType(TYPE_NONE) 99{ 100} 101 102 103BGradient::BGradient(const BGradient& other) 104 : BArchivable(), 105 fColorStops(std::max((int32)4, other.CountColorStops())) 106{ 107 *this = other; 108} 109 110 111// constructor 112BGradient::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 178BGradient::~BGradient() 179{ 180 MakeEmpty(); 181} 182 183 184// Archive 185status_t 186BGradient::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= 258BGradient& 259BGradient::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== 290bool 291BGradient::operator==(const BGradient& other) const 292{ 293 return ((other.GetType() == GetType()) && ColorStopsAreEqual(other)); 294} 295 296 297// operator!= 298bool 299BGradient::operator!=(const BGradient& other) const 300{ 301 return !(*this == other); 302} 303 304 305// ColorStopsAreEqual 306bool 307BGradient::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 329void 330BGradient::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 339int32 340BGradient::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 364bool 365BGradient::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 377bool 378BGradient::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 390bool 391BGradient::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 405bool 406BGradient::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 418bool 419BGradient::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 431int32 432BGradient::CountColorStops() const 433{ 434 return fColorStops.CountItems(); 435} 436 437 438// ColorStopAt 439BGradient::ColorStop* 440BGradient::ColorStopAt(int32 index) const 441{ 442 return (ColorStop*)fColorStops.ItemAt(index); 443} 444 445 446// ColorStopAtFast 447BGradient::ColorStop* 448BGradient::ColorStopAtFast(int32 index) const 449{ 450 return (ColorStop*)fColorStops.ItemAtFast(index); 451} 452 453 454// ColorStops 455BGradient::ColorStop* 456BGradient::ColorStops() const 457{ 458 if (CountColorStops() > 0) { 459 return (ColorStop*) fColorStops.Items(); 460 } 461 return NULL; 462} 463 464 465// SortColorStopsByOffset 466void 467BGradient::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 480void 481BGradient::MakeEmpty() 482{ 483 int32 count = CountColorStops(); 484 for (int32 i = 0; i < count; i++) 485 delete ColorStopAtFast(i); 486 fColorStops.MakeEmpty(); 487} 488 489 490status_t 491BGradient::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 538static BGradient* 539gradient_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 559status_t 560BGradient::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