/* This file is part of the KDE project Copyright (C) 2002 - 2005, The Karbon Developers This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include "vdocument.h" #include "vglobal.h" #include "vgradient.h" #include #include #include int VGradient::VColorStopList::compareItems( TQPtrCollection::Item item1, TQPtrCollection::Item item2 ) { float r1 = ( (VColorStop*)item1 )->rampPoint; float r2 = ( (VColorStop*)item2 )->rampPoint; return ( r1 == r2 ? 0 : r1 < r2 ? -1 : 1 ); } // VGradient::VColorStopList::compareItems VGradient::VGradient( VGradientType type ) : m_type( type ) { m_colorStops.setAutoDelete( true ); // set up dummy gradient VColor color; color.set( 1.0, 0.0, 0.0 ); addStop( color, 0.0, 0.5 ); color.set( 1.0, 1.0, 0.0 ); addStop( color, 1.0, 0.5 ); setOrigin( KoPoint( 0, 0 ) ); setVector( KoPoint( 0, 50 ) ); setRepeatMethod( VGradient::reflect ); } VGradient::VGradient( const VGradient& gradient ) { m_colorStops.setAutoDelete( true ); m_origin = gradient.m_origin; m_focalPoint = gradient.m_focalPoint; m_vector = gradient.m_vector; m_type = gradient.m_type; m_repeatMethod = gradient.m_repeatMethod; m_colorStops.clear(); TQPtrVector cs = gradient.colorStops(); for( uint i = 0; i < cs.count(); i++ ) m_colorStops.append( new VColorStop( *cs[i] ) ); m_colorStops.sort(); } // VGradient::VGradient VGradient& VGradient::operator=( const VGradient& gradient ) { m_colorStops.setAutoDelete( true ); if ( this == &gradient ) return *this; m_origin = gradient.m_origin; m_focalPoint = gradient.m_focalPoint; m_vector = gradient.m_vector; m_type = gradient.m_type; m_repeatMethod = gradient.m_repeatMethod; m_colorStops.clear(); TQPtrVector cs = gradient.colorStops(); for( uint i = 0; i < cs.count(); i++ ) m_colorStops.append( new VColorStop( *cs[i] ) ); m_colorStops.sort(); return *this; } // VGradient::operator= const TQPtrVector VGradient::colorStops() const { TQPtrVector v; m_colorStops.toVector( &v ); v.setAutoDelete( false ); return v; } // VGradient::colorStops() void VGradient::clearStops() { m_colorStops.clear(); } void VGradient::addStop( const VColorStop& colorStop ) { m_colorStops.inSort( new VColorStop( colorStop ) ); } // VGradient::addStop void VGradient::addStop( const VColor &color, float rampPoint, float midPoint ) { // Clamping between 0.0 and 1.0 rampPoint = kMax( 0.0f, rampPoint ); rampPoint = kMin( 1.0f, rampPoint ); // Clamping between 0.0 and 1.0 midPoint = kMax( 0.0f, midPoint ); midPoint = kMin( 1.0f, midPoint ); // Work around stops with the same position VColorStop *v; for(v = m_colorStops.first(); v; v = m_colorStops.next()) { if(rampPoint == v->rampPoint) rampPoint += 0.001f; } m_colorStops.inSort( new VColorStop( rampPoint, midPoint, color ) ); } void VGradient::removeStop( const VColorStop& colorstop ) { m_colorStops.remove( &colorstop ); } // VGradient::removeStop void VGradient::save( TQDomElement& element ) const { TQDomElement me = element.ownerDocument().createElement( "GRADIENT" ); me.setAttribute( "originX", m_origin.x() ); me.setAttribute( "originY", m_origin.y() ); me.setAttribute( "focalX", m_focalPoint.x() ); me.setAttribute( "focalY", m_focalPoint.y() ); me.setAttribute( "vectorX", m_vector.x() ); me.setAttribute( "vectorY", m_vector.y() ); me.setAttribute( "type", m_type ); me.setAttribute( "repeatMethod", m_repeatMethod ); // save stops VColorStop* colorstop; TQPtrList& colorStops = const_cast( m_colorStops ); for( colorstop = colorStops.first(); colorstop; colorstop = colorStops.next() ) { TQDomElement stop = element.ownerDocument().createElement( "COLORSTOP" ); colorstop->color.save( stop ); stop.setAttribute( "ramppoint", colorstop->rampPoint ); stop.setAttribute( "midpoint", colorstop->midPoint ); me.appendChild( stop ); } element.appendChild( me ); } TQString VGradient::saveOasis( KoGenStyles &mainStyles ) const { bool radial = m_type == VGradient::radial; KoGenStyle gradientStyle( radial ? VDocument::STYLE_RADIAL_GRADIENT : VDocument::STYLE_LINEAR_GRADIENT /*no family name*/); if( radial ) { gradientStyle.addAttribute( "draw:style", "radial" ); gradientStyle.addAttributePt( "svg:cx", m_origin.x() ); gradientStyle.addAttributePt( "svg:cy", m_origin.y() ); double dx = m_vector.x() - m_origin.x(); double dy = m_vector.y() - m_origin.y(); gradientStyle.addAttributePt( "svg:r", sqrt( dx * dx + dy * dy ) ); gradientStyle.addAttributePt( "svg:fx", m_focalPoint.x() ); gradientStyle.addAttributePt( "svg:fy", m_focalPoint.y() ); } else { gradientStyle.addAttribute( "draw:style", "linear" ); gradientStyle.addAttributePt( "svg:x1", m_origin.x() ); gradientStyle.addAttributePt( "svg:y1", m_origin.y() ); gradientStyle.addAttributePt( "svg:x2", m_vector.x() ); gradientStyle.addAttributePt( "svg:y2", m_vector.y() ); } if( m_repeatMethod == VGradient::repeat ) gradientStyle.addAttribute( "svg:spreadMethod", "repeat" ); else if( m_repeatMethod == VGradient::reflect ) gradientStyle.addAttribute( "svg:spreadMethod", "reflect" ); else gradientStyle.addAttribute( "svg:spreadMethod", "pad" ); TQBuffer buffer; buffer.open( IO_WriteOnly ); KoXmlWriter elementWriter( TQT_TQIODEVICE(&buffer) ); // TODO pass indentation level // save stops VColorStop* colorstop; TQPtrList& colorStops = const_cast( m_colorStops ); for( colorstop = colorStops.first(); colorstop; colorstop = colorStops.next() ) { elementWriter.startElement( "svg:stop" ); elementWriter.addAttribute( "svg:offset", TQString( "%1" ).tqarg( colorstop->rampPoint ) ); elementWriter.addAttribute( "svg:color", TQColor( colorstop->color ).name() ); if( colorstop->color.opacity() < 1 ) elementWriter.addAttribute( "svg:stop-opacity", TQString( "%1" ).tqarg( colorstop->color.opacity() ) ); elementWriter.endElement(); } TQString elementContents = TQString::fromUtf8( buffer.buffer(), buffer.buffer().size() ); gradientStyle.addChildElement( "svg:stop", elementContents ); return mainStyles.lookup( gradientStyle, "gradient" ); } void VGradient::loadOasis( const TQDomElement &object, KoStyleStack &/*stack*/, VObject* tqparent ) { kdDebug(38000) << "namespaceURI: " << object.namespaceURI() << endl; kdDebug(38000) << "localName: " << object.localName() << endl; KoRect bb; if( tqparent ) bb = tqparent->boundingBox(); if( object.namespaceURI() == KoXmlNS::draw && object.localName() == "gradient" ) { m_repeatMethod = VGradient::reflect; TQString strType = object.attributeNS( KoXmlNS::draw, "style", TQString() ); if( strType == "radial" ) { m_type = VGradient::radial; // TODO : find out whether Oasis works with boundingBox only? double cx = KoUnit::parseValue( object.attributeNS( KoXmlNS::draw, "cx", TQString() ).remove("%") ); m_origin.setX( bb.bottomLeft().x() + bb.width() * 0.01 * cx ); double cy = KoUnit::parseValue( object.attributeNS( KoXmlNS::draw, "cy", TQString() ).remove("%") ); m_origin.setY( bb.bottomLeft().y() - bb.height() * 0.01 * cy ); m_focalPoint = m_origin; m_vector = bb.topRight(); } else if( strType == "linear" ) { m_type = VGradient::linear; double angle = 90 + object.attributeNS( KoXmlNS::draw, "angle", "0" ).toDouble(); double radius = 0.5 * sqrt( bb.width()*bb.width() + bb.height()*bb.height() ); double sx = cos( angle * VGlobal::pi / 180 ) * radius; double sy = sin( angle * VGlobal::pi / 180 ) * radius; m_origin.setX( bb.center().x() + sx ); m_origin.setY( bb.center().y() + sy ); m_vector.setX( bb.center().x() - sx ); m_vector.setY( bb.center().y() - sy ); m_focalPoint = m_origin; } else return; VColor startColor( TQColor( object.attributeNS( KoXmlNS::draw, "start-color", TQString() ) ) ); VColor endColor( TQColor( object.attributeNS( KoXmlNS::draw, "end-color", TQString() ) ) ); double startOpacity = 0.01 * object.attributeNS( KoXmlNS::draw, "start-intensity", "100" ).remove("%").toDouble(); double endOpacity = 0.01 * object.attributeNS( KoXmlNS::draw, "end-intensity", "100" ).remove("%").toDouble(); startColor.setOpacity( startOpacity ); endColor.setOpacity( endOpacity ); m_colorStops.clear(); addStop( startColor, 0.0, 0.5 ); addStop( endColor, 1.0, 0.0 ); m_colorStops.sort(); } else if( object.namespaceURI() == KoXmlNS::svg ) { if( object.localName() == "linearGradient" ) { m_type = VGradient::linear; m_origin.setX( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "x1", TQString() ) ) ); m_origin.setY( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "y1", TQString() ) ) ); m_vector.setX( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "x2", TQString() ) ) ); m_vector.setY( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "y2", TQString() ) ) ); m_focalPoint = m_origin; } else if( object.localName() == "radialGradient" ) { m_type = VGradient::radial; m_origin.setX( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "cx", TQString() ) ) ); m_origin.setY( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "cy", TQString() ) ) ); double r = KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "r", TQString() ) ); m_vector.setX( m_origin.x() + r ); m_vector.setY( m_origin.y() ); m_focalPoint.setX( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "fx", TQString() ) ) ); m_focalPoint.setY( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "fy", TQString() ) ) ); } TQString strSpread( object.attributeNS( KoXmlNS::svg, "spreadMethod", "pad" ) ); if( strSpread == "repeat" ) m_repeatMethod = VGradient::repeat; else if( strSpread == "reflect" ) m_repeatMethod = VGradient::reflect; else m_repeatMethod = VGradient::none; m_colorStops.clear(); // load stops TQDomNodeList list = object.childNodes(); for( uint i = 0; i < list.count(); ++i ) { if( list.item( i ).isElement() ) { TQDomElement colorstop = list.item( i ).toElement(); if( colorstop.namespaceURI() == KoXmlNS::svg && colorstop.localName() == "stop" ) { VColor color( TQColor( colorstop.attributeNS( KoXmlNS::svg, "color", TQString() ) ) ); color.setOpacity( colorstop.attributeNS( KoXmlNS::svg, "stop-opacity", "1.0" ).toDouble() ); addStop( color, colorstop.attributeNS( KoXmlNS::svg, "offset", "0.0" ).toDouble(), 0.5 ); } } } m_colorStops.sort(); } } void VGradient::load( const TQDomElement& element ) { m_origin.setX( element.attribute( "originX", "0.0" ).toDouble() ); m_origin.setY( element.attribute( "originY", "0.0" ).toDouble() ); m_focalPoint.setX( element.attribute( "focalX", "0.0" ).toDouble() ); m_focalPoint.setY( element.attribute( "focalY", "0.0" ).toDouble() ); m_vector.setX( element.attribute( "vectorX", "0.0" ).toDouble() ); m_vector.setY( element.attribute( "vectorY", "0.0" ).toDouble() ); m_type = (VGradientType)element.attribute( "type", 0 ).toInt(); m_repeatMethod = (VGradientRepeatMethod)element.attribute( "repeatMethod", 0 ).toInt(); m_colorStops.clear(); // load stops TQDomNodeList list = element.childNodes(); for( uint i = 0; i < list.count(); ++i ) { if( list.item( i ).isElement() ) { TQDomElement colorstop = list.item( i ).toElement(); if( colorstop.tagName() == "COLORSTOP" ) { VColor color; color.load( colorstop.firstChild().toElement() ); addStop( color, colorstop.attribute( "ramppoint", "0.0" ).toDouble(), colorstop.attribute( "midpoint", "0.5" ).toDouble() ); } } } m_colorStops.sort(); } void VGradient::transform( const TQWMatrix &m ) { m_origin = m_origin.transform( m ); m_focalPoint = m_focalPoint.transform( m ); m_vector = m_vector.transform( m ); }