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 <Message.h> 17 18 19 // constructor 20 BGradient::ColorStop::ColorStop(const rgb_color c, float o) 21 { 22 color.red = c.red; 23 color.green = c.green; 24 color.blue = c.blue; 25 color.alpha = c.alpha; 26 offset = o; 27 } 28 29 30 // constructor 31 BGradient::ColorStop::ColorStop(uint8 r, uint8 g, uint8 b, uint8 a, float o) 32 { 33 color.red = r; 34 color.green = g; 35 color.blue = b; 36 color.alpha = a; 37 offset = o; 38 } 39 40 41 // constructor 42 BGradient::ColorStop::ColorStop(const ColorStop& other) 43 { 44 color.red = other.color.red; 45 color.green = other.color.green; 46 color.blue = other.color.blue; 47 color.alpha = other.color.alpha; 48 offset = other.offset; 49 } 50 51 52 // constructor 53 BGradient::ColorStop::ColorStop() 54 { 55 color.red = 0; 56 color.green = 0; 57 color.blue = 0; 58 color.alpha = 255; 59 offset = 0; 60 } 61 62 63 // operator!= 64 bool 65 BGradient::ColorStop::operator!=(const ColorStop& other) const 66 { 67 return color.red != other.color.red || 68 color.green != other.color.green || 69 color.blue != other.color.blue || 70 color.alpha != other.color.alpha || 71 offset != other.offset; 72 } 73 74 75 static bool 76 sort_color_stops_by_offset(const BGradient::ColorStop* left, 77 const BGradient::ColorStop* right) 78 { 79 return left->offset < right->offset; 80 } 81 82 83 // #pragma mark - 84 85 86 // constructor 87 BGradient::BGradient() 88 : BArchivable(), 89 fColorStops(4), 90 fType(TYPE_NONE) 91 { 92 } 93 94 95 // constructor 96 BGradient::BGradient(BMessage* archive) 97 : BArchivable(archive), 98 fColorStops(4), 99 fType(TYPE_NONE) 100 { 101 if (!archive) 102 return; 103 104 // color stops 105 ColorStop stop; 106 for (int32 i = 0; archive->FindFloat("offset", i, &stop.offset) >= B_OK; i++) { 107 if (archive->FindInt32("color", i, (int32*)&stop.color) >= B_OK) 108 AddColorStop(stop, i); 109 else 110 break; 111 } 112 if (archive->FindInt32("type", (int32*)&fType) < B_OK) 113 fType = TYPE_LINEAR; 114 115 // linear 116 if (archive->FindFloat("linear_x1", (float*)&fData.linear.x1) < B_OK) 117 fData.linear.x1 = 0.0f; 118 if (archive->FindFloat("linear_y1", (float*)&fData.linear.y1) < B_OK) 119 fData.linear.y1 = 0.0f; 120 if (archive->FindFloat("linear_x2", (float*)&fData.linear.x2) < B_OK) 121 fData.linear.x2 = 0.0f; 122 if (archive->FindFloat("linear_y2", (float*)&fData.linear.y2) < B_OK) 123 fData.linear.y2 = 0.0f; 124 125 // radial 126 if (archive->FindFloat("radial_cx", (float*)&fData.radial.cx) < B_OK) 127 fData.radial.cx = 0.0f; 128 if (archive->FindFloat("radial_cy", (float*)&fData.radial.cy) < B_OK) 129 fData.radial.cy = 0.0f; 130 if (archive->FindFloat("radial_radius", (float*)&fData.radial.radius) < B_OK) 131 fData.radial.radius = 0.0f; 132 133 // radial focus 134 if (archive->FindFloat("radial_f_cx", (float*)&fData.radial_focus.cx) < B_OK) 135 fData.radial_focus.cx = 0.0f; 136 if (archive->FindFloat("radial_f_cy", (float*)&fData.radial_focus.cy) < B_OK) 137 fData.radial_focus.cy = 0.0f; 138 if (archive->FindFloat("radial_f_fx", (float*)&fData.radial_focus.fx) < B_OK) 139 fData.radial_focus.fx = 0.0f; 140 if (archive->FindFloat("radial_f_fy", (float*)&fData.radial_focus.fy) < B_OK) 141 fData.radial_focus.fy = 0.0f; 142 if (archive->FindFloat("radial_f_radius", (float*)&fData.radial_focus.radius) < B_OK) 143 fData.radial_focus.radius = 0.0f; 144 145 // diamond 146 if (archive->FindFloat("diamond_cx", (float*)&fData.diamond.cx) < B_OK) 147 fData.diamond.cx = 0.0f; 148 if (archive->FindFloat("diamond_cy", (float*)&fData.diamond.cy) < B_OK) 149 fData.diamond.cy = 0.0f; 150 151 // conic 152 if (archive->FindFloat("conic_cx", (float*)&fData.conic.cx) < B_OK) 153 fData.conic.cx = 0.0f; 154 if (archive->FindFloat("conic_cy", (float*)&fData.conic.cy) < B_OK) 155 fData.conic.cy = 0.0f; 156 if (archive->FindFloat("conic_angle", (float*)&fData.conic.angle) < B_OK) 157 fData.conic.angle = 0.0f; 158 } 159 160 161 // destructor 162 BGradient::~BGradient() 163 { 164 MakeEmpty(); 165 } 166 167 168 // Archive 169 status_t 170 BGradient::Archive(BMessage* into, bool deep) const 171 { 172 status_t ret = BArchivable::Archive(into, deep); 173 174 // color steps 175 if (ret >= B_OK) { 176 for (int32 i = 0; ColorStop* stop = ColorStopAt(i); i++) { 177 ret = into->AddInt32("color", (const uint32&)stop->color); 178 if (ret < B_OK) 179 break; 180 ret = into->AddFloat("offset", stop->offset); 181 if (ret < B_OK) 182 break; 183 } 184 } 185 // gradient type 186 if (ret >= B_OK) 187 ret = into->AddInt32("type", (int32)fType); 188 189 // linear 190 if (ret >= B_OK) 191 ret = into->AddFloat("linear_x1", (float)fData.linear.x1); 192 if (ret >= B_OK) 193 ret = into->AddFloat("linear_y1", (float)fData.linear.y1); 194 if (ret >= B_OK) 195 ret = into->AddFloat("linear_x2", (float)fData.linear.x2); 196 if (ret >= B_OK) 197 ret = into->AddFloat("linear_y2", (float)fData.linear.y2); 198 199 // radial 200 if (ret >= B_OK) 201 ret = into->AddFloat("radial_cx", (float)fData.radial.cx); 202 if (ret >= B_OK) 203 ret = into->AddFloat("radial_cy", (float)fData.radial.cy); 204 if (ret >= B_OK) 205 ret = into->AddFloat("radial_radius", (float)fData.radial.radius); 206 207 // radial focus 208 if (ret >= B_OK) 209 ret = into->AddFloat("radial_f_cx", (float)fData.radial_focus.cx); 210 if (ret >= B_OK) 211 ret = into->AddFloat("radial_f_cy", (float)fData.radial_focus.cy); 212 if (ret >= B_OK) 213 ret = into->AddFloat("radial_f_fx", (float)fData.radial_focus.fx); 214 if (ret >= B_OK) 215 ret = into->AddFloat("radial_f_fy", (float)fData.radial_focus.fy); 216 if (ret >= B_OK) 217 ret = into->AddFloat("radial_f_radius", (float)fData.radial_focus.radius); 218 219 // diamond 220 if (ret >= B_OK) 221 ret = into->AddFloat("diamond_cx", (float)fData.diamond.cx); 222 if (ret >= B_OK) 223 ret = into->AddFloat("diamond_cy", (float)fData.diamond.cy); 224 225 // conic 226 if (ret >= B_OK) 227 ret = into->AddFloat("conic_cx", (float)fData.conic.cx); 228 if (ret >= B_OK) 229 ret = into->AddFloat("conic_cy", (float)fData.conic.cy); 230 if (ret >= B_OK) 231 ret = into->AddFloat("conic_angle", (float)fData.conic.angle); 232 233 // finish off 234 if (ret >= B_OK) 235 ret = into->AddString("class", "BGradient"); 236 237 return ret; 238 } 239 240 241 // operator= 242 BGradient& 243 BGradient::operator=(const BGradient& other) 244 { 245 SetColorStops(other); 246 fType = other.fType; 247 return *this; 248 } 249 250 251 // operator== 252 bool 253 BGradient::operator==(const BGradient& other) const 254 { 255 return ((other.GetType() == GetType()) && ColorStopsAreEqual(other)); 256 } 257 258 259 // operator!= 260 bool 261 BGradient::operator!=(const BGradient& other) const 262 { 263 return !(*this == other); 264 } 265 266 267 // ColorStopsAreEqual 268 bool 269 BGradient::ColorStopsAreEqual(const BGradient& other) const 270 { 271 int32 count = CountColorStops(); 272 if (count == other.CountColorStops() && 273 fType == other.fType) { 274 275 bool equal = true; 276 for (int32 i = 0; i < count; i++) { 277 ColorStop* ourStop = ColorStopAtFast(i); 278 ColorStop* otherStop = other.ColorStopAtFast(i); 279 if (*ourStop != *otherStop) { 280 equal = false; 281 break; 282 } 283 } 284 return equal; 285 } 286 return false; 287 } 288 289 290 // SetColorStops 291 void 292 BGradient::SetColorStops(const BGradient& other) 293 { 294 MakeEmpty(); 295 for (int32 i = 0; ColorStop* stop = other.ColorStopAt(i); i++) 296 AddColorStop(*stop, i); 297 } 298 299 300 // AddColor 301 int32 302 BGradient::AddColor(const rgb_color& color, float offset) 303 { 304 // Out of bounds stops would crash the app_server 305 if (offset < 0.f || offset > 255.f) 306 return -1; 307 308 // find the correct index (sorted by offset) 309 ColorStop* stop = new ColorStop(color, offset); 310 int32 index = 0; 311 int32 count = CountColorStops(); 312 for (; index < count; index++) { 313 ColorStop* s = ColorStopAtFast(index); 314 if (s->offset > stop->offset) 315 break; 316 } 317 if (!fColorStops.AddItem((void*)stop, index)) { 318 delete stop; 319 return -1; 320 } 321 return index; 322 } 323 324 325 // AddColorStop 326 bool 327 BGradient::AddColorStop(const ColorStop& colorStop, int32 index) 328 { 329 ColorStop* stop = new ColorStop(colorStop); 330 if (!fColorStops.AddItem((void*)stop, index)) { 331 delete stop; 332 return false; 333 } 334 return true; 335 } 336 337 338 // RemoveColor 339 bool 340 BGradient::RemoveColor(int32 index) 341 { 342 ColorStop* stop = (ColorStop*)fColorStops.RemoveItem(index); 343 if (!stop) { 344 return false; 345 } 346 delete stop; 347 return true; 348 } 349 350 351 // SetColorStop 352 bool 353 BGradient::SetColorStop(int32 index, const ColorStop& color) 354 { 355 if (ColorStop* stop = ColorStopAt(index)) { 356 if (*stop != color) { 357 stop->color = color.color; 358 stop->offset = color.offset; 359 return true; 360 } 361 } 362 return false; 363 } 364 365 366 // SetColor 367 bool 368 BGradient::SetColor(int32 index, const rgb_color& color) 369 { 370 ColorStop* stop = ColorStopAt(index); 371 if (stop && stop->color != color) { 372 stop->color = color; 373 return true; 374 } 375 return false; 376 } 377 378 379 // SetOffset 380 bool 381 BGradient::SetOffset(int32 index, float offset) 382 { 383 ColorStop* stop = ColorStopAt(index); 384 if (stop && stop->offset != offset) { 385 stop->offset = offset; 386 return true; 387 } 388 return false; 389 } 390 391 392 // CountColorStops 393 int32 394 BGradient::CountColorStops() const 395 { 396 return fColorStops.CountItems(); 397 } 398 399 400 // ColorStopAt 401 BGradient::ColorStop* 402 BGradient::ColorStopAt(int32 index) const 403 { 404 return (ColorStop*)fColorStops.ItemAt(index); 405 } 406 407 408 // ColorStopAtFast 409 BGradient::ColorStop* 410 BGradient::ColorStopAtFast(int32 index) const 411 { 412 return (ColorStop*)fColorStops.ItemAtFast(index); 413 } 414 415 416 // ColorStops 417 BGradient::ColorStop* 418 BGradient::ColorStops() const 419 { 420 if (CountColorStops() > 0) { 421 return (ColorStop*) fColorStops.Items(); 422 } 423 return NULL; 424 } 425 426 427 // SortColorStopsByOffset 428 void 429 BGradient::SortColorStopsByOffset() 430 { 431 // Use stable sort: stops with the same offset will retain their original 432 // order. This can be used to have sharp color changes in the gradient. 433 // BList.SortItems() uses qsort(), which isn't stable, and sometimes swaps 434 // such stops. 435 const BGradient::ColorStop** first = (const BGradient::ColorStop**)fColorStops.Items(); 436 const BGradient::ColorStop** last = first + fColorStops.CountItems(); 437 std::stable_sort(first, last, sort_color_stops_by_offset); 438 } 439 440 441 // MakeEmpty 442 void 443 BGradient::MakeEmpty() 444 { 445 int32 count = CountColorStops(); 446 for (int32 i = 0; i < count; i++) 447 delete ColorStopAtFast(i); 448 fColorStops.MakeEmpty(); 449 } 450