include/sal/log-areas.dox               |    1 
 oox/Library_oox.mk                      |    6 
 oox/source/drawingml/fillproperties.cxx |  212 ++++++++++++++++++++++++++++----
 3 files changed, 193 insertions(+), 26 deletions(-)

New commits:
commit f4a2f1e647354efb75be8c90384d6cd3e5f9b9bd
Author: Tor Lillqvist <t...@collabora.com>
Date:   Mon Jul 14 20:23:22 2014 +0300

    bnc#862510: Improve handling of OOXML gradients
    
    OOXML gradients can have an arbitrary number of "stops". LibreOffice 
gradients
    have just a start and end colour, plus an optional uniformly coloured border
    (on the "start" side). In addition, LibreOffice has the "axial" gradient 
mode,
    which means the gradient is reflected in the middle.
    
    It is thus obviously impossible in general to losslessly map OOXML gradients
    to LibreOffice ones. But let's try a bit harder than earlier to get visually
    more similar result, in at least some simple sample cases.
    
    We look for the widest gradient segment and use that for the start and end
    colours of the LibreOffice gradient.
    
    Also, map an OOXML gradient to an axial LibreOffice gradient only if it is
    symmetrical. Also, use the border property when suitable. In general, look 
for
    the widest OOXML gradient segment (once a segment corresponding to the
    LibreOffice gradient border, if any, has been accounted for) and use that as
    the LibreOffice gradient.
    
    Possibly some perceptionally better heuristic should be used... Like, if we
    have a three-segment gradient, with a wide gradient segment between two
    visually very similar colours (for example, two shades of red), and a 
narrower
    segment ending with a visually very different colour (for example, yellow), 
it
    probably would be best to represent that in LibreOffice as a gradient from 
the
    first red shade to yellow, instead of as a gradient between the two shades 
of
    red. Or even, if a first or last gradient segment is between very similar
    colours, equalize those start and end colours, thus using a border colour in
    LibreOffice instead. The possibilities for bikeshedding are endless.
    
    I am sure there are instances where the old code (by accident?) produced
    visually more pleasing results... But hopefully this works more pleasingly 
and
    consistently in a larger number of cases.
    
    Change-Id: If153e986ad943454307e3ba718479d5ac4cdc7ab

diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index c22b8e6..e09e2c5 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -179,6 +179,7 @@ certain functionality.
 @li @c oox.cscode - see oox/source/drawingml/customshapes/README
 @li @c oox.csdata - see oox/source/drawingml/customshapes/README
 @li @c oox.drawingml - DrawingML
+@li @c oox.drawingml.gradient
 @li @c oox.ppt - pptx filter
 @li @c oox.storage - ZipStorage class
 @li @c oox.xmlstream - XmlStream class
diff --git a/oox/Library_oox.mk b/oox/Library_oox.mk
index f356565..17fb398 100644
--- a/oox/Library_oox.mk
+++ b/oox/Library_oox.mk
@@ -59,16 +59,18 @@ $(eval $(call gb_Library_use_libraries,oox,\
        $(gb_UWINAPI) \
 ))
 
-ifeq ($(TLS),OPENSSL)
 $(eval $(call gb_Library_use_externals,oox,\
        boost_headers \
+))
+
+ifeq ($(TLS),OPENSSL)
+$(eval $(call gb_Library_use_externals,oox,\
        openssl \
        openssl_headers \
 ))
 else
 ifeq ($(TLS),NSS)
 $(eval $(call gb_Library_use_externals,oox,\
-       boost_headers \
        plc4 \
        nss3 \
 ))
diff --git a/oox/source/drawingml/fillproperties.cxx 
b/oox/source/drawingml/fillproperties.cxx
index 6507e6f..361abf8 100644
--- a/oox/source/drawingml/fillproperties.cxx
+++ b/oox/source/drawingml/fillproperties.cxx
@@ -17,6 +17,9 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
+#include <iterator>
+#include <boost/utility.hpp>
+
 #include "oox/drawingml/fillproperties.hxx"
 
 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
