LCOV - code coverage report
Current view: top level - canvas/source/vcl - canvashelper_texturefill.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 0 342 0.0 %
Date: 2015-06-13 12:38:46 Functions: 0 9 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : 
      21             : #include <canvas/debug.hxx>
      22             : #include <tools/diagnose_ex.h>
      23             : 
      24             : #include <rtl/math.hxx>
      25             : 
      26             : #include <com/sun/star/rendering/TextDirection.hpp>
      27             : #include <com/sun/star/rendering/TexturingMode.hpp>
      28             : #include <com/sun/star/rendering/PathCapType.hpp>
      29             : #include <com/sun/star/rendering/PathJoinType.hpp>
      30             : 
      31             : #include <tools/poly.hxx>
      32             : #include <vcl/window.hxx>
      33             : #include <vcl/bitmapex.hxx>
      34             : #include <vcl/bmpacc.hxx>
      35             : #include <vcl/virdev.hxx>
      36             : #include <vcl/canvastools.hxx>
      37             : 
      38             : #include <basegfx/matrix/b2dhommatrix.hxx>
      39             : #include <basegfx/range/b2drectangle.hxx>
      40             : #include <basegfx/point/b2dpoint.hxx>
      41             : #include <basegfx/vector/b2dsize.hxx>
      42             : #include <basegfx/polygon/b2dpolygon.hxx>
      43             : #include <basegfx/polygon/b2dpolygontools.hxx>
      44             : #include <basegfx/polygon/b2dpolypolygontools.hxx>
      45             : #include <basegfx/polygon/b2dlinegeometry.hxx>
      46             : #include <basegfx/tools/tools.hxx>
      47             : #include <basegfx/tools/lerp.hxx>
      48             : #include <basegfx/tools/keystoplerp.hxx>
      49             : #include <basegfx/tools/canvastools.hxx>
      50             : #include <basegfx/numeric/ftools.hxx>
      51             : 
      52             : #include <comphelper/sequence.hxx>
      53             : 
      54             : #include <canvas/canvastools.hxx>
      55             : #include <canvas/parametricpolypolygon.hxx>
      56             : 
      57             : #include <boost/bind.hpp>
      58             : #include <boost/tuple/tuple.hpp>
      59             : 
      60             : #include "spritecanvas.hxx"
      61             : #include "canvashelper.hxx"
      62             : #include "impltools.hxx"
      63             : 
      64             : 
      65             : using namespace ::com::sun::star;
      66             : 
      67             : namespace vclcanvas
      68             : {
      69             :     namespace
      70             :     {
      71           0 :         bool textureFill( OutputDevice&         rOutDev,
      72             :                           GraphicObject&        rGraphic,
      73             :                           const ::Point&        rPosPixel,
      74             :                           const ::Size&         rNextTileX,
      75             :                           const ::Size&         rNextTileY,
      76             :                           sal_Int32             nTilesX,
      77             :                           sal_Int32             nTilesY,
      78             :                           const ::Size&         rTileSize,
      79             :                           const GraphicAttr&    rAttr)
      80             :         {
      81           0 :             bool bRet( false );
      82           0 :             Point   aCurrPos;
      83             :             int     nX, nY;
      84             : 
      85           0 :             for( nY=0; nY < nTilesY; ++nY )
      86             :             {
      87           0 :                 aCurrPos.X() = rPosPixel.X() + nY*rNextTileY.Width();
      88           0 :                 aCurrPos.Y() = rPosPixel.Y() + nY*rNextTileY.Height();
      89             : 
      90           0 :                 for( nX=0; nX < nTilesX; ++nX )
      91             :                 {
      92             :                     // update return value. This method should return true, if
      93             :                     // at least one of the looped Draws succeeded.
      94             :                     bRet |= rGraphic.Draw( &rOutDev,
      95             :                                            aCurrPos,
      96             :                                            rTileSize,
      97           0 :                                            &rAttr );
      98             : 
      99           0 :                     aCurrPos.X() += rNextTileX.Width();
     100           0 :                     aCurrPos.Y() += rNextTileX.Height();
     101             :                 }
     102             :             }
     103             : 
     104           0 :             return bRet;
     105             :         }
     106             : 
     107             : 
     108             :         /** Fill linear or axial gradient
     109             : 
     110             :             Since most of the code for linear and axial gradients are
     111             :             the same, we've a unified method here
     112             :          */
     113           0 :         void fillLinearGradient( OutputDevice&                                  rOutDev,
     114             :                                  const ::basegfx::B2DHomMatrix&                 rTextureTransform,
     115             :                                  const ::Rectangle&                             rBounds,
     116             :                                  unsigned int                                   nStepCount,
     117             :                                  const ::canvas::ParametricPolyPolygon::Values& rValues,
     118             :                                  const std::vector< ::Color >&                  rColors )
     119             :         {
     120             :             // determine general position of gradient in relation to
     121             :             // the bound rect
     122             :             // =====================================================
     123             : 
     124           0 :             ::basegfx::B2DPoint aLeftTop( 0.0, 0.0 );
     125           0 :             ::basegfx::B2DPoint aLeftBottom( 0.0, 1.0 );
     126           0 :             ::basegfx::B2DPoint aRightTop( 1.0, 0.0 );
     127           0 :             ::basegfx::B2DPoint aRightBottom( 1.0, 1.0 );
     128             : 
     129           0 :             aLeftTop    *= rTextureTransform;
     130           0 :             aLeftBottom *= rTextureTransform;
     131           0 :             aRightTop   *= rTextureTransform;
     132           0 :             aRightBottom*= rTextureTransform;
     133             : 
     134             :             // calc length of bound rect diagonal
     135             :             const ::basegfx::B2DVector aBoundRectDiagonal(
     136           0 :                 vcl::unotools::b2DPointFromPoint( rBounds.TopLeft() ) -
     137           0 :                 vcl::unotools::b2DPointFromPoint( rBounds.BottomRight() ) );
     138           0 :             const double nDiagonalLength( aBoundRectDiagonal.getLength() );
     139             : 
     140             :             // create direction of gradient:
     141             :             //     _______
     142             :             //     |  |  |
     143             :             // ->  |  |  | ...
     144             :             //     |  |  |
     145             :             //     -------
     146           0 :             ::basegfx::B2DVector aDirection( aRightTop - aLeftTop );
     147           0 :             aDirection.normalize();
     148             : 
     149             :             // now, we potentially have to enlarge our gradient area
     150             :             // atop and below the transformed [0,1]x[0,1] unit rect,
     151             :             // for the gradient to fill the complete bound rect.
     152             :             ::basegfx::tools::infiniteLineFromParallelogram( aLeftTop,
     153             :                                                              aLeftBottom,
     154             :                                                              aRightTop,
     155             :                                                              aRightBottom,
     156           0 :                                                              vcl::unotools::b2DRectangleFromRectangle( rBounds ) );
     157             : 
     158             : 
     159             :             // render gradient
     160             :             // ===============
     161             : 
     162             :             // for linear gradients, it's easy to render
     163             :             // non-overlapping polygons: just split the gradient into
     164             :             // nStepCount small strips. Prepare the strip now.
     165             : 
     166             :             // For performance reasons, we create a temporary VCL
     167             :             // polygon here, keep it all the way and only change the
     168             :             // vertex values in the loop below (as ::Polygon is a
     169             :             // pimpl class, creating one every loop turn would really
     170             :             // stress the mem allocator)
     171           0 :             ::Polygon aTempPoly( static_cast<sal_uInt16>(5) );
     172             : 
     173             :             OSL_ENSURE( nStepCount >= 3,
     174             :                         "fillLinearGradient(): stepcount smaller than 3" );
     175             : 
     176             : 
     177             :             // fill initial strip (extending two times the bound rect's
     178             :             // diagonal to the 'left'
     179             : 
     180             : 
     181             :             // calculate left edge, by moving left edge of the
     182             :             // gradient rect two times the bound rect's diagonal to
     183             :             // the 'left'. Since we postpone actual rendering into the
     184             :             // loop below, we set the _right_ edge here, which will be
     185             :             // readily copied into the left edge in the loop below
     186           0 :             const ::basegfx::B2DPoint& rPoint1( aLeftTop - 2.0*nDiagonalLength*aDirection );
     187           0 :             aTempPoly[1] = ::Point( ::basegfx::fround( rPoint1.getX() ),
     188           0 :                                     ::basegfx::fround( rPoint1.getY() ) );
     189             : 
     190           0 :             const ::basegfx::B2DPoint& rPoint2( aLeftBottom - 2.0*nDiagonalLength*aDirection );
     191           0 :             aTempPoly[2] = ::Point( ::basegfx::fround( rPoint2.getX() ),
     192           0 :                                     ::basegfx::fround( rPoint2.getY() ) );
     193             : 
     194             : 
     195             :             // iteratively render all other strips
     196             : 
     197             : 
     198             :             // ensure that nStepCount matches color stop parity, to
     199             :             // have a well-defined middle color e.g. for axial
     200             :             // gradients.
     201           0 :             if( (rColors.size() % 2) != (nStepCount % 2) )
     202           0 :                 ++nStepCount;
     203             : 
     204           0 :             rOutDev.SetLineColor();
     205             : 
     206           0 :             basegfx::tools::KeyStopLerp aLerper(rValues.maStops);
     207             : 
     208             :             // only iterate nStepCount-1 steps, as the last strip is
     209             :             // explicitly painted below
     210           0 :             for( unsigned int i=0; i<nStepCount-1; ++i )
     211             :             {
     212             :                 std::ptrdiff_t nIndex;
     213             :                 double fAlpha;
     214           0 :                 boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(double(i)/nStepCount);
     215             : 
     216             :                 rOutDev.SetFillColor(
     217           0 :                     Color( (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha)),
     218           0 :                            (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha)),
     219           0 :                            (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha)) ));
     220             : 
     221             :                 // copy right egde of polygon to left edge (and also
     222             :                 // copy the closing point)
     223           0 :                 aTempPoly[0] = aTempPoly[4] = aTempPoly[1];
     224           0 :                 aTempPoly[3] = aTempPoly[2];
     225             : 
     226             :                 // calculate new right edge, from interpolating
     227             :                 // between start and end line. Note that i is
     228             :                 // increased by one, to account for the fact that we
     229             :                 // calculate the right border here (whereas the fill
     230             :                 // color is governed by the left edge)
     231             :                 const ::basegfx::B2DPoint& rPoint3(
     232           0 :                     (nStepCount - i-1)/double(nStepCount)*aLeftTop +
     233           0 :                     (i+1)/double(nStepCount)*aRightTop );
     234           0 :                 aTempPoly[1] = ::Point( ::basegfx::fround( rPoint3.getX() ),
     235           0 :                                         ::basegfx::fround( rPoint3.getY() ) );
     236             : 
     237             :                 const ::basegfx::B2DPoint& rPoint4(
     238           0 :                     (nStepCount - i-1)/double(nStepCount)*aLeftBottom +
     239           0 :                     (i+1)/double(nStepCount)*aRightBottom );
     240           0 :                 aTempPoly[2] = ::Point( ::basegfx::fround( rPoint4.getX() ),
     241           0 :                                         ::basegfx::fround( rPoint4.getY() ) );
     242             : 
     243           0 :                 rOutDev.DrawPolygon( aTempPoly );
     244           0 :             }
     245             : 
     246             :             // fill final strip (extending two times the bound rect's
     247             :             // diagonal to the 'right'
     248             : 
     249             : 
     250             :             // copy right egde of polygon to left edge (and also
     251             :             // copy the closing point)
     252           0 :             aTempPoly[0] = aTempPoly[4] = aTempPoly[1];
     253           0 :             aTempPoly[3] = aTempPoly[2];
     254             : 
     255             :             // calculate new right edge, by moving right edge of the
     256             :             // gradient rect two times the bound rect's diagonal to
     257             :             // the 'right'.
     258           0 :             const ::basegfx::B2DPoint& rPoint3( aRightTop + 2.0*nDiagonalLength*aDirection );
     259           0 :             aTempPoly[0] = aTempPoly[4] = ::Point( ::basegfx::fround( rPoint3.getX() ),
     260           0 :                                                    ::basegfx::fround( rPoint3.getY() ) );
     261             : 
     262           0 :             const ::basegfx::B2DPoint& rPoint4( aRightBottom + 2.0*nDiagonalLength*aDirection );
     263           0 :             aTempPoly[3] = ::Point( ::basegfx::fround( rPoint4.getX() ),
     264           0 :                                     ::basegfx::fround( rPoint4.getY() ) );
     265             : 
     266           0 :             rOutDev.SetFillColor( rColors.back() );
     267             : 
     268           0 :             rOutDev.DrawPolygon( aTempPoly );
     269           0 :         }
     270             : 
     271           0 :         void fillPolygonalGradient( OutputDevice&                                  rOutDev,
     272             :                                     const ::basegfx::B2DHomMatrix&                 rTextureTransform,
     273             :                                     const ::Rectangle&                             rBounds,
     274             :                                     unsigned int                                   nStepCount,
     275             :                                     bool                                           bFillNonOverlapping,
     276             :                                     const ::canvas::ParametricPolyPolygon::Values& rValues,
     277             :                                     const std::vector< ::Color >&                  rColors )
     278             :         {
     279           0 :             const ::basegfx::B2DPolygon& rGradientPoly( rValues.maGradientPoly );
     280             : 
     281           0 :             ENSURE_OR_THROW( rGradientPoly.count() > 2,
     282             :                               "fillPolygonalGradient(): polygon without area given" );
     283             : 
     284             :             // For performance reasons, we create a temporary VCL polygon
     285             :             // here, keep it all the way and only change the vertex values
     286             :             // in the loop below (as ::Polygon is a pimpl class, creating
     287             :             // one every loop turn would really stress the mem allocator)
     288           0 :             ::basegfx::B2DPolygon   aOuterPoly( rGradientPoly );
     289           0 :             ::basegfx::B2DPolygon   aInnerPoly;
     290             : 
     291             :             // subdivide polygon _before_ rendering, would otherwise have
     292             :             // to be performed on every loop turn.
     293           0 :             if( aOuterPoly.areControlPointsUsed() )
     294           0 :                 aOuterPoly = ::basegfx::tools::adaptiveSubdivideByAngle(aOuterPoly);
     295             : 
     296           0 :             aInnerPoly = aOuterPoly;
     297             : 
     298             :             // only transform outer polygon _after_ copying it into
     299             :             // aInnerPoly, because inner polygon has to be scaled before
     300             :             // the actual texture transformation takes place
     301           0 :             aOuterPoly.transform( rTextureTransform );
     302             : 
     303             :             // determine overall transformation for inner polygon (might
     304             :             // have to be prefixed by anisotrophic scaling)
     305           0 :             ::basegfx::B2DHomMatrix aInnerPolygonTransformMatrix;
     306             : 
     307             : 
     308             :             // apply scaling (possibly anisotrophic) to inner polygon
     309             : 
     310             : 
     311             :             // scale inner polygon according to aspect ratio: for
     312             :             // wider-than-tall bounds (nAspectRatio > 1.0), the inner
     313             :             // polygon, representing the gradient focus, must have
     314             :             // non-zero width. Specifically, a bound rect twice as wide as
     315             :             // tall has a focus polygon of half its width.
     316           0 :             const double nAspectRatio( rValues.mnAspectRatio );
     317           0 :             if( nAspectRatio > 1.0 )
     318             :             {
     319             :                 // width > height case
     320           0 :                 aInnerPolygonTransformMatrix.scale( 1.0 - 1.0/nAspectRatio,
     321           0 :                                                     0.0 );
     322             :             }
     323           0 :             else if( nAspectRatio < 1.0 )
     324             :             {
     325             :                 // width < height case
     326             :                 aInnerPolygonTransformMatrix.scale( 0.0,
     327           0 :                                                     1.0 - nAspectRatio );
     328             :             }
     329             :             else
     330             :             {
     331             :                 // isotrophic case
     332           0 :                 aInnerPolygonTransformMatrix.scale( 0.0, 0.0 );
     333             :             }
     334             : 
     335             :             // and finally, add texture transform to it.
     336           0 :             aInnerPolygonTransformMatrix *= rTextureTransform;
     337             : 
     338             :             // apply final matrix to polygon
     339           0 :             aInnerPoly.transform( aInnerPolygonTransformMatrix );
     340             : 
     341             : 
     342           0 :             const sal_uInt32 nNumPoints( aOuterPoly.count() );
     343           0 :             ::Polygon        aTempPoly( static_cast<sal_uInt16>(nNumPoints+1) );
     344             : 
     345             :             // increase number of steps by one: polygonal gradients have
     346             :             // the outermost polygon rendered in rColor2, and the
     347             :             // innermost in rColor1. The innermost polygon will never
     348             :             // have zero area, thus, we must divide the interval into
     349             :             // nStepCount+1 steps. For example, to create 3 steps:
     350             : 
     351             :             // |                       |
     352             :             // |-------|-------|-------|
     353             :             // |                       |
     354             :             // 3       2       1       0
     355             : 
     356             :             // This yields 4 tick marks, where 0 is never attained (since
     357             :             // zero-area polygons typically don't display perceivable
     358             :             // color).
     359           0 :             ++nStepCount;
     360             : 
     361           0 :             rOutDev.SetLineColor();
     362             : 
     363           0 :             basegfx::tools::KeyStopLerp aLerper(rValues.maStops);
     364             : 
     365           0 :             if( !bFillNonOverlapping )
     366             :             {
     367             :                 // fill background
     368           0 :                 rOutDev.SetFillColor( rColors.front() );
     369           0 :                 rOutDev.DrawRect( rBounds );
     370             : 
     371             :                 // render polygon
     372             :                 // ==============
     373             : 
     374           0 :                 for( unsigned int i=1,p; i<nStepCount; ++i )
     375             :                 {
     376           0 :                     const double fT( i/double(nStepCount) );
     377             : 
     378             :                     std::ptrdiff_t nIndex;
     379             :                     double fAlpha;
     380           0 :                     boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(fT);
     381             : 
     382             :                     // lerp color
     383             :                     rOutDev.SetFillColor(
     384           0 :                         Color( (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha)),
     385           0 :                                (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha)),
     386           0 :                                (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha)) ));
     387             : 
     388             :                     // scale and render polygon, by interpolating between
     389             :                     // outer and inner polygon.
     390             : 
     391           0 :                     for( p=0; p<nNumPoints; ++p )
     392             :                     {
     393           0 :                         const ::basegfx::B2DPoint& rOuterPoint( aOuterPoly.getB2DPoint(p) );
     394           0 :                         const ::basegfx::B2DPoint& rInnerPoint( aInnerPoly.getB2DPoint(p) );
     395             : 
     396           0 :                         aTempPoly[(sal_uInt16)p] = ::Point(
     397           0 :                             basegfx::fround( fT*rInnerPoint.getX() + (1-fT)*rOuterPoint.getX() ),
     398           0 :                             basegfx::fround( fT*rInnerPoint.getY() + (1-fT)*rOuterPoint.getY() ) );
     399           0 :                     }
     400             : 
     401             :                     // close polygon explicitly
     402           0 :                     aTempPoly[(sal_uInt16)p] = aTempPoly[0];
     403             : 
     404             :                     // TODO(P1): compare with vcl/source/gdi/outdev4.cxx,
     405             :                     // OutputDevice::ImplDrawComplexGradient(), there's a note
     406             :                     // that on some VDev's, rendering disjunct poly-polygons
     407             :                     // is faster!
     408           0 :                     rOutDev.DrawPolygon( aTempPoly );
     409             :                 }
     410             :             }
     411             :             else
     412             :             {
     413             :                 // render polygon
     414             :                 // ==============
     415             : 
     416             :                 // For performance reasons, we create a temporary VCL polygon
     417             :                 // here, keep it all the way and only change the vertex values
     418             :                 // in the loop below (as ::Polygon is a pimpl class, creating
     419             :                 // one every loop turn would really stress the mem allocator)
     420           0 :                 ::tools::PolyPolygon    aTempPolyPoly;
     421           0 :                 ::Polygon               aTempPoly2( static_cast<sal_uInt16>(nNumPoints+1) );
     422             : 
     423           0 :                 aTempPoly2[0] = rBounds.TopLeft();
     424           0 :                 aTempPoly2[1] = rBounds.TopRight();
     425           0 :                 aTempPoly2[2] = rBounds.BottomRight();
     426           0 :                 aTempPoly2[3] = rBounds.BottomLeft();
     427           0 :                 aTempPoly2[4] = rBounds.TopLeft();
     428             : 
     429           0 :                 aTempPolyPoly.Insert( aTempPoly );
     430           0 :                 aTempPolyPoly.Insert( aTempPoly2 );
     431             : 
     432           0 :                 for( unsigned int i=0,p; i<nStepCount; ++i )
     433             :                 {
     434           0 :                     const double fT( (i+1)/double(nStepCount) );
     435             : 
     436             :                     std::ptrdiff_t nIndex;
     437             :                     double fAlpha;
     438           0 :                     boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(fT);
     439             : 
     440             :                     // lerp color
     441             :                     rOutDev.SetFillColor(
     442           0 :                         Color( (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha)),
     443           0 :                                (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha)),
     444           0 :                                (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha)) ));
     445             : 
     446             : #if OSL_DEBUG_LEVEL > 2
     447             :                     if( i && !(i % 10) )
     448             :                         rOutDev.SetFillColor( COL_RED );
     449             : #endif
     450             : 
     451             :                     // scale and render polygon. Note that here, we
     452             :                     // calculate the inner polygon, which is actually the
     453             :                     // start of the _next_ color strip. Thus, i+1
     454             : 
     455           0 :                     for( p=0; p<nNumPoints; ++p )
     456             :                     {
     457           0 :                         const ::basegfx::B2DPoint& rOuterPoint( aOuterPoly.getB2DPoint(p) );
     458           0 :                         const ::basegfx::B2DPoint& rInnerPoint( aInnerPoly.getB2DPoint(p) );
     459             : 
     460           0 :                         aTempPoly[(sal_uInt16)p] = ::Point(
     461           0 :                             basegfx::fround( fT*rInnerPoint.getX() + (1-fT)*rOuterPoint.getX() ),
     462           0 :                             basegfx::fround( fT*rInnerPoint.getY() + (1-fT)*rOuterPoint.getY() ) );
     463           0 :                     }
     464             : 
     465             :                     // close polygon explicitly
     466           0 :                     aTempPoly[(sal_uInt16)p] = aTempPoly[0];
     467             : 
     468             :                     // swap inner and outer polygon
     469           0 :                     aTempPolyPoly.Replace( aTempPolyPoly.GetObject( 1 ), 0 );
     470             : 
     471           0 :                     if( i+1<nStepCount )
     472             :                     {
     473             :                         // assign new inner polygon. Note that with this
     474             :                         // formulation, the internal pimpl objects for both
     475             :                         // temp polygons and the polypolygon remain identical,
     476             :                         // minimizing heap accesses (only a Polygon wrapper
     477             :                         // object is freed and deleted twice during this swap).
     478           0 :                         aTempPolyPoly.Replace( aTempPoly, 1 );
     479             :                     }
     480             :                     else
     481             :                     {
     482             :                         // last, i.e. inner strip. Now, the inner polygon
     483             :                         // has zero area anyway, and to not leave holes in
     484             :                         // the gradient, finally render a simple polygon:
     485           0 :                         aTempPolyPoly.Remove( 1 );
     486             :                     }
     487             : 
     488           0 :                     rOutDev.DrawPolyPolygon( aTempPolyPoly );
     489           0 :                 }
     490           0 :             }
     491           0 :         }
     492             : 
     493           0 :         void doGradientFill( OutputDevice&                                  rOutDev,
     494             :                              const ::canvas::ParametricPolyPolygon::Values& rValues,
     495             :                              const std::vector< ::Color >&                  rColors,
     496             :                              const ::basegfx::B2DHomMatrix&                 rTextureTransform,
     497             :                              const ::Rectangle&                             rBounds,
     498             :                              unsigned int                                   nStepCount,
     499             :                              bool                                           bFillNonOverlapping )
     500             :         {
     501           0 :             switch( rValues.meType )
     502             :             {
     503             :                 case ::canvas::ParametricPolyPolygon::GRADIENT_LINEAR:
     504             :                     fillLinearGradient( rOutDev,
     505             :                                         rTextureTransform,
     506             :                                         rBounds,
     507             :                                         nStepCount,
     508             :                                         rValues,
     509           0 :                                         rColors );
     510           0 :                     break;
     511             : 
     512             :                 case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL:
     513             :                     // FALLTHROUGH intended
     514             :                 case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR:
     515             :                     fillPolygonalGradient( rOutDev,
     516             :                                            rTextureTransform,
     517             :                                            rBounds,
     518             :                                            nStepCount,
     519             :                                            bFillNonOverlapping,
     520             :                                            rValues,
     521           0 :                                            rColors );
     522           0 :                     break;
     523             : 
     524             :                 default:
     525           0 :                     ENSURE_OR_THROW( false,
     526             :                                       "CanvasHelper::doGradientFill(): Unexpected case" );
     527             :             }
     528           0 :         }
     529             : 
     530           0 :         int numColorSteps( const ::Color& rColor1, const ::Color& rColor2 )
     531             :         {
     532             :             return ::std::max(
     533           0 :                 labs( rColor1.GetRed() - rColor2.GetRed() ),
     534             :                 ::std::max(
     535           0 :                     labs( rColor1.GetGreen() - rColor2.GetGreen() ),
     536           0 :                     labs( rColor1.GetBlue()  - rColor2.GetBlue() ) ) );
     537             :         }
     538             : 
     539           0 :         bool gradientFill( OutputDevice&                                   rOutDev,
     540             :                            OutputDevice*                                   p2ndOutDev,
     541             :                            const ::canvas::ParametricPolyPolygon::Values&  rValues,
     542             :                            const std::vector< ::Color >&                   rColors,
     543             :                            const ::tools::PolyPolygon&                     rPoly,
     544             :                            const rendering::ViewState&                     viewState,
     545             :                            const rendering::RenderState&                   renderState,
     546             :                            const rendering::Texture&                       texture,
     547             :                            int                                             nTransparency )
     548             :         {
     549             :             // TODO(T2): It is maybe necessary to lock here, should
     550             :             // maGradientPoly someday cease to be const. But then, beware of
     551             :             // deadlocks, canvashelper calls this method with locked own
     552             :             // mutex.
     553             : 
     554             :             // calc step size
     555             : 
     556           0 :             int nColorSteps = 0;
     557           0 :             for( size_t i=0; i<rColors.size()-1; ++i )
     558           0 :                 nColorSteps += numColorSteps(rColors[i],rColors[i+1]);
     559             : 
     560           0 :             ::basegfx::B2DHomMatrix aTotalTransform;
     561             :             const int nStepCount=
     562             :                 ::canvas::tools::calcGradientStepCount(aTotalTransform,
     563             :                                                        viewState,
     564             :                                                        renderState,
     565             :                                                        texture,
     566           0 :                                                        nColorSteps);
     567             : 
     568           0 :             rOutDev.SetLineColor();
     569             : 
     570             :             // determine maximal bound rect of texture-filled
     571             :             // polygon
     572             :             const ::Rectangle aPolygonDeviceRectOrig(
     573           0 :                 rPoly.GetBoundRect() );
     574             : 
     575           0 :             if( tools::isRectangle( rPoly ) )
     576             :             {
     577             :                 // use optimized output path
     578             : 
     579             : 
     580             :                 // this distinction really looks like a
     581             :                 // micro-optimization, but in fact greatly speeds up
     582             :                 // especially complex gradients. That's because when using
     583             :                 // clipping, we can output polygons instead of
     584             :                 // poly-polygons, and don't have to output the gradient
     585             :                 // twice for XOR
     586             : 
     587           0 :                 rOutDev.Push( PushFlags::CLIPREGION );
     588           0 :                 rOutDev.IntersectClipRegion( aPolygonDeviceRectOrig );
     589             :                 doGradientFill( rOutDev,
     590             :                                 rValues,
     591             :                                 rColors,
     592             :                                 aTotalTransform,
     593             :                                 aPolygonDeviceRectOrig,
     594             :                                 nStepCount,
     595           0 :                                 false );
     596           0 :                 rOutDev.Pop();
     597             : 
     598           0 :                 if( p2ndOutDev && nTransparency < 253 )
     599             :                 {
     600             :                     // HACK. Normally, CanvasHelper does not care about
     601             :                     // actually what mp2ndOutDev is...  well, here we do &
     602             :                     // assume a 1bpp target - everything beyond 97%
     603             :                     // transparency is fully transparent
     604           0 :                     p2ndOutDev->SetFillColor( COL_BLACK );
     605           0 :                     p2ndOutDev->DrawRect( aPolygonDeviceRectOrig );
     606             :                 }
     607             :             }
     608             :             else
     609             :             {
     610           0 :                 const vcl::Region aPolyClipRegion( rPoly );
     611             : 
     612           0 :                 rOutDev.Push( PushFlags::CLIPREGION );
     613           0 :                 rOutDev.SetClipRegion( aPolyClipRegion );
     614             : 
     615             :                 doGradientFill( rOutDev,
     616             :                                 rValues,
     617             :                                 rColors,
     618             :                                 aTotalTransform,
     619             :                                 aPolygonDeviceRectOrig,
     620             :                                 nStepCount,
     621           0 :                                 false );
     622           0 :                 rOutDev.Pop();
     623             : 
     624           0 :                 if( p2ndOutDev && nTransparency < 253 )
     625             :                 {
     626             :                     // HACK. Normally, CanvasHelper does not care about
     627             :                     // actually what mp2ndOutDev is...  well, here we do &
     628             :                     // assume a 1bpp target - everything beyond 97%
     629             :                     // transparency is fully transparent
     630           0 :                     p2ndOutDev->SetFillColor( COL_BLACK );
     631           0 :                     p2ndOutDev->DrawPolyPolygon( rPoly );
     632           0 :                 }
     633             :             }
     634             : 
     635             : #if OSL_DEBUG_LEVEL > 3
     636             :             // extra-verbosity
     637             :             {
     638             :                 ::basegfx::B2DRectangle aRect(0.0, 0.0, 1.0, 1.0);
     639             :                 ::basegfx::B2DRectangle aTextureDeviceRect;
     640             :                 ::basegfx::B2DHomMatrix aTextureTransform;
     641             :                 ::canvas::tools::calcTransformedRectBounds( aTextureDeviceRect,
     642             :                                                             aRect,
     643             :                                                             aTextureTransform );
     644             :                 rOutDev.SetLineColor( COL_RED );
     645             :                 rOutDev.SetFillColor();
     646             :                 rOutDev.DrawRect( vcl::unotools::rectangleFromB2DRectangle( aTextureDeviceRect ) );
     647             : 
     648             :                 rOutDev.SetLineColor( COL_BLUE );
     649             :                 ::Polygon aPoly1(
     650             :                     vcl::unotools::rectangleFromB2DRectangle( aRect ));
     651             :                 ::basegfx::B2DPolygon aPoly2( aPoly1.getB2DPolygon() );
     652             :                 aPoly2.transform( aTextureTransform );
     653             :                 ::Polygon aPoly3( aPoly2 );
     654             :                 rOutDev.DrawPolygon( aPoly3 );
     655             :             }
     656             : #endif
     657             : 
     658           0 :             return true;
     659             :         }
     660             :     }
     661             : 
     662           0 :     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas*                          pCanvas,
     663             :                                                                                          const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
     664             :                                                                                          const rendering::ViewState&                        viewState,
     665             :                                                                                          const rendering::RenderState&                      renderState,
     666             :                                                                                          const uno::Sequence< rendering::Texture >&         textures )
     667             :     {
     668           0 :         ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
     669             :                          "CanvasHelper::fillPolyPolygon(): polygon is NULL");
     670           0 :         ENSURE_ARG_OR_THROW( textures.getLength(),
     671             :                          "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence");
     672             : 
     673           0 :         if( mpOutDev )
     674             :         {
     675           0 :             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
     676             : 
     677           0 :             const int nTransparency( setupOutDevState( viewState, renderState, IGNORE_COLOR ) );
     678             :             ::tools::PolyPolygon aPolyPoly( tools::mapPolyPolygon(
     679             :                                        ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon),
     680           0 :                                        viewState, renderState ) );
     681             : 
     682             :             // TODO(F1): Multi-texturing
     683           0 :             if( textures[0].Gradient.is() )
     684             :             {
     685             :                 // try to cast XParametricPolyPolygon2D reference to
     686             :                 // our implementation class.
     687             :                 ::canvas::ParametricPolyPolygon* pGradient =
     688           0 :                       dynamic_cast< ::canvas::ParametricPolyPolygon* >( textures[0].Gradient.get() );
     689             : 
     690           0 :                 if( pGradient && pGradient->getValues().maColors.getLength() )
     691             :                 {
     692             :                     // copy state from Gradient polypoly locally
     693             :                     // (given object might change!)
     694             :                     const ::canvas::ParametricPolyPolygon::Values& rValues(
     695           0 :                         pGradient->getValues() );
     696             : 
     697           0 :                     if( rValues.maColors.getLength() < 2 )
     698             :                     {
     699           0 :                         rendering::RenderState aTempState=renderState;
     700           0 :                         aTempState.DeviceColor = rValues.maColors[0];
     701           0 :                         fillPolyPolygon(pCanvas, xPolyPolygon, viewState, aTempState);
     702             :                     }
     703             :                     else
     704             :                     {
     705           0 :                         std::vector< ::Color > aColors(rValues.maColors.getLength());
     706           0 :                         std::transform(&rValues.maColors[0],
     707           0 :                                        &rValues.maColors[0]+rValues.maColors.getLength(),
     708             :                                        aColors.begin(),
     709             :                                        boost::bind(
     710             :                                            &vcl::unotools::stdColorSpaceSequenceToColor,
     711           0 :                                            _1));
     712             : 
     713             :                         // TODO(E1): Return value
     714             :                         // TODO(F1): FillRule
     715           0 :                         gradientFill( mpOutDev->getOutDev(),
     716           0 :                                       mp2ndOutDev.get() ? &mp2ndOutDev->getOutDev() : nullptr,
     717             :                                       rValues,
     718             :                                       aColors,
     719             :                                       aPolyPoly,
     720             :                                       viewState,
     721             :                                       renderState,
     722           0 :                                       textures[0],
     723           0 :                                       nTransparency );
     724           0 :                     }
     725             :                 }
     726             :                 else
     727             :                 {
     728             :                     // TODO(F1): The generic case is missing here
     729           0 :                     ENSURE_OR_THROW( false,
     730             :                                       "CanvasHelper::fillTexturedPolyPolygon(): unknown parametric polygon encountered" );
     731             :                 }
     732             :             }
     733           0 :             else if( textures[0].Bitmap.is() )
     734             :             {
     735           0 :                 const geometry::IntegerSize2D aBmpSize( textures[0].Bitmap->getSize() );
     736             : 
     737           0 :                 ENSURE_ARG_OR_THROW( aBmpSize.Width != 0 &&
     738             :                                  aBmpSize.Height != 0,
     739             :                                  "CanvasHelper::fillTexturedPolyPolygon(): zero-sized texture bitmap" );
     740             : 
     741             :                 // determine maximal bound rect of texture-filled
     742             :                 // polygon
     743             :                 const ::Rectangle aPolygonDeviceRect(
     744           0 :                     aPolyPoly.GetBoundRect() );
     745             : 
     746             : 
     747             :                 // first of all, determine whether we have a
     748             :                 // drawBitmap() in disguise
     749             :                 // =========================================
     750             : 
     751           0 :                 const bool bRectangularPolygon( tools::isRectangle( aPolyPoly ) );
     752             : 
     753           0 :                 ::basegfx::B2DHomMatrix aTotalTransform;
     754             :                 ::canvas::tools::mergeViewAndRenderTransform(aTotalTransform,
     755             :                                                              viewState,
     756           0 :                                                              renderState);
     757           0 :                 ::basegfx::B2DHomMatrix aTextureTransform;
     758             :                 ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform,
     759           0 :                                                                 textures[0].AffineTransform );
     760             : 
     761           0 :                 aTotalTransform *= aTextureTransform;
     762             : 
     763           0 :                 const ::basegfx::B2DRectangle aRect(0.0, 0.0, 1.0, 1.0);
     764           0 :                 ::basegfx::B2DRectangle aTextureDeviceRect;
     765             :                 ::canvas::tools::calcTransformedRectBounds( aTextureDeviceRect,
     766             :                                                             aRect,
     767           0 :                                                             aTotalTransform );
     768             : 
     769             :                 const ::Rectangle aIntegerTextureDeviceRect(
     770           0 :                     vcl::unotools::rectangleFromB2DRectangle( aTextureDeviceRect ) );
     771             : 
     772           0 :                 if( bRectangularPolygon &&
     773           0 :                     aIntegerTextureDeviceRect == aPolygonDeviceRect )
     774             :                 {
     775           0 :                     rendering::RenderState aLocalState( renderState );
     776             :                     ::canvas::tools::appendToRenderState(aLocalState,
     777           0 :                                                          aTextureTransform);
     778           0 :                     ::basegfx::B2DHomMatrix aScaleCorrection;
     779             :                     aScaleCorrection.scale( 1.0/aBmpSize.Width,
     780           0 :                                             1.0/aBmpSize.Height );
     781             :                     ::canvas::tools::appendToRenderState(aLocalState,
     782           0 :                                                          aScaleCorrection);
     783             : 
     784             :                     // need alpha modulation?
     785           0 :                     if( !::rtl::math::approxEqual( textures[0].Alpha,
     786           0 :                                                    1.0 ) )
     787             :                     {
     788             :                         // setup alpha modulation values
     789           0 :                         aLocalState.DeviceColor.realloc(4);
     790           0 :                         double* pColor = aLocalState.DeviceColor.getArray();
     791             :                         pColor[0] =
     792           0 :                         pColor[1] =
     793           0 :                         pColor[2] = 0.0;
     794           0 :                         pColor[3] = textures[0].Alpha;
     795             : 
     796             :                         return drawBitmapModulated( pCanvas,
     797           0 :                                                     textures[0].Bitmap,
     798             :                                                     viewState,
     799           0 :                                                     aLocalState );
     800             :                     }
     801             :                     else
     802             :                     {
     803             :                         return drawBitmap( pCanvas,
     804           0 :                                            textures[0].Bitmap,
     805             :                                            viewState,
     806           0 :                                            aLocalState );
     807           0 :                     }
     808             :                 }
     809             :                 else
     810             :                 {
     811             :                     // No easy mapping to drawBitmap() - calculate
     812             :                     // texturing parameters
     813             :                     // ===========================================
     814             : 
     815           0 :                     BitmapEx aBmpEx( tools::bitmapExFromXBitmap( textures[0].Bitmap ) );
     816             : 
     817             :                     // scale down bitmap to [0,1]x[0,1] rect, as required
     818             :                     // from the XCanvas interface.
     819           0 :                     ::basegfx::B2DHomMatrix aScaling;
     820           0 :                     ::basegfx::B2DHomMatrix aPureTotalTransform; // pure view*render*texture transform
     821             :                     aScaling.scale( 1.0/aBmpSize.Width,
     822           0 :                                     1.0/aBmpSize.Height );
     823             : 
     824           0 :                     aTotalTransform = aTextureTransform * aScaling;
     825           0 :                     aPureTotalTransform = aTextureTransform;
     826             : 
     827             :                     // combine with view and render transform
     828           0 :                     ::basegfx::B2DHomMatrix aMatrix;
     829           0 :                     ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState);
     830             : 
     831             :                     // combine all three transformations into one
     832             :                     // global texture-to-device-space transformation
     833           0 :                     aTotalTransform *= aMatrix;
     834           0 :                     aPureTotalTransform *= aMatrix;
     835             : 
     836             :                     // analyze transformation, and setup an
     837             :                     // appropriate GraphicObject
     838           0 :                     ::basegfx::B2DVector aScale;
     839           0 :                     ::basegfx::B2DPoint  aOutputPos;
     840             :                     double               nRotate;
     841             :                     double               nShearX;
     842           0 :                     aTotalTransform.decompose( aScale, aOutputPos, nRotate, nShearX );
     843             : 
     844           0 :                     GraphicAttr             aGrfAttr;
     845           0 :                     GraphicObjectSharedPtr  pGrfObj;
     846             : 
     847           0 :                     if( ::basegfx::fTools::equalZero( nShearX ) )
     848             :                     {
     849             :                         // no shear, GraphicObject is enough (the
     850             :                         // GraphicObject only supports scaling, rotation
     851             :                         // and translation)
     852             : 
     853             :                         // setup GraphicAttr
     854             :                         aGrfAttr.SetMirrorFlags(
     855           0 :                             ( aScale.getX() < 0.0 ? BmpMirrorFlags::Horizontal : BmpMirrorFlags::NONE ) |
     856           0 :                             ( aScale.getY() < 0.0 ? BmpMirrorFlags::Vertical : BmpMirrorFlags::NONE ) );
     857           0 :                         aGrfAttr.SetRotation( static_cast< sal_uInt16 >(::basegfx::fround( nRotate*10.0 )) );
     858             : 
     859           0 :                         pGrfObj.reset( new GraphicObject( aBmpEx ) );
     860             :                     }
     861             :                     else
     862             :                     {
     863             :                         // complex transformation, use generic affine bitmap
     864             :                         // transformation
     865           0 :                         aBmpEx = tools::transformBitmap( aBmpEx,
     866             :                                                          aTotalTransform,
     867             :                                                          uno::Sequence< double >(),
     868           0 :                                                          tools::MODULATE_NONE);
     869             : 
     870           0 :                         pGrfObj.reset( new GraphicObject( aBmpEx ) );
     871             : 
     872             :                         // clear scale values, generated bitmap already
     873             :                         // contains scaling
     874           0 :                         aScale.setX( 0.0 ); aScale.setY( 0.0 );
     875             :                     }
     876             : 
     877             : 
     878             :                     // render texture tiled into polygon
     879             :                     // =================================
     880             : 
     881             :                     // calc device space direction vectors. We employ
     882             :                     // the followin approach for tiled output: the
     883             :                     // texture bitmap is output in texture space
     884             :                     // x-major order, i.e. tile neighbors in texture
     885             :                     // space x direction are rendered back-to-back in
     886             :                     // device coordinate space (after the full device
     887             :                     // transformation). Thus, the aNextTile* vectors
     888             :                     // denote the output position updates in device
     889             :                     // space, to get from one tile to the next.
     890           0 :                     ::basegfx::B2DVector aNextTileX( 1.0, 0.0 );
     891           0 :                     ::basegfx::B2DVector aNextTileY( 0.0, 1.0 );
     892           0 :                     aNextTileX *= aPureTotalTransform;
     893           0 :                     aNextTileY *= aPureTotalTransform;
     894             : 
     895           0 :                     ::basegfx::B2DHomMatrix aInverseTextureTransform( aPureTotalTransform );
     896             : 
     897           0 :                     ENSURE_ARG_OR_THROW( aInverseTextureTransform.isInvertible(),
     898             :                                      "CanvasHelper::fillTexturedPolyPolygon(): singular texture matrix" );
     899             : 
     900           0 :                     aInverseTextureTransform.invert();
     901             : 
     902             :                     // calc bound rect of extended texture area in
     903             :                     // device coordinates. Therefore, we first calc
     904             :                     // the area of the polygon bound rect in texture
     905             :                     // space. To maintain texture phase, this bound
     906             :                     // rect is then extended to integer coordinates
     907             :                     // (extended, because shrinking might leave some
     908             :                     // inner polygon areas unfilled).
     909             :                     // Finally, the bound rect is transformed back to
     910             :                     // device coordinate space, were we determine the
     911             :                     // start point from it.
     912           0 :                     ::basegfx::B2DRectangle aTextureSpacePolygonRect;
     913             :                     ::canvas::tools::calcTransformedRectBounds( aTextureSpacePolygonRect,
     914             :                                                                 vcl::unotools::b2DRectangleFromRectangle(
     915             :                                                                     aPolygonDeviceRect ),
     916           0 :                                                                 aInverseTextureTransform );
     917             : 
     918             :                     // calc left, top of extended polygon rect in
     919             :                     // texture space, create one-texture instance rect
     920             :                     // from it (i.e. rect from start point extending
     921             :                     // 1.0 units to the right and 1.0 units to the
     922             :                     // bottom). Note that the rounding employed here
     923             :                     // is a bit subtle, since we need to round up/down
     924             :                     // as _soon_ as any fractional amount is
     925             :                     // encountered. This is to ensure that the full
     926             :                     // polygon area is filled with texture tiles.
     927           0 :                     const sal_Int32 nX1( ::canvas::tools::roundDown( aTextureSpacePolygonRect.getMinX() ) );
     928           0 :                     const sal_Int32 nY1( ::canvas::tools::roundDown( aTextureSpacePolygonRect.getMinY() ) );
     929           0 :                     const sal_Int32 nX2( ::canvas::tools::roundUp( aTextureSpacePolygonRect.getMaxX() ) );
     930           0 :                     const sal_Int32 nY2( ::canvas::tools::roundUp( aTextureSpacePolygonRect.getMaxY() ) );
     931             :                     const ::basegfx::B2DRectangle aSingleTextureRect(
     932             :                         nX1, nY1,
     933             :                         nX1 + 1.0,
     934           0 :                         nY1 + 1.0 );
     935             : 
     936             :                     // and convert back to device space
     937           0 :                     ::basegfx::B2DRectangle aSingleDeviceTextureRect;
     938             :                     ::canvas::tools::calcTransformedRectBounds( aSingleDeviceTextureRect,
     939             :                                                                 aSingleTextureRect,
     940           0 :                                                                 aPureTotalTransform );
     941             : 
     942             :                     const ::Point aPtRepeat( vcl::unotools::pointFromB2DPoint(
     943           0 :                                                  aSingleDeviceTextureRect.getMinimum() ) );
     944           0 :                     const ::Size  aSz( ::basegfx::fround( aScale.getX() * aBmpSize.Width ),
     945           0 :                                        ::basegfx::fround( aScale.getY() * aBmpSize.Height ) );
     946           0 :                     const ::Size  aIntegerNextTileX( vcl::unotools::sizeFromB2DSize(aNextTileX) );
     947           0 :                     const ::Size  aIntegerNextTileY( vcl::unotools::sizeFromB2DSize(aNextTileY) );
     948             : 
     949           0 :                     const ::Point aPt( textures[0].RepeatModeX == rendering::TexturingMode::NONE ?
     950           0 :                                        ::basegfx::fround( aOutputPos.getX() ) : aPtRepeat.X(),
     951           0 :                                        textures[0].RepeatModeY == rendering::TexturingMode::NONE ?
     952           0 :                                        ::basegfx::fround( aOutputPos.getY() ) : aPtRepeat.Y() );
     953           0 :                     const sal_Int32 nTilesX( textures[0].RepeatModeX == rendering::TexturingMode::NONE ?
     954           0 :                                              1 : nX2 - nX1 );
     955           0 :                     const sal_Int32 nTilesY( textures[0].RepeatModeX == rendering::TexturingMode::NONE ?
     956           0 :                                              1 : nY2 - nY1 );
     957             : 
     958           0 :                     OutputDevice& rOutDev( mpOutDev->getOutDev() );
     959             : 
     960           0 :                     if( bRectangularPolygon )
     961             :                     {
     962             :                         // use optimized output path
     963             : 
     964             : 
     965             :                         // this distinction really looks like a
     966             :                         // micro-optimization, but in fact greatly speeds up
     967             :                         // especially complex fills. That's because when using
     968             :                         // clipping, we can output polygons instead of
     969             :                         // poly-polygons, and don't have to output the gradient
     970             :                         // twice for XOR
     971             : 
     972             :                         // setup alpha modulation
     973           0 :                         if( !::rtl::math::approxEqual( textures[0].Alpha,
     974           0 :                                                        1.0 ) )
     975             :                         {
     976             :                             // TODO(F1): Note that the GraphicManager has
     977             :                             // a subtle difference in how it calculates
     978             :                             // the resulting alpha value: it's using the
     979             :                             // inverse alpha values (i.e. 'transparency'),
     980             :                             // and calculates transOrig + transModulate,
     981             :                             // instead of transOrig + transModulate -
     982             :                             // transOrig*transModulate (which would be
     983             :                             // equivalent to the origAlpha*modulateAlpha
     984             :                             // the DX canvas performs)
     985             :                             aGrfAttr.SetTransparency(
     986             :                                 static_cast< sal_uInt8 >(
     987           0 :                                     ::basegfx::fround( 255.0*( 1.0 - textures[0].Alpha ) ) ) );
     988             :                         }
     989             : 
     990           0 :                         rOutDev.IntersectClipRegion( aPolygonDeviceRect );
     991             :                         textureFill( rOutDev,
     992           0 :                                      *pGrfObj,
     993             :                                      aPt,
     994             :                                      aIntegerNextTileX,
     995             :                                      aIntegerNextTileY,
     996             :                                      nTilesX,
     997             :                                      nTilesY,
     998             :                                      aSz,
     999           0 :                                      aGrfAttr );
    1000             : 
    1001           0 :                         if( mp2ndOutDev )
    1002             :                         {
    1003           0 :                             OutputDevice& r2ndOutDev( mp2ndOutDev->getOutDev() );
    1004           0 :                             r2ndOutDev.IntersectClipRegion( aPolygonDeviceRect );
    1005             :                             textureFill( r2ndOutDev,
    1006           0 :                                          *pGrfObj,
    1007             :                                          aPt,
    1008             :                                          aIntegerNextTileX,
    1009             :                                          aIntegerNextTileY,
    1010             :                                          nTilesX,
    1011             :                                          nTilesY,
    1012             :                                          aSz,
    1013           0 :                                          aGrfAttr );
    1014             :                         }
    1015             :                     }
    1016             :                     else
    1017             :                     {
    1018             :                         // output texture the hard way: XORing out the
    1019             :                         // polygon
    1020             :                         // ===========================================
    1021             : 
    1022           0 :                         if( !::rtl::math::approxEqual( textures[0].Alpha,
    1023           0 :                                                        1.0 ) )
    1024             :                         {
    1025             :                             // uh-oh. alpha blending is required,
    1026             :                             // cannot do direct XOR, but have to
    1027             :                             // prepare the filled polygon within a
    1028             :                             // VDev
    1029           0 :                             ScopedVclPtrInstance< VirtualDevice > pVDev( rOutDev );
    1030           0 :                             pVDev->SetOutputSizePixel( aPolygonDeviceRect.GetSize() );
    1031             : 
    1032             :                             // shift output to origin of VDev
    1033           0 :                             const ::Point aOutPos( aPt - aPolygonDeviceRect.TopLeft() );
    1034           0 :                             aPolyPoly.Translate( ::Point( -aPolygonDeviceRect.Left(),
    1035           0 :                                                           -aPolygonDeviceRect.Top() ) );
    1036             : 
    1037           0 :                             const vcl::Region aPolyClipRegion( aPolyPoly );
    1038             : 
    1039           0 :                             pVDev->SetClipRegion( aPolyClipRegion );
    1040           0 :                             textureFill( *pVDev.get(),
    1041           0 :                                          *pGrfObj,
    1042             :                                          aOutPos,
    1043             :                                          aIntegerNextTileX,
    1044             :                                          aIntegerNextTileY,
    1045             :                                          nTilesX,
    1046             :                                          nTilesY,
    1047             :                                          aSz,
    1048           0 :                                          aGrfAttr );
    1049             : 
    1050             :                             // output VDev content alpha-blended to
    1051             :                             // target position.
    1052           0 :                             const ::Point aEmptyPoint;
    1053             :                             Bitmap aContentBmp(
    1054           0 :                                 pVDev->GetBitmap( aEmptyPoint,
    1055           0 :                                                  pVDev->GetOutputSizePixel() ) );
    1056             : 
    1057             :                             sal_uInt8 nCol( static_cast< sal_uInt8 >(
    1058           0 :                                            ::basegfx::fround( 255.0*( 1.0 - textures[0].Alpha ) ) ) );
    1059           0 :                             AlphaMask aAlpha( pVDev->GetOutputSizePixel(),
    1060           0 :                                               &nCol );
    1061             : 
    1062           0 :                             BitmapEx aOutputBmpEx( aContentBmp, aAlpha );
    1063             :                             rOutDev.DrawBitmapEx( aPolygonDeviceRect.TopLeft(),
    1064           0 :                                                   aOutputBmpEx );
    1065             : 
    1066           0 :                             if( mp2ndOutDev )
    1067           0 :                                 mp2ndOutDev->getOutDev().DrawBitmapEx( aPolygonDeviceRect.TopLeft(),
    1068           0 :                                                                        aOutputBmpEx );
    1069             :                         }
    1070             :                         else
    1071             :                         {
    1072           0 :                             const vcl::Region aPolyClipRegion( aPolyPoly );
    1073             : 
    1074           0 :                             rOutDev.Push( PushFlags::CLIPREGION );
    1075           0 :                             rOutDev.SetClipRegion( aPolyClipRegion );
    1076             : 
    1077             :                             textureFill( rOutDev,
    1078           0 :                                          *pGrfObj,
    1079             :                                          aPt,
    1080             :                                          aIntegerNextTileX,
    1081             :                                          aIntegerNextTileY,
    1082             :                                          nTilesX,
    1083             :                                          nTilesY,
    1084             :                                          aSz,
    1085           0 :                                          aGrfAttr );
    1086           0 :                             rOutDev.Pop();
    1087             : 
    1088           0 :                             if( mp2ndOutDev )
    1089             :                             {
    1090           0 :                                 OutputDevice& r2ndOutDev( mp2ndOutDev->getOutDev() );
    1091           0 :                                 r2ndOutDev.Push( PushFlags::CLIPREGION );
    1092             : 
    1093           0 :                                 r2ndOutDev.SetClipRegion( aPolyClipRegion );
    1094             :                                 textureFill( r2ndOutDev,
    1095           0 :                                              *pGrfObj,
    1096             :                                              aPt,
    1097             :                                              aIntegerNextTileX,
    1098             :                                              aIntegerNextTileY,
    1099             :                                              nTilesX,
    1100             :                                              nTilesY,
    1101             :                                              aSz,
    1102           0 :                                              aGrfAttr );
    1103           0 :                                 r2ndOutDev.Pop();
    1104           0 :                             }
    1105             :                         }
    1106           0 :                     }
    1107           0 :                 }
    1108           0 :             }
    1109             :         }
    1110             : 
    1111             :         // TODO(P1): Provide caching here.
    1112           0 :         return uno::Reference< rendering::XCachedPrimitive >(NULL);
    1113             :     }
    1114             : 
    1115           0 : }
    1116             : 
    1117             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11