/* * Copyright 2006-2009, Haiku. * Distributed under the terms of the MIT License. * * Authors: * Stephan Aßmus * Artur Wyszynski */ #include "Gradient.h" #include #include #include #include #include #include #include #include #include #include #include // 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) { } BGradient::BGradient(const BGradient& other) : BArchivable(), fColorStops(std::max((int32)4, other.CountColorStops())) { *this = other; } // 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) { if (&other == this) return *this; SetColorStops(other); fType = other.fType; switch (fType) { case TYPE_LINEAR: fData.linear = other.fData.linear; break; case TYPE_RADIAL: fData.radial = other.fData.radial; break; case TYPE_RADIAL_FOCUS: fData.radial_focus = other.fData.radial_focus; break; case TYPE_DIAMOND: fData.diamond = other.fData.diamond; break; case TYPE_CONIC: fData.conic = other.fData.conic; break; case TYPE_NONE: break; } 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(); } status_t BGradient::Flatten(BDataIO* stream) const { int32 stopCount = CountColorStops(); stream->Write(&fType, sizeof(Type)); stream->Write(&stopCount, sizeof(int32)); if (stopCount > 0) { for (int i = 0; i < stopCount; i++) { stream->Write(ColorStopAtFast(i), sizeof(ColorStop)); } } switch (fType) { case TYPE_LINEAR: stream->Write(&fData.linear.x1, sizeof(float)); stream->Write(&fData.linear.y1, sizeof(float)); stream->Write(&fData.linear.x2, sizeof(float)); stream->Write(&fData.linear.y2, sizeof(float)); break; case TYPE_RADIAL: stream->Write(&fData.radial.cx, sizeof(float)); stream->Write(&fData.radial.cy, sizeof(float)); stream->Write(&fData.radial.radius, sizeof(float)); break; case TYPE_RADIAL_FOCUS: stream->Write(&fData.radial_focus.cx, sizeof(float)); stream->Write(&fData.radial_focus.cy, sizeof(float)); stream->Write(&fData.radial_focus.fx, sizeof(float)); stream->Write(&fData.radial_focus.fy, sizeof(float)); stream->Write(&fData.radial_focus.radius, sizeof(float)); break; case TYPE_DIAMOND: stream->Write(&fData.diamond.cx, sizeof(float)); stream->Write(&fData.diamond.cy, sizeof(float)); break; case TYPE_CONIC: stream->Write(&fData.conic.cx, sizeof(float)); stream->Write(&fData.conic.cy, sizeof(float)); stream->Write(&fData.conic.angle, sizeof(float)); break; case TYPE_NONE: break; } return B_OK; } static BGradient* gradient_for_type(BGradient::Type type) { switch (type) { case BGradient::TYPE_LINEAR: return new (std::nothrow) BGradientLinear(); case BGradient::TYPE_RADIAL: return new (std::nothrow) BGradientRadial(); case BGradient::TYPE_RADIAL_FOCUS: return new (std::nothrow) BGradientRadialFocus(); case BGradient::TYPE_DIAMOND: return new (std::nothrow) BGradientDiamond(); case BGradient::TYPE_CONIC: return new (std::nothrow) BGradientConic(); case BGradient::TYPE_NONE: return new (std::nothrow) BGradient(); } return NULL; } status_t BGradient::Unflatten(BGradient *&output, BDataIO* stream) { output = NULL; Type gradientType; int32 colorsCount; stream->Read(&gradientType, sizeof(Type)); status_t status = stream->Read(&colorsCount, sizeof(int32)); if (status < B_OK) return status; ObjectDeleter gradient(gradient_for_type(gradientType)); if (!gradient.IsSet()) return B_NO_MEMORY; if (colorsCount > 0) { ColorStop stop; for (int i = 0; i < colorsCount; i++) { if ((status = stream->Read(&stop, sizeof(ColorStop))) < B_OK) return status; if (!gradient->AddColorStop(stop, i)) return B_NO_MEMORY; } } switch (gradientType) { case TYPE_LINEAR: stream->Read(&gradient->fData.linear.x1, sizeof(float)); stream->Read(&gradient->fData.linear.y1, sizeof(float)); stream->Read(&gradient->fData.linear.x2, sizeof(float)); if ((status = stream->Read(&gradient->fData.linear.y2, sizeof(float))) < B_OK) return status; break; case TYPE_RADIAL: stream->Read(&gradient->fData.radial.cx, sizeof(float)); stream->Read(&gradient->fData.radial.cy, sizeof(float)); if ((stream->Read(&gradient->fData.radial.radius, sizeof(float))) < B_OK) return status; break; case TYPE_RADIAL_FOCUS: stream->Read(&gradient->fData.radial_focus.cx, sizeof(float)); stream->Read(&gradient->fData.radial_focus.cy, sizeof(float)); stream->Read(&gradient->fData.radial_focus.fx, sizeof(float)); stream->Read(&gradient->fData.radial_focus.fy, sizeof(float)); if ((stream->Read(&gradient->fData.radial_focus.radius, sizeof(float))) < B_OK) return status; break; case TYPE_DIAMOND: stream->Read(&gradient->fData.diamond.cx, sizeof(float)); if ((stream->Read(&gradient->fData.diamond.cy, sizeof(float))) < B_OK) return status; break; case TYPE_CONIC: stream->Read(&gradient->fData.conic.cx, sizeof(float)); stream->Read(&gradient->fData.conic.cy, sizeof(float)); if ((stream->Read(&gradient->fData.conic.angle, sizeof(float))) < B_OK) return status; break; case TYPE_NONE: break; } output = gradient.Detach(); return B_OK; }