@@ -330,8 +333,8 @@ void FillProperties::pushToPropMap( ShapePropertyMap& 
rPropMap,
                     aGradient.StartIntensity = 100;
                     aGradient.EndIntensity = 100;
 
-                    size_t nColorCount = 
maGradientProps.maGradientStops.size();
-                    if( nColorCount > 1 )
+                    // Old code, values in aGradient overwritten in many cases 
by newer code below
+                    if( maGradientProps.maGradientStops.size() > 1 )
                     {
                         aGradient.StartColor = 
maGradientProps.maGradientStops.begin()->second.getColor( rGraphicHelper, 
nPhClr );
                         aGradient.EndColor = 
maGradientProps.maGradientStops.rbegin()->second.getColor( rGraphicHelper, 
nPhClr );
@@ -367,33 +370,194 @@ void FillProperties::pushToPropMap( ShapePropertyMap& 
rPropMap,
                     }
                     else
                     {
-                        /*  Try to detect a VML axial gradient. This type of
-                            gradient is simulated by a 3-point linear gradient.
-                            Even if it's a multi-color linear gradient, its 
probably better to assume
-                            axial gradient, when there are 3 or more points.
-                         */
-                        bool bAxial = (nColorCount >= 3);
-                        aGradient.Style = bAxial ? awt::GradientStyle_AXIAL : 
awt::GradientStyle_LINEAR;
-                        nDmlAngle = maGradientProps.moShadeAngle.get( 0 ) - 
nShapeRotation;
-                        // convert DrawingML angle (in 1/60000 degrees) to API 
angle (in 1/10 degrees)
-                        aGradient.Angle = static_cast< sal_Int16 >( (4500 - 
(nDmlAngle / (PER_DEGREE / 10))) % 3600 );
-                        if( bAxial )
+                        // A copy of the gradient stops for local modification
+                        GradientFillProperties::GradientStopMap 
aGradientStops(maGradientProps.maGradientStops);
+
+                        // Add a fake gradient stop at 0% and 100% if 
necessary, so that the gradient always starts
+                        // at 0% and ends at 100%, to make following logic 
clearer (?).
+                        if( aGradientStops.find(0.0) == aGradientStops.end() )
+                        {
+                            // temp variable required
+                            Color aFirstColor(aGradientStops.begin()->second);
+                            aGradientStops[0.0] = aFirstColor;
+                        }
+
+                        if( aGradientStops.find(1.0) == aGradientStops.end() )
+                        {
+                            // ditto
+                            Color aLastColor(aGradientStops.rbegin()->second);
+                            aGradientStops[1.0] = aLastColor;
+                        }
+
+                        // Check if the gradient is symmetric, which we will 
emulate with an "axial" gradient.
+                        bool bSymmetric(true);
+                        {
+                            
GradientFillProperties::GradientStopMap::const_iterator aItA( 
aGradientStops.begin() );
+                            
GradientFillProperties::GradientStopMap::const_iterator aItZ( boost::prior( 
aGradientStops.end() ) );
+                            while( bSymmetric && aItA->first < aItZ->first )
+                            {
+                                if( aItA->second.getColor( rGraphicHelper, 
nPhClr ) != aItZ->second.getColor( rGraphicHelper, nPhClr ) ||
+                                    aItA->second.getTransparency() != 
aItZ->second.getTransparency() )
+                                    bSymmetric = false;
+                                else
+                                {
+                                    aItA++;
+                                    aItZ = boost::prior(aItZ);
+                                }
+                            }
+                            // Don't be fooled if the middlemost stop isn't at 
0.5.
+                            if( bSymmetric && aItA == aItZ && aItA->first != 
0.5 )
+                                bSymmetric = false;
+
+                            // If symmetric, do the rest of the logic for just 
a half.
+                            if( bSymmetric )
+                            {
+                                // aItZ already points to the colour for the 
middle, but insert a fake stop at the
+                                // exact middle if necessary.
+                                if( aItA->first != aItZ->first )
+                                {
+                                    Color aMiddleColor = aItZ->second;
+                                    aGradientStops[0.5] = aMiddleColor;
+                                }
+                                // Drop the rest of the stops
+                                while( aGradientStops.rbegin()->first > 0.5 )
+                                    aGradientStops.erase( 
aGradientStops.rbegin()->first );
+                            }
+                        }
+
+                        SAL_INFO("oox.drawingml.gradient", "symmetric: " << 
(bSymmetric ? "YES" : "NO") <<
+                                 ", number of stops: " << 
aGradientStops.size());
+                        for (GradientFillProperties::GradientStopMap::iterator 
p(aGradientStops.begin());
+                             p != aGradientStops.end();
+                             p++)
+                            SAL_INFO("oox.drawingml.gradient", "  " << 
std::distance(aGradientStops.begin(), p) << ": " <<
+                                     p->first << ": " <<
+                                     std::hex << p->second.getColor( 
rGraphicHelper, nPhClr ) << std::dec <<
+                                     "@" << (100-p->second.getTransparency()) 
<< "%");
+
+                        // Now estimate the simple LO style gradient (only two 
stops, at n% and 100%, where n ==
+                        // the "border") that best emulates the gradient 
between begin() and prior(end()).
+
+                        // First look for the largest segment in the gradient.
+                        
GradientFillProperties::GradientStopMap::const_iterator 
aIt(aGradientStops.begin());
+                        double nWidestWidth = -1;
+                        
GradientFillProperties::GradientStopMap::const_iterator aWidestSegmentStart;
+                        aIt++;
+                        while( aIt != aGradientStops.end() )
+                        {
+                            if( aIt->first - boost::prior(aIt)->first > 
nWidestWidth )
+                            {
+                                nWidestWidth = aIt->first - 
boost::prior(aIt)->first;
+                                aWidestSegmentStart = boost::prior(aIt);
+                            }
+                            aIt++;
+                        }
+                        assert( nWidestWidth > 0 );
+
+                        double nBorder = 0;
+                        bool bSwap(false);
+
+                        // Do we have just two segments, and either one is of 
uniform colour, or three or more
+                        // segments, and the widest one is the first or last 
one, and is it of uniform colour? If
+                        // so, deduce the border from it, and drop that 
segment.
+                        if( aGradientStops.size() == 3 &&
+                            aGradientStops.begin()->second.getColor( 
rGraphicHelper, nPhClr ) == 
boost::next(aGradientStops.begin())->second.getColor( rGraphicHelper, nPhClr ) 
&&
+                            aGradientStops.begin()->second.getTransparency() 
== boost::next(aGradientStops.begin())->second.getTransparency( ) )
+                        {
+                            // Two segments, first is uniformly coloured
+                            SAL_INFO("oox.drawingml.gradient", "two segments, 
first is uniformly coloured");
+                            nBorder = 
boost::next(aGradientStops.begin())->first - aGradientStops.begin()->first;
+                            aGradientStops.erase(aGradientStops.begin());
+                            aWidestSegmentStart = aGradientStops.begin();
+                        }
+                        else if( !bSymmetric &&
+                                 aGradientStops.size() == 3 &&
+                                 
boost::next(aGradientStops.begin())->second.getColor( rGraphicHelper, nPhClr ) 
== boost::prior(aGradientStops.end())->second.getColor( rGraphicHelper, nPhClr 
) &&
+                                 
boost::next(aGradientStops.begin())->second.getTransparency() == 
boost::prior(aGradientStops.end())->second.getTransparency( ) )
                         {
-                            
GradientFillProperties::GradientStopMap::const_iterator aIt = 
maGradientProps.maGradientStops.begin();
-                            // Try to find the axial median
-                            for(size_t i=0;i<nColorCount;i+=3)
-                                ++aIt;
-                            // API StartColor is inner color in axial gradient
-                            // aIt->second.hasColor() kind would be better 
than Color != API_RGB_WHITE
-                            if( aGradient.StartColor == aGradient.EndColor &&
-                                ( !aIt->second.hasTransparency() || 
aIt->second.getColor( rGraphicHelper, nPhClr ) != API_RGB_WHITE ) )
+                            // Two segments, second is uniformly coloured
+                            SAL_INFO("oox.drawingml.gradient", "two segments, 
second is uniformly coloured");
+                            nBorder = 
boost::prior(aGradientStops.end())->first - 
boost::next(aGradientStops.begin())->first;
+                            
aGradientStops.erase(boost::next(aGradientStops.begin()));
+                            aWidestSegmentStart = aGradientStops.begin();
+                            bSwap = true;
+                            nShapeRotation = 180*60000 - nShapeRotation;
+                        }
+                        else if( !bSymmetric &&
+                                 aGradientStops.size() >= 4 &&
+                                 aWidestSegmentStart->second.getColor( 
rGraphicHelper, nPhClr ) == boost::next(aWidestSegmentStart)->second.getColor( 
rGraphicHelper, nPhClr ) &&
+                                 aWidestSegmentStart->second.getTransparency() 
== boost::next(aWidestSegmentStart)->second.getTransparency() &&
+                                 ( aWidestSegmentStart == 
aGradientStops.begin() ||
+                                   boost::next(aWidestSegmentStart) == 
boost::prior( aGradientStops.end() ) ) )
+                        {
+                            // Not symmetric, three or more segments, the 
widest is first or last and is uniformly coloured
+                            SAL_INFO("oox.drawingml.gradient", "first or last 
segment is widest and is uniformly coloured");
+                            nBorder = boost::next(aWidestSegmentStart)->first 
- aWidestSegmentStart->first;
+
+                            // If it's the last segment that is uniformly 
coloured, rotate the gradient 180
+                            // degrees and swap start and end colours
+                            if( boost::next(aWidestSegmentStart) == 
boost::prior( aGradientStops.end() ) )
                             {
-                                aGradient.StartColor = aIt->second.getColor( 
rGraphicHelper, nPhClr );
+                                bSwap = true;
+                                nShapeRotation = 180*60000 - nShapeRotation;
                             }
 
-                            if( nStartTrans == nEndTrans && 
aIt->second.hasTransparency() )
-                                nStartTrans = 
aIt->second.getTransparency()*255/100;
+                            aGradientStops.erase( aWidestSegmentStart );
+
+                            // Look for which is widest now
+                            aIt = boost::next(aGradientStops.begin());
+                            nWidestWidth = -1;
+                            while( aIt != aGradientStops.end() )
+                            {
+                                if( aIt->first - boost::prior(aIt)->first > 
nWidestWidth )
+                                {
+                                    nWidestWidth = aIt->first - 
boost::prior(aIt)->first;
+                                    aWidestSegmentStart = boost::prior(aIt);
+                                }
+                                aIt++;
+                            }
+                        }
+                        SAL_INFO("oox.drawingml.gradient", "widest segment 
start: " << aWidestSegmentStart->first << ", border: " << nBorder);
+                        assert( (!bSymmetric && !bSwap) || !(bSymmetric && 
bSwap) );
+
+                        // Now we have a potential border and a largest 
segment. Use those.
+
+                        aGradient.Style = bSymmetric ? 
awt::GradientStyle_AXIAL : awt::GradientStyle_LINEAR;
+                        nDmlAngle = maGradientProps.moShadeAngle.get( 0 ) - 
nShapeRotation;
+                        // convert DrawingML angle (in 1/60000 degrees) to API 
angle (in 1/10 degrees)
+                        aGradient.Angle = static_cast< sal_Int16 >( (4500 - 
(nDmlAngle / (PER_DEGREE / 10))) % 3600 );
+                        Color aStartColor, aEndColor;
+                        if( bSymmetric )
+                        {
+                            aStartColor = 
boost::next(aWidestSegmentStart)->second;
+                            aEndColor = aWidestSegmentStart->second;
+                            nBorder *= 2;
+                        }
+                        else if( bSwap )
+                        {
+                            aStartColor = 
boost::next(aWidestSegmentStart)->second;
+                            aEndColor = aWidestSegmentStart->second;
                         }
+                        else
+                        {
+                            aStartColor = aWidestSegmentStart->second;
+                            aEndColor = 
boost::next(aWidestSegmentStart)->second;
+                        }
+
+                        SAL_INFO("oox.drawingml.gradient", "start color: " << 
std::hex << aStartColor.getColor( rGraphicHelper, nPhClr ) << std::dec <<
+                                 "@" << (100-aStartColor.getTransparency()) << 
"%"
+                                 ", end color: " << std::hex << 
aEndColor.getColor( rGraphicHelper, nPhClr ) << std::dec <<
+                                 "@" << (100-aEndColor.getTransparency()) << 
"%");
+
+                        aGradient.StartColor = aStartColor.getColor( 
rGraphicHelper, nPhClr );
+                        aGradient.EndColor = aEndColor.getColor( 
rGraphicHelper, nPhClr );
+
+                        if( aStartColor.hasTransparency() )
+                            nStartTrans = 
aStartColor.getTransparency()*255/100;
+                        if( aEndColor.hasTransparency() )
+                            nEndTrans = aEndColor.getTransparency()*255/100;
+
+                        aGradient.Border = 100*nBorder;
                     }
 
                     // push gradient or named gradient to property map
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to