* stable_sort is part of the C++ standard, and should work just as well.
450 lines
9.6 KiB
C++
450 lines
9.6 KiB
C++
/*
|
|
* Copyright 2006-2009, Haiku.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Stephan Aßmus <[email protected]>
|
|
* Artur Wyszynski <[email protected]>
|
|
*/
|
|
|
|
#include "Gradient.h"
|
|
|
|
#include <algorithm>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
|
|
#include <Message.h>
|
|
|
|
|
|
// constructor
|
|
BGradient::ColorStop::ColorStop(const rgb_color c, float o)
|
|
{
|
|
color.red = c.red;
|
|
color.green = c.green;
|
|
color.blue = c.blue;
|
|
color.alpha = c.alpha;
|
|
offset = o;
|
|
}
|
|
|
|
|
|
// constructor
|
|
BGradient::ColorStop::ColorStop(uint8 r, uint8 g, uint8 b, uint8 a, float o)
|
|
{
|
|
color.red = r;
|
|
color.green = g;
|
|
color.blue = b;
|
|
color.alpha = a;
|
|
offset = o;
|
|
}
|
|
|
|
|
|
// constructor
|
|
BGradient::ColorStop::ColorStop(const ColorStop& other)
|
|
{
|
|
color.red = other.color.red;
|
|
color.green = other.color.green;
|
|
color.blue = other.color.blue;
|
|
color.alpha = other.color.alpha;
|
|
offset = other.offset;
|
|
}
|
|
|
|
|
|
// constructor
|
|
BGradient::ColorStop::ColorStop()
|
|
{
|
|
color.red = 0;
|
|
color.green = 0;
|
|
color.blue = 0;
|
|
color.alpha = 255;
|
|
offset = 0;
|
|
}
|
|
|
|
|
|
// operator!=
|
|
bool
|
|
BGradient::ColorStop::operator!=(const ColorStop& other) const
|
|
{
|
|
return color.red != other.color.red ||
|
|
color.green != other.color.green ||
|
|
color.blue != other.color.blue ||
|
|
color.alpha != other.color.alpha ||
|
|
offset != other.offset;
|
|
}
|
|
|
|
|
|
static bool
|
|
sort_color_stops_by_offset(const BGradient::ColorStop* left,
|
|
const BGradient::ColorStop* right)
|
|
{
|
|
return left->offset < right->offset;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
// constructor
|
|
BGradient::BGradient()
|
|
: BArchivable(),
|
|
fColorStops(4),
|
|
fType(TYPE_NONE)
|
|
{
|
|
}
|
|
|
|
|
|
// constructor
|
|
BGradient::BGradient(BMessage* archive)
|
|
: BArchivable(archive),
|
|
fColorStops(4),
|
|
fType(TYPE_NONE)
|
|
{
|
|
if (!archive)
|
|
return;
|
|
|
|
// color stops
|
|
ColorStop stop;
|
|
for (int32 i = 0; archive->FindFloat("offset", i, &stop.offset) >= B_OK; i++) {
|
|
if (archive->FindInt32("color", i, (int32*)&stop.color) >= B_OK)
|
|
AddColorStop(stop, i);
|
|
else
|
|
break;
|
|
}
|
|
if (archive->FindInt32("type", (int32*)&fType) < B_OK)
|
|
fType = TYPE_LINEAR;
|
|
|
|
// linear
|
|
if (archive->FindFloat("linear_x1", (float*)&fData.linear.x1) < B_OK)
|
|
fData.linear.x1 = 0.0f;
|
|
if (archive->FindFloat("linear_y1", (float*)&fData.linear.y1) < B_OK)
|
|
fData.linear.y1 = 0.0f;
|
|
if (archive->FindFloat("linear_x2", (float*)&fData.linear.x2) < B_OK)
|
|
fData.linear.x2 = 0.0f;
|
|
if (archive->FindFloat("linear_y2", (float*)&fData.linear.y2) < B_OK)
|
|
fData.linear.y2 = 0.0f;
|
|
|
|
// radial
|
|
if (archive->FindFloat("radial_cx", (float*)&fData.radial.cx) < B_OK)
|
|
fData.radial.cx = 0.0f;
|
|
if (archive->FindFloat("radial_cy", (float*)&fData.radial.cy) < B_OK)
|
|
fData.radial.cy = 0.0f;
|
|
if (archive->FindFloat("radial_radius", (float*)&fData.radial.radius) < B_OK)
|
|
fData.radial.radius = 0.0f;
|
|
|
|
// radial focus
|
|
if (archive->FindFloat("radial_f_cx", (float*)&fData.radial_focus.cx) < B_OK)
|
|
fData.radial_focus.cx = 0.0f;
|
|
if (archive->FindFloat("radial_f_cy", (float*)&fData.radial_focus.cy) < B_OK)
|
|
fData.radial_focus.cy = 0.0f;
|
|
if (archive->FindFloat("radial_f_fx", (float*)&fData.radial_focus.fx) < B_OK)
|
|
fData.radial_focus.fx = 0.0f;
|
|
if (archive->FindFloat("radial_f_fy", (float*)&fData.radial_focus.fy) < B_OK)
|
|
fData.radial_focus.fy = 0.0f;
|
|
if (archive->FindFloat("radial_f_radius", (float*)&fData.radial_focus.radius) < B_OK)
|
|
fData.radial_focus.radius = 0.0f;
|
|
|
|
// diamond
|
|
if (archive->FindFloat("diamond_cx", (float*)&fData.diamond.cx) < B_OK)
|
|
fData.diamond.cx = 0.0f;
|
|
if (archive->FindFloat("diamond_cy", (float*)&fData.diamond.cy) < B_OK)
|
|
fData.diamond.cy = 0.0f;
|
|
|
|
// conic
|
|
if (archive->FindFloat("conic_cx", (float*)&fData.conic.cx) < B_OK)
|
|
fData.conic.cx = 0.0f;
|
|
if (archive->FindFloat("conic_cy", (float*)&fData.conic.cy) < B_OK)
|
|
fData.conic.cy = 0.0f;
|
|
if (archive->FindFloat("conic_angle", (float*)&fData.conic.angle) < B_OK)
|
|
fData.conic.angle = 0.0f;
|
|
}
|
|
|
|
|
|
// destructor
|
|
BGradient::~BGradient()
|
|
{
|
|
MakeEmpty();
|
|
}
|
|
|
|
|
|
// Archive
|
|
status_t
|
|
BGradient::Archive(BMessage* into, bool deep) const
|
|
{
|
|
status_t ret = BArchivable::Archive(into, deep);
|
|
|
|
// color steps
|
|
if (ret >= B_OK) {
|
|
for (int32 i = 0; ColorStop* stop = ColorStopAt(i); i++) {
|
|
ret = into->AddInt32("color", (const uint32&)stop->color);
|
|
if (ret < B_OK)
|
|
break;
|
|
ret = into->AddFloat("offset", stop->offset);
|
|
if (ret < B_OK)
|
|
break;
|
|
}
|
|
}
|
|
// gradient type
|
|
if (ret >= B_OK)
|
|
ret = into->AddInt32("type", (int32)fType);
|
|
|
|
// linear
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("linear_x1", (float)fData.linear.x1);
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("linear_y1", (float)fData.linear.y1);
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("linear_x2", (float)fData.linear.x2);
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("linear_y2", (float)fData.linear.y2);
|
|
|
|
// radial
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("radial_cx", (float)fData.radial.cx);
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("radial_cy", (float)fData.radial.cy);
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("radial_radius", (float)fData.radial.radius);
|
|
|
|
// radial focus
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("radial_f_cx", (float)fData.radial_focus.cx);
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("radial_f_cy", (float)fData.radial_focus.cy);
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("radial_f_fx", (float)fData.radial_focus.fx);
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("radial_f_fy", (float)fData.radial_focus.fy);
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("radial_f_radius", (float)fData.radial_focus.radius);
|
|
|
|
// diamond
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("diamond_cx", (float)fData.diamond.cx);
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("diamond_cy", (float)fData.diamond.cy);
|
|
|
|
// conic
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("conic_cx", (float)fData.conic.cx);
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("conic_cy", (float)fData.conic.cy);
|
|
if (ret >= B_OK)
|
|
ret = into->AddFloat("conic_angle", (float)fData.conic.angle);
|
|
|
|
// finish off
|
|
if (ret >= B_OK)
|
|
ret = into->AddString("class", "BGradient");
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
// operator=
|
|
BGradient&
|
|
BGradient::operator=(const BGradient& other)
|
|
{
|
|
SetColorStops(other);
|
|
fType = other.fType;
|
|
return *this;
|
|
}
|
|
|
|
|
|
// operator==
|
|
bool
|
|
BGradient::operator==(const BGradient& other) const
|
|
{
|
|
return ((other.GetType() == GetType()) && ColorStopsAreEqual(other));
|
|
}
|
|
|
|
|
|
// operator!=
|
|
bool
|
|
BGradient::operator!=(const BGradient& other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
|
|
// ColorStopsAreEqual
|
|
bool
|
|
BGradient::ColorStopsAreEqual(const BGradient& other) const
|
|
{
|
|
int32 count = CountColorStops();
|
|
if (count == other.CountColorStops() &&
|
|
fType == other.fType) {
|
|
|
|
bool equal = true;
|
|
for (int32 i = 0; i < count; i++) {
|
|
ColorStop* ourStop = ColorStopAtFast(i);
|
|
ColorStop* otherStop = other.ColorStopAtFast(i);
|
|
if (*ourStop != *otherStop) {
|
|
equal = false;
|
|
break;
|
|
}
|
|
}
|
|
return equal;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// SetColorStops
|
|
void
|
|
BGradient::SetColorStops(const BGradient& other)
|
|
{
|
|
MakeEmpty();
|
|
for (int32 i = 0; ColorStop* stop = other.ColorStopAt(i); i++)
|
|
AddColorStop(*stop, i);
|
|
}
|
|
|
|
|
|
// AddColor
|
|
int32
|
|
BGradient::AddColor(const rgb_color& color, float offset)
|
|
{
|
|
// Out of bounds stops would crash the app_server
|
|
if (offset < 0.f || offset > 255.f)
|
|
return -1;
|
|
|
|
// find the correct index (sorted by offset)
|
|
ColorStop* stop = new ColorStop(color, offset);
|
|
int32 index = 0;
|
|
int32 count = CountColorStops();
|
|
for (; index < count; index++) {
|
|
ColorStop* s = ColorStopAtFast(index);
|
|
if (s->offset > stop->offset)
|
|
break;
|
|
}
|
|
if (!fColorStops.AddItem((void*)stop, index)) {
|
|
delete stop;
|
|
return -1;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
|
|
// AddColorStop
|
|
bool
|
|
BGradient::AddColorStop(const ColorStop& colorStop, int32 index)
|
|
{
|
|
ColorStop* stop = new ColorStop(colorStop);
|
|
if (!fColorStops.AddItem((void*)stop, index)) {
|
|
delete stop;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// RemoveColor
|
|
bool
|
|
BGradient::RemoveColor(int32 index)
|
|
{
|
|
ColorStop* stop = (ColorStop*)fColorStops.RemoveItem(index);
|
|
if (!stop) {
|
|
return false;
|
|
}
|
|
delete stop;
|
|
return true;
|
|
}
|
|
|
|
|
|
// SetColorStop
|
|
bool
|
|
BGradient::SetColorStop(int32 index, const ColorStop& color)
|
|
{
|
|
if (ColorStop* stop = ColorStopAt(index)) {
|
|
if (*stop != color) {
|
|
stop->color = color.color;
|
|
stop->offset = color.offset;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// SetColor
|
|
bool
|
|
BGradient::SetColor(int32 index, const rgb_color& color)
|
|
{
|
|
ColorStop* stop = ColorStopAt(index);
|
|
if (stop && stop->color != color) {
|
|
stop->color = color;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// SetOffset
|
|
bool
|
|
BGradient::SetOffset(int32 index, float offset)
|
|
{
|
|
ColorStop* stop = ColorStopAt(index);
|
|
if (stop && stop->offset != offset) {
|
|
stop->offset = offset;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// CountColorStops
|
|
int32
|
|
BGradient::CountColorStops() const
|
|
{
|
|
return fColorStops.CountItems();
|
|
}
|
|
|
|
|
|
// ColorStopAt
|
|
BGradient::ColorStop*
|
|
BGradient::ColorStopAt(int32 index) const
|
|
{
|
|
return (ColorStop*)fColorStops.ItemAt(index);
|
|
}
|
|
|
|
|
|
// ColorStopAtFast
|
|
BGradient::ColorStop*
|
|
BGradient::ColorStopAtFast(int32 index) const
|
|
{
|
|
return (ColorStop*)fColorStops.ItemAtFast(index);
|
|
}
|
|
|
|
|
|
// ColorStops
|
|
BGradient::ColorStop*
|
|
BGradient::ColorStops() const
|
|
{
|
|
if (CountColorStops() > 0) {
|
|
return (ColorStop*) fColorStops.Items();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// SortColorStopsByOffset
|
|
void
|
|
BGradient::SortColorStopsByOffset()
|
|
{
|
|
// Use stable sort: stops with the same offset will retain their original
|
|
// order. This can be used to have sharp color changes in the gradient.
|
|
// BList.SortItems() uses qsort(), which isn't stable, and sometimes swaps
|
|
// such stops.
|
|
const BGradient::ColorStop** first = (const BGradient::ColorStop**)fColorStops.Items();
|
|
const BGradient::ColorStop** last = first + fColorStops.CountItems();
|
|
std::stable_sort(first, last, sort_color_stops_by_offset);
|
|
}
|
|
|
|
|
|
// MakeEmpty
|
|
void
|
|
BGradient::MakeEmpty()
|
|
{
|
|
int32 count = CountColorStops();
|
|
for (int32 i = 0; i < count; i++)
|
|
delete ColorStopAtFast(i);
|
|
fColorStops.MakeEmpty();
|
|
}
|