/** \file
* SPGradient, SPStop, SPLinearGradient, SPRadialGradient,
* SPMesh, SPMeshRow, SPMeshPatch
*/
/*
* Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
* bulia byak <buliabyak@users.sf.net>
* Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
* Jon A. Cruz <jon@joncruz.org>
* Abhishek Sharma
* Tavmjong Bah <tavmjong@free.fr>
*
* Copyright (C) 1999-2002 Lauris Kaplinski
* Copyright (C) 2000-2001 Ximian, Inc.
* Copyright (C) 2004 David Turner
* Copyright (C) 2009 Jasper van de Gronde
* Copyright (C) 2011 Tavmjong Bah
*
* Released under GNU GPL, read the file 'COPYING' for more information
*
*/
#define noSP_GRADIENT_VERBOSE
#include <cstring>
#include <string>
#include <cairo.h>
#include "display/cairo-utils.h"
#include "svg/svg-color.h"
#include "svg/css-ostringstream.h"
#include "attributes.h"
#include "document-private.h"
#include "sp-gradient.h"
#include "gradient-chemistry.h"
#include "sp-gradient-reference.h"
#include "sp-linear-gradient.h"
#include "sp-radial-gradient.h"
#include "sp-mesh.h"
#include "sp-mesh-row.h"
#include "sp-mesh-patch.h"
#include "sp-stop.h"
#include "streq.h"
#include "uri.h"
#include "style.h"
#include "display/grayscale.h"
/// Has to be power of 2 Seems to be unused.
//#define NCOLORS NR_GRADIENT_VECTOR_LENGTH
{
return has_stops;
}
{
return has_patches;
}
{
return units_set;
}
{
return units;
}
{
return spread_set;
}
{
return spread;
}
{
}
}
/**
* return true if this gradient is "equivalent" to that gradient.
* Equivalent meaning they have the same stop count, same stop colors and same stop opacity
* @param that - A gradient to compare this to
*/
{
//TODO Make this work for mesh gradients
bool status = false;
while(1){ // not really a loop, used to avoid deep nesting or multiple exit points from function
if ( this->isSwatch() ){
// drop down to check stops.
}
else if (
}
else { break; } // this should never happen, some unhandled type of gradient
bool effective = true;
effective = false;
break;
}
else {
}
}
if (!effective) break;
status = true;
break;
}
return status;
}
/**
* return true if this gradient is "aligned" to that gradient.
* Aligned means that they have exactly the same coordinates and transform.
* @param that - A gradient to compare this to
*/
{
bool status = false;
/* Some gradients have coordinates/other values specified, some don't.
yes/yes check the coordinates/other values
It is NOT safe to just compare the computed values because if that field has
not been set the computed value could be full of garbage.
matches the default value.
*/
while(1){ // not really a loop, used to avoid deep nesting or multiple exit points from function
if(this->gradientTransform_set &&
} else if( sg->x1._set || sg->y1._set || sg->x2._set || sg->y2._set) { break; } // some mix of set and not set
// none set? assume aligned and fall through
} else if( sg->cx._set || sg->cy._set || sg->fx._set || sg->fy._set || sg->r._set ) { break; } // some mix of set and not set
// none set? assume aligned and fall through
// none set? assume aligned and fall through
} else {
break;
}
status = true;
break;
}
return status;
}
/*
* Gradient
*/
spread(),
state(2),
vector() {
this->has_patches = 0;
this->ref = new SPGradientReference(this);
this->ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(SPGradient::gradientRefChanged), this));
/** \todo
* Fixme: reprs being rearranged (e.g. via the XML editor)
* may require us to clear the state.
*/
this->state = SP_GRADIENT_STATE_UNKNOWN;
this->units = SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX;
this->gradientTransform_set = FALSE;
this->spread = SP_GRADIENT_SPREAD_PAD;
this->spread_set = FALSE;
}
SPGradient::~SPGradient() {
}
/**
* Virtual build: set gradient attributes from its associated repr.
*/
{
// Work-around in case a swatch had been marked for immediate collection:
}
if (SP_IS_STOP(ochild)) {
break;
}
}
this->readAttr( "gradientUnits" );
this->readAttr( "gradientTransform" );
this->readAttr( "spreadMethod" );
this->readAttr( "xlink:href" );
this->readAttr( "osb:paint" );
// Register ourselves
}
/**
* Virtual release of SPGradient members before destruction.
*/
{
#ifdef SP_GRADIENT_VERBOSE
#endif
if (this->document) {
// Unregister ourselves
}
if (this->ref) {
this->modified_connection.disconnect();
delete this->ref;
}
//this->modified_connection.~connection();
SPPaintServer::release();
}
/**
* Set gradient attribute to value.
*/
{
switch (key) {
case SP_ATTR_GRADIENTUNITS:
if (value) {
this->units = SP_GRADIENT_UNITS_USERSPACEONUSE;
} else {
this->units = SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX;
}
} else {
this->units = SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX;
}
break;
case SP_ATTR_GRADIENTTRANSFORM: {
this->gradientTransform = t;
this->gradientTransform_set = TRUE;
} else {
this->gradientTransform_set = FALSE;
}
break;
}
case SP_ATTR_SPREADMETHOD:
if (value) {
this->spread = SP_GRADIENT_SPREAD_REFLECT;
this->spread = SP_GRADIENT_SPREAD_REPEAT;
} else {
this->spread = SP_GRADIENT_SPREAD_PAD;
}
this->spread_set = TRUE;
} else {
this->spread_set = FALSE;
}
break;
case SP_ATTR_XLINK_HREF:
if (value) {
try {
} catch (Inkscape::BadURIException &e) {
}
} else {
}
break;
case SP_ATTR_OSB_SWATCH:
{
bool modified = false;
modified = true;
}
if (newVal) {
Glib::ustring paintVal = ( this->hasStops() && (this->getStopCount() == 0) ) ? "solid" : "gradient";
modified = true;
}
}
if (modified) {
}
}
break;
default:
break;
}
}
/**
* Gets called when the gradient is (re)attached to another gradient.
*/
{
if (old_ref) {
}
if ( SP_IS_GRADIENT(ref)
{
gr->modified_connection = ref->connectModified(sigc::bind<2>(sigc::ptr_fun(&SPGradient::gradientRefModified), gr));
}
// Per SVG, all unset attributes must be inherited from linked gradient.
// So, as we're now (re)linked, we assign linkee's values to this gradient if they are not yet set -
// but without setting the _set flags.
// FIXME: do the same for gradientTransform too
}
if (!gr->spread_set) {
}
/// \todo Fixme: what should the flags (second) argument be? */
}
/**
* Callback for child_added event.
*/
{
this->invalidateVector();
if ( this->getStopCount() > 0 ) {
}
}
}
/// \todo Fixme: should we schedule "modified" here?
}
/**
* Callback for remove_child event.
*/
{
this->invalidateVector();
if (SP_IS_STOP(ochild)) {
break;
}
}
if ( this->getStopCount() == 0 ) {
}
}
/* Fixme: should we schedule "modified" here? */
}
/**
* Callback for modified event.
*/
{
if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) {
// CPPIFY
// This comparison has never worked (i. e. always evaluated to false),
// the right value would have been SP_TYPE_MESH
//if( this->get_type() != SP_GRADIENT_TYPE_MESH ) {
// if (!SP_IS_MESH(this)) {
// this->invalidateVector();
// } else {
// this->invalidateArray();
// }
this->invalidateVector();
}
if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
// CPPIFY
// see above
//if( this->get_type() != SP_GRADIENT_TYPE_MESH ) {
// if (!SP_IS_MESH(this)) {
// this->ensureVector();
// } else {
// this->ensureArray();
// }
this->ensureVector();
}
// FIXME: climb up the ladder of hrefs
l = g_slist_prepend(l, child);
}
l = g_slist_reverse(l);
while (l) {
l = g_slist_remove(l, child);
}
}
}
{
if (SP_IS_STOP(ochild)) {
}
}
return first;
}
{
int count = 0;
for (SPStop *stop = const_cast<SPGradient*>(this)->getFirstStop(); stop && stop->getNextStop(); stop = stop->getNextStop()) {
count++;
}
return count;
}
/**
* Write gradient attributes to repr.
*/
Inkscape::XML::Node *SPGradient::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
{
if (flags & SP_OBJECT_WRITE_BUILD) {
if (crepr) {
l = g_slist_prepend(l, crepr);
}
}
while (l) {
l = g_slist_remove(l, l->data);
}
}
}
switch (this->units) {
break;
default:
break;
}
}
g_free(c);
}
/* FIXME: Ensure that this->spread is the inherited value
* if !this->spread_set. Not currently happening: see SPGradient::modified.
*/
switch (this->spread) {
break;
break;
default:
break;
}
}
if ( this->isSolid() ) {
} else {
}
} else {
}
return repr;
}
/**
* Forces the vector to be built, if not present (i.e., changed).
*
* \pre SP_IS_GRADIENT(gradient).
*/
{
}
}
/**
* Forces the array to be built, if not present (i.e., changed).
*
* \pre SP_IS_GRADIENT(gradient).
*/
{
//std::cout << "SPGradient::ensureArray()" << std::endl;
rebuildArray();
}
}
/**
* Set units property of gradient and emit modified.
*/
{
}
}
/**
* Set spread property of gradient and emit modified.
*/
{
spread_set = TRUE;
}
}
/**
* Returns the first of {src, src-\>ref-\>getObject(),
* src-\>ref-\>getObject()-\>ref-\>getObject(),...}
* for which \a match is true, or NULL if none found.
*
* The raison d'ĂȘtre of this routine is that it correctly handles cycles in the href chain (e.g., if
* a gradient gives itself as its href, or if each of two gradients gives the other as its href).
*
* \pre SP_IS_GRADIENT(src).
*/
static SPGradient *
{
/* Use a pair of pointers for detecting loops: p1 advances half as fast as p2. If there is a
loop, then once p1 has entered the loop, we'll detect it the next time the distance between
p1 and p2 is a multiple of the loop size. */
bool do1 = false;
for (;;) {
return p2;
}
if (!p2) {
return p2;
}
if (do1) {
}
/* We've been here before, so return NULL to indicate that no matching gradient found
* in the chain. */
return NULL;
}
}
}
/**
* True if gradient has stops.
*/
{
}
/**
* True if gradient has spread set.
*/
{
return gr->isSpreadSet();
}
/**
* True if gradient has units set.
*/
static bool
{
return gr->isUnitsSet();
}
{
if (force_vector) {
}
return src;
}
/**
* Returns the effective spread of given gradient (climbing up the refs chain if needed).
*
* \pre SP_IS_GRADIENT(gradient).
*/
{
return ( src
: SP_GRADIENT_SPREAD_PAD ); // pad is the default
}
/**
* Returns the effective units of given gradient (climbing up the refs chain if needed).
*
* \pre SP_IS_GRADIENT(gradient).
*/
{
return ( src
: SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ); // bbox is the default
}
/**
* Clears the gradient's svg:stop children from its repr.
*/
void
{
/* Collect stops from original repr */
}
}
/* Remove all stops */
while (sl) {
/** \todo
* fixme: This should work, unless we make gradient
* into generic group.
*/
}
}
/**
* Writes the gradient's internal vector (whether from its own stops, or
* inherited from refs) into the gradient repr as svg:stop elements.
*/
void
{
/* We have to be careful, as vector may be our own, so construct repr list at first */
/* strictly speaking, offset an SVG <number> rather than a CSS one, but exponents make no
* sense for offset proportions. */
os << "stop-color:" << gr->vector.stops[i].color.toString() << ";stop-opacity:" << gr->vector.stops[i].opacity;
/* Order will be reversed here */
}
/* And insert new children from list */
while (cl) {
}
}
{
if ( gradient->invalidateVector() ) {
// Conditional to avoid causing infinite loop if there's a cycle in the href chain.
}
}
/** Return true if change made. */
{
bool ret = false;
ret = true;
}
return ret;
}
/** Return true if change made. */
{
bool ret = false;
ret = true;
}
return ret;
}
/** Creates normalized color vector */
{
if (SP_IS_STOP(child)) {
len ++;
}
}
/* Copy vector from referenced gradient */
reffed->ensureVector();
return;
}
}
if (SP_IS_STOP(child)) {
// "Each gradient offset value is required to be equal to or greater than the
// previous gradient stop's offset value. If a given gradient stop's offset
// value is not equal to or greater than all previous offset values, then the
// offset value is adjusted to be equal to the largest of all previous offset
// values."
} else {
}
// "Gradient offset values less than 0 (or less than 0%) are rounded up to
// 0%. Gradient offset values greater than 1 (or greater than 100%) are rounded
// down to 100%."
}
}
// Normalize per section 13.2.4 of SVG 1.1.
/* "If no stops are defined, then painting shall occur as if 'none' were specified as the
* paint style."
*/
{
}
{
}
} else {
/* "If one stop is defined, then paint with the solid color fill using the color defined
* for that gradient stop."
*/
// If the first one is not at 0, then insert a copy of the first at 0.
}
// If the last one is not at 1, then insert a copy of the last at 1.
}
}
}
/** Creates normalized color mesh patch array */
{
// std::cout << "SPGradient::rebuildArray()" << std::endl;
if( !SP_IS_MESH(this) ) {
g_warning( "SPGradient::rebuildArray() called for non-mesh gradient" );
return;
}
has_patches = false;
if (SP_IS_MESHROW(ro)) {
has_patches = true;
// std::cout << " Has Patches" << std::endl;
break;
}
}
// MESH_FIXME: TO PROPERLY COPY
if ( !hasPatches() && reffed ) {
/* Copy array from referenced gradient */
reffed->ensureArray();
// if (!reffed->array.nodes.empty()) {
// array.built = reffed->array.built;
// for( uint i = 0; i < reffed->array.nodes.size(); ++i ) {
// array.nodes[i].assign(reffed->array.nodes[i].begin(), reffed->array.nodes[i].end());
// // FILL ME
// }
// return;
// }
}
}
{
} else {
return ctm;
}
}
{
return ( gr->gradientTransform
} else {
}
}
void
{
}
}
/* CAIRO RENDERING STUFF */
void
SPGradient *gr,
double opacity)
{
// set spread type
break;
break;
case SP_GRADIENT_SPREAD_PAD:
default:
break;
}
// add stops
{
// multiply stop opacity by paint opacity
}
// set pattern matrix
}
}
{
// CPPIFY
//if( gr->get_type() != SP_GRADIENT_TYPE_MESH ) {
if (!SP_IS_MESH(gr)) {
gr->ensureVector();
{
}
}
return pat;
}
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :