gradient-chemistry.cpp revision 96d6bc601d13d7b733fbd67725bcc3e83fb96e24
#define __SP_GRADIENT_CHEMISTRY_C__
/*
* Various utility methods for gradients
*
* Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
* bulia byak
* Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
*
* 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 "style.h"
#include "document-private.h"
#include "desktop-style.h"
#include "sp-gradient-reference.h"
#include "sp-gradient-vector.h"
#include "sp-linear-gradient.h"
#include "sp-radial-gradient.h"
#include "sp-stop.h"
#include "widgets/gradient-vector.h"
#include "sp-text.h"
#include "sp-tspan.h"
#include <libnr/nr-matrix-fns.h>
#include "svg/svg-color.h"
// 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.
{
/* 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__, SP_OBJECT_ID(gr));
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 */
// 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 *
{
// create a new private gradient of the requested type
if (type == SP_GRADIENT_TYPE_LINEAR) {
} 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
*/
{
// 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 {
}
return gr_new;
} else {
return gr;
}
}
{
return gr_new;
}
return gr;
}
/**
* 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.
*/
{
// calculate the bbox of the item
NR::Maybe<NR::Rect> bbox = item->getBounds(NR::identity()); // we need "true" bbox without item_i2d_affine
if (SP_IS_RADIALGRADIENT(gr)) {
// we want it to be elliptic, not circular
{
g_free(c);
}
} else {
}
// set the gradientUnits
return gr;
}
/**
* Convert an item's gradient to userspace if necessary, also fork it if necessary.
* @return The new gradient.
*/
{
// First, fork it if it is shared
// calculate the bbox of the item
NR::Maybe<NR::Rect> bbox = item->getBounds(NR::identity()); // we need "true" bbox without item_i2d_affine
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;
}
void
{
if (set) {
} else {
}
g_free(c);
}
{
if (fill_or_stroke) {
if (SP_IS_GRADIENT (server)) {
}
}
} else {
if (SP_IS_GRADIENT (server)) {
}
}
}
return gradient;
}
{
for (SPObject *ochild = sp_object_first_child(gradient); ochild != NULL; ochild = SP_OBJECT_NEXT(ochild)) {
if (SP_IS_STOP (ochild))
}
return NULL;
}
{
return NULL;
for ( SPObject *ochild = sp_object_first_child(SP_OBJECT(gradient)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) {
if (SP_IS_STOP (ochild)) {
}
break;
}
}
}
{
if (SP_IS_STOP (ochild))
}
return NULL;
}
{
return stop;
}
return NULL;
}
{
}
return stop;
}
void
{
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:
break;
}
}
sp_item_gradient_stop_query_style (SPItem *item, guint point_type, guint point_i, bool 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 sp_stop_get_rgba32(first);
}
}
break;
case POINT_LG_END:
case POINT_RG_R1:
case POINT_RG_R2:
{
if (last) {
return sp_stop_get_rgba32(last);
}
}
break;
case POINT_LG_MID:
case POINT_RG_MID1:
case POINT_RG_MID2:
{
if (stopi) {
return sp_stop_get_rgba32(stopi);
}
}
break;
default:
break;
}
return 0;
}
void
sp_item_gradient_stop_set_style (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke, SPCSSAttr *stop)
{
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;
}
}
void
{
return;
if (!vector) // orphan!
return;
}
}
}
child->deleteObject();
}
iter --;
}
}
// FIXME: make general global function
static double
{
if (r < 0.0) return 0.0;
if (r > length) return 1.0;
return (r / length);
}
/**
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, guint point_type, guint point_i, NR::Point p_w, bool fill_or_stroke, bool write_repr, bool scale)
{
return;
// 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.
double offset = get_offset_between_points (p, NR::Point(lg->x1.computed, lg->y1.computed), NR::Point(lg->x2.computed, lg->y2.computed));
if (write_repr) {
} else {
}
}
break;
default:
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:
{
if (write_repr) {
} else {
}
break;
}
case POINT_RG_MID2:
if (write_repr) {
} else {
}
break;
}
if (transform_set) {
if (write_repr) {
g_free(s);
} else {
}
}
}
}
{
if (gradient)
return sp_gradient_get_vector (gradient, false);
return NULL;
}
{
if (gradient)
return sp_gradient_get_spread (gradient);
return SP_GRADIENT_SPREAD_PAD;
}
/**
Returns the position of point point_type of the gradient applied to item (either fill_or_stroke),
in desktop coordinates.
*/
{
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:
{
p = (1-offset) * NR::Point(lg->x1.computed, lg->y1.computed) + offset * NR::Point(lg->x2.computed, lg->y2.computed);
}
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:
{
p = (1-offset) * NR::Point (rg->cx.computed, rg->cy.computed) + offset * NR::Point(rg->cx.computed + rg->r.computed, rg->cy.computed);
}
break;
case POINT_RG_MID2:
{
p = (1-offset) * NR::Point (rg->cx.computed, rg->cy.computed) + offset * NR::Point(rg->cx.computed, rg->cy.computed - rg->r.computed);
}
break;
}
}
NR::Maybe<NR::Rect> bbox = item->getBounds(NR::identity()); // we need "true" bbox without item_i2d_affine
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.
*/
{
if (style_type == SP_PAINT_TYPE_PAINTSERVER)
if (ps
{
/* Current fill style is the gradient of the required type */
//g_print("hrefcount %d count %d\n", SP_OBJECT_HREFCOUNT(ig), count_gradient_hrefs(SP_OBJECT(item), ig));
// 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(SP_OBJECT(item), is_fill? "fill" : "stroke", SP_OBJECT(normalized), true);
}
return normalized;
}
} else {
/* Current fill style is not a gradient or wrong type, so construct everything */
sp_style_set_property_url(SP_OBJECT(item), ( is_fill ? "fill" : "stroke" ), SP_OBJECT(constructed), true);
return constructed;
}
}
static void
{
if (link) {
*ref = '#';
} else {
}
}
/*
* Get default normalized gradient vector of document, create if there is none
*/
{
// 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
gchar b[64];
{
g_free(t);
}
{
g_free(t);
}
/* fixme: This does not look like nice */
SPGradient *gr;
/* fixme: Maybe add extra sanity check here */
return gr;
}
/**
Return the preferred vector for \a o, made from (in order of preference) its current vector,
current fill or stroke color, or from desktop style if \a o is NULL or doesn't have style.
*/
{
} else {
// take the color of the object
if (SP_IS_GRADIENT (server)) {
} else {
}
} else {
// if o doesn't use flat color, then take current color of the desktop.
}
}
}
{
}
return vector;
}
/*
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:encoding=utf-8:textwidth=99 :