LCOV - code coverage report
Current view: top level - canvas/source/vcl - canvashelper_texturefill.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 0 334 0.0 %
Date: 2012-08-25 Functions: 0 9 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 0 -

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

Generated by: LCOV version 1.10