gradient-chemistry.cpp revision 0de73848362e95b081e5fa85f910d6481094b2b9
/*
* Various utility methods for gradients
*
* Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
* bulia byak
* Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
* Jon A. Cruz <jon@joncruz.org>
* Abhishek Sharma
* Tavmjong Bah <tavmjong@free.fr>
*
* Copyright (C) 2012 Tavmjong Bah
* Copyright (C) 2010 Authors
* Copyright (C) 2007 Johan Engelen
* Copyright (C) 2001-2005 authors
* Copyright (C) 2001 Ximian, Inc.
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "widgets/gradient-vector.h"
#include "style.h"
#include "document-private.h"
#include "document-undo.h"
#include "desktop.h"
#include "desktop-style.h"
#include "ui/tools/tool-base.h"
#include "selection.h"
#include "verbs.h"
#include "sp-gradient-reference.h"
#include "sp-gradient-vector.h"
#include "sp-linear-gradient.h"
#include "sp-radial-gradient.h"
#include "sp-mesh-gradient.h"
#include "sp-stop.h"
#include "gradient-drag.h"
#include "gradient-chemistry.h"
#include "sp-text.h"
#include "sp-tspan.h"
#include "svg/svg-color.h"
#include "svg/css-ostringstream.h"
#include "preferences.h"
#define noSP_GR_VERBOSE
using Inkscape::DocumentUndo;
namespace {
std::vector<Inkscape::PaintTarget> vectorOfPaintTargets(paintTargetItems, paintTargetItems + (sizeof(paintTargetItems) / sizeof(paintTargetItems[0])));
} // namespace
namespace Inkscape {
{
return vectorOfPaintTargets;
}
} // namespace Inkscape
// Terminology:
//
// "vector" is a gradient that has stops but not position coords. It can be referenced by one or
//
// "private" is a gradient that has no stops but has position coords (e.g. center, radius etc for a
// radial). It references a vector for the actual colors. Each private is only used by one
// object. It is either linear or radial.
{
#ifdef SP_GR_VERBOSE
#endif
/* If we are already normalized vector, just return */
/* Fail, if we have wrong state set */
g_warning("file %s: line %d: Cannot normalize private gradient to vector (%s)", __FILE__, __LINE__, gr->getId());
return NULL;
}
/* First make sure we have vector directly defined (i.e. gr has its own stops) */
/* We do not have stops ourselves, so flatten stops as well */
gr->ensureVector();
// this adds stops from gr->vector as children to gr
}
/* If gr hrefs some other gradient, remove the href */
// We are hrefing someone, so require flattening
}
}
/* Everything is OK, set state flag */
return gr;
}
/**
* Creates new private gradient for the given vector
*/
static SPGradient *sp_gradient_get_private_normalized(SPDocument *document, SPGradient *vector, SPGradientType type)
{
#ifdef SP_GR_VERBOSE
#endif
// create a new private gradient of the requested type
if (type == SP_GRADIENT_TYPE_LINEAR) {
} else if(type == SP_GRADIENT_TYPE_RADIAL) {
} else {
}
// privates are garbage-collectable
// link to vector
/* Append the new private gradient to defs */
// get corresponding object
return gr;
}
/**
Count how many times gr is used by the styles of o and its descendants
*/
{
if (!o)
return 1;
guint i = 0;
if (style
{
i ++;
}
if (style
{
i ++;
}
}
return i;
}
/**
* If gr has other users, create a new private; also check if gr links to vector, relink if not
*/
{
#ifdef SP_GR_VERBOSE
#endif
// Orphaned gradient, no vector with stops at the end of the line; this used to be an assert
// but i think we should not abort on this - maybe just write a validity warning into some sort
// of log
return (gr);
}
// user is the object that uses this gradient; normally it's item but for tspans, we
// check its ancestor text so that tspans don't get different gradients from their
// texts.
while (SP_IS_TSPAN(user)) {
}
// Check the number of uses of the gradient within this object;
// if we are private and there are no other users,
// check vector
/* our href is not the vector, and vector is different from gr; relink */
}
return gr;
}
// we have to clone a fresh new private gradient for the given vector
// create an empty one
// copy all the attributes to it
if (SP_IS_RADIALGRADIENT(gr)) {
} else if (SP_IS_LINEARGRADIENT(gr)) {
} else {
}
return gr_new;
} else {
return gr;
}
}
{
#ifdef SP_GR_VERBOSE
#endif
// Some people actually prefer their gradient vectors to be shared...
return gr;
return gr_new;
}
return gr;
}
/**
* Obtain the vector from the gradient. A forked vector will be created and linked to this gradient if another gradient uses it.
*/
{
#ifdef SP_GR_VERBOSE
#endif
}
return vector;
}
/**
* Convert an item's gradient to userspace _without_ preserving coords, setting them to defaults
* instead. No forking or reapplying is done because this is only called for newly created privates.
* @return The new gradient.
*/
{
#ifdef SP_GR_VERBOSE
#endif
// calculate the bbox of the item
if (!bbox)
return gr;
if (SP_IS_RADIALGRADIENT(gr)) {
// we want it to be elliptic, not circular
{
g_free(c);
}
} else if (SP_IS_LINEARGRADIENT(gr)) {
// Assume horizontal gradient by default (as per SVG 1.1)
// Get the preferred gradient angle from prefs
if (angle != 0.0) {
// Find where our gradient line intersects the bounding box.
}
}
}
}
} else {
// Mesh
// THIS IS BEING CALLED TWICE WHENEVER A NEW GRADIENT IS CREATED, WRITING HERE CAUSES PROBLEMS
// IN SPMeshNodeArray::create()
//sp_repr_set_svg_double(repr, "x", bbox->min()[Geom::X]);
//sp_repr_set_svg_double(repr, "y", bbox->min()[Geom::Y]);
}
// set the gradientUnits
return gr;
}
/**
* Convert an item's gradient to userspace if necessary, also fork it if necessary.
* @return The new gradient.
*/
{
#ifdef SP_GR_VERBOSE
#endif
return gr;
}
// First, fork it if it is shared
// calculate the bbox of the item
if ( bbox ) {
} else {
// would be degenerate otherwise
}
/* skew is the additional transform, defined by the proportions of the item, that we need
* to apply to the gradient in order to work around this weird bit from SVG 1.1
* (http://www.w3.org/TR/SVG11/pservers.html#LinearGradients):
*
* When gradientUnits="objectBoundingBox" and gradientTransform is the identity
* matrix, the stripes of the linear gradient are perpendicular to the gradient
* vector in object bounding box space (i.e., the abstract coordinate system where
* square, the stripes that are conceptually perpendicular to the gradient vector
* within object bounding box space will render non-perpendicular relative to the
* gradient vector in user space due to application of the non-uniform scaling
* transformation from bounding box space to user space.
*/
skew[4] = 0;
skew[5] = 0;
// apply skew to the gradient
{
g_free(c);
}
// Matrix to convert points to userspace coords; postmultiply by inverse of skew so
// as to cancel it out when it's applied to the gradient during rendering
if (SP_IS_RADIALGRADIENT(gr)) {
// original points in the bbox coords
// converted points in userspace coords
} else {
}
// set the gradientUnits
}
// apply the gradient to the item (may be necessary if we forked it); not recursive
// generally because grouped items will be taken care of later (we're being called
// from sp_item_adjust_paint_recursive); however text and all its children should all
// want to access tspans and set gradients on them separately)
if (SP_IS_TEXT(item)) {
} else {
}
return gr;
}
{
#ifdef SP_GR_VERBOSE
#endif
if (set) {
} else {
}
g_free(c);
}
{
SPGradient *gradient = 0;
switch (fill_or_stroke)
{
if ( SP_IS_GRADIENT(server) ) {
}
}
break;
case Inkscape::FOR_STROKE:
if ( SP_IS_GRADIENT(server) ) {
}
}
break;
}
return gradient;
}
{
return stop;
}
return NULL;
}
{
if (!stop) {
return NULL;
}
// if this is valid but weird gradient without an offset-zero stop element,
// inkscape has created a handle for the start of gradient anyway,
// so when it asks for stop N that corresponds to stop element N-1
{
stop_i--;
}
if (!stop) {
return NULL;
}
}
return stop;
}
{
return SP_RGBA32_U_COMPOSE(r, g, b, a);
}
{
#ifdef SP_GR_VERBOSE
#endif
guint32 cnew = average_color (c1, c2, (offset - prev_stop->offset) / (next_stop->offset - prev_stop->offset));
gchar c[64];
sp_svg_write_color (c, sizeof(c), cnew);
return newstop;
}
void sp_item_gradient_edit_stop(SPItem *item, GrPointType point_type, guint point_i, Inkscape::PaintTarget fill_or_stroke)
{
return;
}
switch (point_type) {
case POINT_LG_BEGIN:
case POINT_RG_CENTER:
case POINT_RG_FOCUS:
{
}
break;
case POINT_LG_END:
case POINT_RG_R1:
case POINT_RG_R2:
{
}
break;
case POINT_LG_MID:
case POINT_RG_MID1:
case POINT_RG_MID2:
{
}
break;
default:
g_warning( "Unhandled gradient handle" );
break;
}
}
guint32 sp_item_gradient_stop_query_style(SPItem *item, GrPointType point_type, guint point_i, Inkscape::PaintTarget fill_or_stroke)
{
return 0;
}
if (!vector) // orphan!
return 0; // what else to do?
switch (point_type) {
case POINT_LG_BEGIN:
case POINT_RG_CENTER:
case POINT_RG_FOCUS:
{
if (first) {
return first->get_rgba32();
}
}
break;
case POINT_LG_END:
case POINT_RG_R1:
case POINT_RG_R2:
{
if (last) {
return last->get_rgba32();
}
}
break;
case POINT_LG_MID:
case POINT_RG_MID1:
case POINT_RG_MID2:
{
if (stopi) {
return stopi->get_rgba32();
}
}
break;
default:
break;
}
return 0;
} else if (SP_IS_MESHGRADIENT(gradient)) {
// Mesh gradient
switch (point_type) {
case POINT_MG_CORNER: {
return 0;
}
if (cornerpoint) {
} else {
return 0;
}
break;
}
case POINT_MG_HANDLE:
case POINT_MG_TENSOR:
{
// Do nothing. Handles and tensors don't have color
break;
}
default:
g_warning( "Bad mesh handle type" );
}
return 0;
}
return 0;
}
void sp_item_gradient_stop_set_style(SPItem *item, GrPointType point_type, guint point_i, Inkscape::PaintTarget fill_or_stroke, SPCSSAttr *stop)
{
#ifdef SP_GR_VERBOSE
g_message("sp_item_gradient_stop_set_style(%p, %d, %d, %d, %p)", item, point_type, point_i, fill_or_stroke, stop);
#endif
return;
if (!vector) // orphan!
return;
}
switch (point_type) {
case POINT_LG_BEGIN:
case POINT_RG_CENTER:
case POINT_RG_FOCUS:
{
if (first) {
}
}
break;
case POINT_LG_END:
case POINT_RG_R1:
case POINT_RG_R2:
{
if (last) {
}
}
break;
case POINT_LG_MID:
case POINT_RG_MID1:
case POINT_RG_MID2:
{
if (stopi) {
}
}
break;
default:
break;
}
} else {
// Mesh gradient
bool changed = false;
switch (point_type) {
case POINT_MG_CORNER: {
if( color_str ) {
}
changed = true;
}
if( opacity_str ) {
double opacity = 1.0;
changed = true;
}
if( changed ) {
}
break;
}
case POINT_MG_HANDLE:
case POINT_MG_TENSOR:
{
// Do nothing. Handles and tensors don't have colors.
break;
}
default:
g_warning( "Bad mesh handle type" );
}
}
}
{
#ifdef SP_GR_VERBOSE
#endif
return;
if (!vector) // orphan!
return;
}
double offset;
offset=0;
}
}
child->deleteObject();
}
--iter;
}
}
{
#ifdef SP_GR_VERBOSE
#endif
return;
if (!vector) // orphan!
return;
}
if (SP_IS_STOP(child)) {
//g_message("Stop color %d", color);
gchar c[64];
sp_svg_write_color (c, sizeof(c),
)
);
}
}
}
/**
Set the position of point point_type of the gradient applied to item (either fill_or_stroke) to
p_w (in desktop coordinates). Write_repr if you want the change to become permanent.
*/
void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint point_i, Geom::Point p_w, Inkscape::PaintTarget fill_or_stroke, bool write_repr, bool scale)
{
#ifdef SP_GR_VERBOSE
g_message("sp_item_gradient_set_coords(%p, %d, %d, (%f, %f), ...)", item, point_type, point_i, p_w[Geom::X], p_w[Geom::Y] );
#endif
return;
// Needed only if units are set to SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX
gradient = sp_gradient_convert_to_userspace(gradient, item, (fill_or_stroke == Inkscape::FOR_FILL) ? "fill" : "stroke");
// now p is in gradient's original coordinates
if (SP_IS_LINEARGRADIENT(gradient)) {
switch (point_type) {
case POINT_LG_BEGIN:
if (scale) {
}
if (write_repr) {
if (scale) {
}
} else {
}
break;
case POINT_LG_END:
if (scale) {
}
if (write_repr) {
if (scale) {
}
} else {
}
break;
case POINT_LG_MID:
{
// using X-coordinates only to determine the offset, assuming p has been snapped to the vector from begin to end.
lg->ensureVector();
if (write_repr) {
} else {
}
}
break;
default:
g_warning( "Bad linear gradient handle type" );
break;
}
} else if (SP_IS_RADIALGRADIENT(gradient)) {
// prevent setting a radius too close to the center
return;
}
bool transform_set = false;
switch (point_type) {
case POINT_RG_CENTER:
if (write_repr) {
} else {
}
break;
case POINT_RG_FOCUS:
if (write_repr) {
} else {
}
break;
case POINT_RG_R1:
{
transform_set = true;
break;
}
case POINT_RG_R2:
{
transform_set = true;
break;
}
case POINT_RG_MID1:
{
rg->ensureVector();
if (write_repr) {
} else {
}
break;
}
case POINT_RG_MID2:
{
rg->ensureVector();
if (write_repr) {
} else {
}
break;
}
default:
g_warning( "Bad radial gradient handle type" );
break;
}
if (transform_set) {
if (write_repr) {
g_free(s);
} else {
}
}
} else if (SP_IS_MESHGRADIENT(gradient)) {
//Geom::Affine new_transform;
//bool transform_set = false;
switch (point_type) {
case POINT_MG_CORNER:
{
// Handles are moved in gradient-drag.cpp
break;
}
case POINT_MG_HANDLE: {
break;
}
case POINT_MG_TENSOR: {
break;
}
default:
g_warning( "Bad mesh handle type" );
}
if( write_repr ) {
//std::cout << "Write mesh repr" << std::endl;
}
}
}
{
if (gradient) {
}
return NULL;
}
{
if (gradient) {
}
return spread;
}
/**
Returns the position of point point_type of the gradient applied to item (either fill_or_stroke),
in desktop coordinates.
*/
Geom::Point getGradientCoords(SPItem *item, GrPointType point_type, guint point_i, Inkscape::PaintTarget fill_or_stroke)
{
#ifdef SP_GR_VERBOSE
#endif
if (!gradient)
return p;
if (SP_IS_LINEARGRADIENT(gradient)) {
switch (point_type) {
case POINT_LG_BEGIN:
break;
case POINT_LG_END:
break;
case POINT_LG_MID:
{
g_message("POINT_LG_MID bug trigger, see LP bug #453067");
break;
}
p = (1-offset) * Geom::Point(lg->x1.computed, lg->y1.computed) + offset * Geom::Point(lg->x2.computed, lg->y2.computed);
}
break;
default:
g_warning( "Bad linear gradient handle type" );
break;
}
} else if (SP_IS_RADIALGRADIENT(gradient)) {
switch (point_type) {
case POINT_RG_CENTER:
break;
case POINT_RG_FOCUS:
break;
case POINT_RG_R1:
break;
case POINT_RG_R2:
break;
case POINT_RG_MID1:
{
g_message("POINT_RG_MID1 bug trigger, see LP bug #453067");
break;
}
p = (1-offset) * Geom::Point (rg->cx.computed, rg->cy.computed) + offset * Geom::Point(rg->cx.computed + rg->r.computed, rg->cy.computed);
}
break;
case POINT_RG_MID2:
{
g_message("POINT_RG_MID2 bug trigger, see LP bug #453067");
break;
}
p = (1-offset) * Geom::Point (rg->cx.computed, rg->cy.computed) + offset * Geom::Point(rg->cx.computed, rg->cy.computed - rg->r.computed);
}
break;
default:
g_warning( "Bad radial gradient handle type" );
break;
}
} else if (SP_IS_MESHGRADIENT(gradient)) {
switch (point_type) {
case POINT_MG_CORNER:
break;
case POINT_MG_HANDLE: {
break;
}
case POINT_MG_TENSOR: {
break;
}
default:
g_warning( "Bad mesh handle type" );
}
}
if (bbox) {
}
}
return p;
}
/**
* Sets item fill or stroke to the gradient of the specified type with given vector, creating
* new private gradient, if needed.
* gr has to be a normalized vector.
*/
SPGradient *sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, Inkscape::PaintTarget fill_or_stroke)
{
#ifdef SP_GR_VERBOSE
#endif
if ((fill_or_stroke == Inkscape::FOR_FILL) ? style->fill.isPaintserver() : style->stroke.isPaintserver()) {
ps = (fill_or_stroke == Inkscape::FOR_FILL) ? SP_STYLE_FILL_SERVER(style) : SP_STYLE_STROKE_SERVER(style);
}
if (ps
{
/* Current fill style is the gradient of the required type */
//g_message("hrefcount %d count %d\n", current->hrefcount, count_gradient_hrefs(item, current));
// current is private and it's either used once, or all its uses are by children of item;
// so just change its href to vector
// href is not the vector
}
return current;
} else {
// the gradient is not private, or it is shared with someone else;
// normalize it (this includes creating new private if necessary)
if (normalized != current) {
/* We have to change object style here; recursive because this is used from
* fill&stroke and must work for groups etc. */
sp_style_set_property_url(item, (fill_or_stroke == Inkscape::FOR_FILL) ? "fill" : "stroke", normalized, true);
}
return normalized;
}
} else {
/* Current fill style is not a gradient or wrong type, so construct everything */
/* This is where mesh gradients are constructed. */
sp_style_set_property_url(item, ( (fill_or_stroke == Inkscape::FOR_FILL) ? "fill" : "stroke" ), constructed, true);
return constructed;
}
}
{
#ifdef SP_GR_VERBOSE
#endif
if (link) {
}
if (link) {
} else {
}
}
static void addStop( Inkscape::XML::Node *parent, Glib::ustring const &color, gint opacity, gchar const *offset )
{
#ifdef SP_GR_VERBOSE
#endif
{
}
}
/*
* Get default normalized gradient vector of document, create if there is none
*/
SPGradient *sp_document_default_gradient_vector( SPDocument *document, SPColor const &color, bool singleStop )
{
if ( !singleStop ) {
// set here, but removed when it's edited in the gradient editor
// to further reduce clutter, we could
// (1) here, search gradients by color and return what is found without duplication
// (2) in fill & stroke, show only one copy of each gradient in list
}
if ( !singleStop ) {
}
/* fixme: This does not look like nice */
/* fixme: Maybe add extra sanity check here */
return gr;
}
{
} else {
// take the color of the object
if (paint.isPaintserver()) {
SPObject *server = (fill_or_stroke == Inkscape::FOR_FILL) ? o->style->getFillPaintServer() : o->style->getStrokePaintServer();
if ( SP_IS_GRADIENT(server) ) {
} else {
}
} else {
// if o doesn't use flat color, then take current color of the desktop.
}
}
}
void sp_gradient_invert_selected_gradients(SPDesktop *desktop, Inkscape::PaintTarget fill_or_stroke)
{
}
// we did an undoable action
_("Invert gradient colors"));
}
{
if (!ev) {
return;
}
// First try selected dragger
} else { // If no drag or no dragger selected, act on selection (both fill and stroke gradients)
}
}
// we did an undoable action
_("Reverse gradient"));
}
{
if (doc) {
_("Delete swatch"));
break;
}
}
}
}
/*
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 :