/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_canvas.hxx"

#include <canvas/debug.hxx>
#include <tools/diagnose_ex.h>

#include <com/sun/star/geometry/AffineMatrix2D.hpp>
#include <com/sun/star/geometry/Matrix2D.hpp>
#include <com/sun/star/awt/Rectangle.hpp>
#include <com/sun/star/util/Endianness.hpp>
#include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp>
#include <com/sun/star/rendering/IntegerBitmapLayout.hpp>
#include <com/sun/star/rendering/ColorSpaceType.hpp>
#include <com/sun/star/rendering/ColorComponentTag.hpp>
#include <com/sun/star/rendering/RenderingIntent.hpp>
#include <com/sun/star/rendering/RenderState.hpp>
#include <com/sun/star/rendering/ViewState.hpp>
#include <com/sun/star/rendering/XCanvas.hpp>
#include <com/sun/star/rendering/XColorSpace.hpp>
#include <com/sun/star/rendering/CompositeOperation.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>

#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/range/b2drange.hxx>
#include <basegfx/range/b2irange.hxx>
#include <basegfx/range/b2drectangle.hxx>
#include <basegfx/point/b2dpoint.hxx>
#include <basegfx/point/b2ipoint.hxx>
#include <basegfx/vector/b2ivector.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/tools/canvastools.hxx>
#include <basegfx/numeric/ftools.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>

#include <cppuhelper/compbase1.hxx>
#include <rtl/instance.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <vcl/window.hxx>
#include <vcl/canvastools.hxx>

#include <canvas/canvastools.hxx>

#include <limits>


using namespace ::com::sun::star;

namespace com { namespace sun { namespace star { namespace rendering
{
    bool operator==( const RenderState& renderState1,
                     const RenderState& renderState2 )
    {
        if( renderState1.Clip != renderState2.Clip )
            return false;

        if( renderState1.DeviceColor != renderState2.DeviceColor )
            return false;

        if( renderState1.CompositeOperation != renderState2.CompositeOperation )
            return false;

        ::basegfx::B2DHomMatrix mat1, mat2;
        ::canvas::tools::getRenderStateTransform( mat1, renderState1 );
        ::canvas::tools::getRenderStateTransform( mat2, renderState2 );
        if( mat1 != mat2 )
            return false;

        return true;
    }

    bool operator==( const ViewState& viewState1,
                     const ViewState& viewState2 )
    {
        if( viewState1.Clip != viewState2.Clip )
            return false;

        ::basegfx::B2DHomMatrix mat1, mat2;
        ::canvas::tools::getViewStateTransform( mat1, viewState1 );
        ::canvas::tools::getViewStateTransform( mat2, viewState2 );
        if( mat1 != mat2 )
            return false;

        return true;
    }
}}}}

namespace canvas
{
    namespace tools
    {
        geometry::RealSize2D createInfiniteSize2D()
        {
            return geometry::RealSize2D( 
                ::std::numeric_limits<double>::infinity(),
                ::std::numeric_limits<double>::infinity() );
        }

        rendering::RenderState& initRenderState( rendering::RenderState& renderState )
        {
            // setup identity transform
            setIdentityAffineMatrix2D( renderState.AffineTransform );
            renderState.Clip = uno::Reference< rendering::XPolyPolygon2D >();
            renderState.DeviceColor = uno::Sequence< double >();
            renderState.CompositeOperation = rendering::CompositeOperation::OVER;

            return renderState;
        }

        rendering::ViewState& initViewState( rendering::ViewState& viewState )
        {
            // setup identity transform
            setIdentityAffineMatrix2D( viewState.AffineTransform );
            viewState.Clip = uno::Reference< rendering::XPolyPolygon2D >();

            return viewState;
        }

        ::basegfx::B2DHomMatrix& getViewStateTransform( ::basegfx::B2DHomMatrix&	transform,
                                                        const rendering::ViewState&	viewState )
        {
            return ::basegfx::unotools::homMatrixFromAffineMatrix( transform, viewState.AffineTransform );
        }

        rendering::ViewState& setViewStateTransform( rendering::ViewState& 			viewState,
                                                     const ::basegfx::B2DHomMatrix&	transform )
        {
            ::basegfx::unotools::affineMatrixFromHomMatrix( viewState.AffineTransform, transform );

            return viewState;
        }

        ::basegfx::B2DHomMatrix& getRenderStateTransform( ::basegfx::B2DHomMatrix&		transform,
                                                          const rendering::RenderState&	renderState )
        {
            return ::basegfx::unotools::homMatrixFromAffineMatrix( transform, renderState.AffineTransform );
        }

        rendering::RenderState& setRenderStateTransform( rendering::RenderState& 		renderState,
                                                         const ::basegfx::B2DHomMatrix&	transform )
        {
            ::basegfx::unotools::affineMatrixFromHomMatrix( renderState.AffineTransform, transform );

            return renderState;
        }

        rendering::RenderState& appendToRenderState( rendering::RenderState&		renderState,
                                                   const ::basegfx::B2DHomMatrix&	rTransform )
        {
            ::basegfx::B2DHomMatrix transform;

            getRenderStateTransform( transform, renderState );
            return setRenderStateTransform( renderState, transform * rTransform );
        }

        rendering::ViewState& appendToViewState( rendering::ViewState&			viewState,
                                                 const ::basegfx::B2DHomMatrix&	rTransform )
        {
            ::basegfx::B2DHomMatrix transform;

            getViewStateTransform( transform, viewState );
            return setViewStateTransform( viewState, transform * rTransform );
        }

        rendering::RenderState& prependToRenderState( rendering::RenderState&			renderState,
                                                      const ::basegfx::B2DHomMatrix&	rTransform )
        {
            ::basegfx::B2DHomMatrix transform;

            getRenderStateTransform( transform, renderState );
            return setRenderStateTransform( renderState, rTransform * transform );
        }

        rendering::ViewState& prependToViewState( rendering::ViewState&				viewState,
                                                  const ::basegfx::B2DHomMatrix&		rTransform )
        {
            ::basegfx::B2DHomMatrix transform;

            getViewStateTransform( transform, viewState );
            return setViewStateTransform( viewState, rTransform * transform );
        }

        ::basegfx::B2DHomMatrix& mergeViewAndRenderTransform( ::basegfx::B2DHomMatrix&		combinedTransform,
                                                              const rendering::ViewState&	viewState,
                                                              const rendering::RenderState& renderState	)
        {
            ::basegfx::B2DHomMatrix viewTransform;

            ::basegfx::unotools::homMatrixFromAffineMatrix( combinedTransform, renderState.AffineTransform );
            ::basegfx::unotools::homMatrixFromAffineMatrix( viewTransform, viewState.AffineTransform );

			// this statement performs combinedTransform = viewTransform * combinedTransform
            combinedTransform *= viewTransform; 

            return combinedTransform;
        }

        rendering::ViewState& mergeViewAndRenderState( rendering::ViewState&						resultViewState,
                                                       const rendering::ViewState&					viewState,
                                                       const rendering::RenderState&				renderState,
                                                       const uno::Reference< rendering::XCanvas >& 	/*xCanvas*/ 	)
        {
            ::basegfx::B2DHomMatrix		aTmpMatrix;
            geometry::AffineMatrix2D 	convertedMatrix;

            resultViewState.Clip = NULL; // TODO(F2): intersect clippings

            return setViewStateTransform( 
                resultViewState,
                mergeViewAndRenderTransform( aTmpMatrix,
                                             viewState, 
                                             renderState ) );
        }

        geometry::AffineMatrix2D& setIdentityAffineMatrix2D( geometry::AffineMatrix2D& matrix )
        {
            matrix.m00 = 1.0;
            matrix.m01 = 0.0;
            matrix.m02 = 0.0;
            matrix.m10 = 0.0;
            matrix.m11 = 1.0;
            matrix.m12 = 0.0;

            return matrix;
        }

        geometry::Matrix2D& setIdentityMatrix2D( geometry::Matrix2D& matrix )
        {
            matrix.m00 = 1.0;
            matrix.m01 = 0.0;
            matrix.m10 = 0.0;
            matrix.m11 = 1.0;

            return matrix;
        }

        namespace 
        { 
            class StandardColorSpace : public cppu::WeakImplHelper1< com::sun::star::rendering::XIntegerBitmapColorSpace >
            {
            private:
                uno::Sequence< sal_Int8 >  maComponentTags;
                uno::Sequence< sal_Int32 > maBitCounts;

                virtual ::sal_Int8 SAL_CALL getType(  ) throw (uno::RuntimeException)
                {
                    return rendering::ColorSpaceType::RGB;
                }
                virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags(  ) throw (uno::RuntimeException)
                {
                    return maComponentTags;
                }
                virtual ::sal_Int8 SAL_CALL getRenderingIntent(  ) throw (uno::RuntimeException)
                {
                    return rendering::RenderingIntent::PERCEPTUAL;
                }
                virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties(  ) throw (uno::RuntimeException)
                {
                    return uno::Sequence< beans::PropertyValue >();
                }
                virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor, 
                                                                            const uno::Reference< rendering::XColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException, 
                                                                                                                                                      uno::RuntimeException)
                {
                    // TODO(P3): if we know anything about target
                    // colorspace, this can be greatly sped up
                    uno::Sequence<rendering::ARGBColor> aIntermediate(
                        convertToARGB(deviceColor));
                    return targetColorSpace->convertFromARGB(aIntermediate);
                }
                virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
                {
                    const double*  pIn( deviceColor.getConstArray() );
                    const sal_Size nLen( deviceColor.getLength() );
                    ENSURE_ARG_OR_THROW2(nLen%4==0, 
                                         "number of channels no multiple of 4",
                                         static_cast<rendering::XColorSpace*>(this), 0);

                    uno::Sequence< rendering::RGBColor > aRes(nLen/4);
                    rendering::RGBColor* pOut( aRes.getArray() );
                    for( sal_Size i=0; i<nLen; i+=4 )
                    {
                        *pOut++ = rendering::RGBColor(pIn[0],pIn[1],pIn[2]);
                        pIn += 4;
                    }
                    return aRes;
                }
                virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
                {
                    const double*  pIn( deviceColor.getConstArray() );
                    const sal_Size nLen( deviceColor.getLength() );
                    ENSURE_ARG_OR_THROW2(nLen%4==0, 
                                         "number of channels no multiple of 4",
                                         static_cast<rendering::XColorSpace*>(this), 0);

                    uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
                    rendering::ARGBColor* pOut( aRes.getArray() );
                    for( sal_Size i=0; i<nLen; i+=4 )
                    {
                        *pOut++ = rendering::ARGBColor(pIn[3],pIn[0],pIn[1],pIn[2]);
                        pIn += 4;
                    }
                    return aRes;
                }
                virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
                {
                    const double*  pIn( deviceColor.getConstArray() );
                    const sal_Size nLen( deviceColor.getLength() );
                    ENSURE_ARG_OR_THROW2(nLen%4==0, 
                                         "number of channels no multiple of 4",
                                         static_cast<rendering::XColorSpace*>(this), 0);

                    uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
                    rendering::ARGBColor* pOut( aRes.getArray() );
                    for( sal_Size i=0; i<nLen; i+=4 )
                    {
                        *pOut++ = rendering::ARGBColor(pIn[3],pIn[3]*pIn[0],pIn[3]*pIn[1],pIn[3]*pIn[2]);
                        pIn += 4;
                    }
                    return aRes;
                }
                virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
                {
                    const rendering::RGBColor* pIn( rgbColor.getConstArray() );
                    const sal_Size             nLen( rgbColor.getLength() );

                    uno::Sequence< double > aRes(nLen*4);
                    double* pColors=aRes.getArray();
                    for( sal_Size i=0; i<nLen; ++i )
                    {
                        *pColors++ = pIn->Red;
                        *pColors++ = pIn->Green;
                        *pColors++ = pIn->Blue;
                        *pColors++ = 1.0;
                        ++pIn;
                    }
                    return aRes;
                }
                virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
                {
                    const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
                    const sal_Size              nLen( rgbColor.getLength() );

                    uno::Sequence< double > aRes(nLen*4);
                    double* pColors=aRes.getArray();
                    for( sal_Size i=0; i<nLen; ++i )
                    {
                        *pColors++ = pIn->Red;
                        *pColors++ = pIn->Green;
                        *pColors++ = pIn->Blue;
                        *pColors++ = pIn->Alpha;
                        ++pIn;
                    }
                    return aRes;
                }
                virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
                {
                    const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
                    const sal_Size              nLen( rgbColor.getLength() );

                    uno::Sequence< double > aRes(nLen*4);
                    double* pColors=aRes.getArray();
                    for( sal_Size i=0; i<nLen; ++i )
                    {
                        *pColors++ = pIn->Red/pIn->Alpha;
                        *pColors++ = pIn->Green/pIn->Alpha;
                        *pColors++ = pIn->Blue/pIn->Alpha;
                        *pColors++ = pIn->Alpha;
                        ++pIn;
                    }
                    return aRes;
                }

                // XIntegerBitmapColorSpace
                virtual ::sal_Int32 SAL_CALL getBitsPerPixel(  ) throw (uno::RuntimeException)
                {
                    return 32;
                }
                virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts(  ) throw (uno::RuntimeException)
                {
                    return maBitCounts;
                }
                virtual ::sal_Int8 SAL_CALL getEndianness(  ) throw (uno::RuntimeException)
                {
                    return util::Endianness::LITTLE;
                }
                virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor, 
                                                                                     const uno::Reference< rendering::XColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException, 
                                                                                                                                                               uno::RuntimeException)
                {
                    if( dynamic_cast<StandardColorSpace*>(targetColorSpace.get()) )
                    {
                        const sal_Int8* pIn( deviceColor.getConstArray() );
                        const sal_Size  nLen( deviceColor.getLength() );
                        ENSURE_ARG_OR_THROW2(nLen%4==0, 
                                             "number of channels no multiple of 4",
                                             static_cast<rendering::XColorSpace*>(this), 0);

                        uno::Sequence<double> aRes(nLen);
                        double* pOut( aRes.getArray() );
                        for( sal_Size i=0; i<nLen; i+=4 )
                        {
                            *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
                            *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
                            *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
                            *pOut++ = vcl::unotools::toDoubleColor(255-*pIn++);
                        }
                        return aRes;
                    }
                    else
                    {
                        // TODO(P3): if we know anything about target
                        // colorspace, this can be greatly sped up
                        uno::Sequence<rendering::ARGBColor> aIntermediate(
                            convertIntegerToARGB(deviceColor));
                        return targetColorSpace->convertFromARGB(aIntermediate);
                    }
                }
                virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor, 
                                                                                         const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException, 
                                                                                                                                                                              uno::RuntimeException)
                {
                    if( dynamic_cast<StandardColorSpace*>(targetColorSpace.get()) )
                    {
                        // it's us, so simply pass-through the data
                        return deviceColor;
                    }
                    else
                    {
                        // TODO(P3): if we know anything about target
                        // colorspace, this can be greatly sped up
                        uno::Sequence<rendering::ARGBColor> aIntermediate(
                            convertIntegerToARGB(deviceColor));
                        return targetColorSpace->convertIntegerFromARGB(aIntermediate);
                    }
                }
                virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
                {
                    const sal_Int8* pIn( deviceColor.getConstArray() );
                    const sal_Size  nLen( deviceColor.getLength() );
                    ENSURE_ARG_OR_THROW2(nLen%4==0, 
                                         "number of channels no multiple of 4",
                                         static_cast<rendering::XColorSpace*>(this), 0);

                    uno::Sequence< rendering::RGBColor > aRes(nLen/4);
                    rendering::RGBColor* pOut( aRes.getArray() );
                    for( sal_Size i=0; i<nLen; i+=4 )
                    {
                        *pOut++ = rendering::RGBColor(
                            vcl::unotools::toDoubleColor(pIn[0]),
                            vcl::unotools::toDoubleColor(pIn[1]),
                            vcl::unotools::toDoubleColor(pIn[2]));
                        pIn += 4;
                    }
                    return aRes;
                }

                virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
                {
                    const sal_Int8* pIn( deviceColor.getConstArray() );
                    const sal_Size  nLen( deviceColor.getLength() );
                    ENSURE_ARG_OR_THROW2(nLen%4==0, 
                                         "number of channels no multiple of 4",
                                         static_cast<rendering::XColorSpace*>(this), 0);

                    uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
                    rendering::ARGBColor* pOut( aRes.getArray() );
                    for( sal_Size i=0; i<nLen; i+=4 )
                    {
                        *pOut++ = rendering::ARGBColor(
                            vcl::unotools::toDoubleColor(255-pIn[3]),
                            vcl::unotools::toDoubleColor(pIn[0]),
                            vcl::unotools::toDoubleColor(pIn[1]),
                            vcl::unotools::toDoubleColor(pIn[2]));
                        pIn += 4;
                    }
                    return aRes;
                }

                virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
                {
                    const sal_Int8* pIn( deviceColor.getConstArray() );
                    const sal_Size  nLen( deviceColor.getLength() );
                    ENSURE_ARG_OR_THROW2(nLen%4==0, 
                                         "number of channels no multiple of 4",
                                         static_cast<rendering::XColorSpace*>(this), 0);

                    uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
                    rendering::ARGBColor* pOut( aRes.getArray() );
                    for( sal_Size i=0; i<nLen; i+=4 )
                    {
                        const sal_Int8 nAlpha( 255-pIn[3] );
                        *pOut++ = rendering::ARGBColor(
                            vcl::unotools::toDoubleColor(nAlpha),
                            vcl::unotools::toDoubleColor(nAlpha*pIn[0]),
                            vcl::unotools::toDoubleColor(nAlpha*pIn[1]),
                            vcl::unotools::toDoubleColor(nAlpha*pIn[2]));
                        pIn += 4;
                    }
                    return aRes;
                }

                virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
                {
                    const rendering::RGBColor* pIn( rgbColor.getConstArray() );
                    const sal_Size             nLen( rgbColor.getLength() );

                    uno::Sequence< sal_Int8 > aRes(nLen*4);
                    sal_Int8* pColors=aRes.getArray();
                    for( sal_Size i=0; i<nLen; ++i )
                    {
                        *pColors++ = vcl::unotools::toByteColor(pIn->Red);
                        *pColors++ = vcl::unotools::toByteColor(pIn->Green);
                        *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
                        *pColors++ = 0;
                        ++pIn;
                    }
                    return aRes;
                }

                virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
                {
                    const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
                    const sal_Size              nLen( rgbColor.getLength() );

                    uno::Sequence< sal_Int8 > aRes(nLen*4);
                    sal_Int8* pColors=aRes.getArray();
                    for( sal_Size i=0; i<nLen; ++i )
                    {
                        *pColors++ = vcl::unotools::toByteColor(pIn->Red);
                        *pColors++ = vcl::unotools::toByteColor(pIn->Green);
                        *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
                        *pColors++ = 255-vcl::unotools::toByteColor(pIn->Alpha);
                        ++pIn;
                    }
                    return aRes;
                }

                virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
                {
                    const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
                    const sal_Size              nLen( rgbColor.getLength() );

                    uno::Sequence< sal_Int8 > aRes(nLen*4);
                    sal_Int8* pColors=aRes.getArray();
                    for( sal_Size i=0; i<nLen; ++i )
                    {
                        *pColors++ = vcl::unotools::toByteColor(pIn->Red/pIn->Alpha);
                        *pColors++ = vcl::unotools::toByteColor(pIn->Green/pIn->Alpha);
                        *pColors++ = vcl::unotools::toByteColor(pIn->Blue/pIn->Alpha);
                        *pColors++ = 255-vcl::unotools::toByteColor(pIn->Alpha);
                        ++pIn;
                    }
                    return aRes;
                }

            public:
                StandardColorSpace() : 
                    maComponentTags(4),
                    maBitCounts(4)
                {
                    sal_Int8*  pTags = maComponentTags.getArray();
                    sal_Int32* pBitCounts = maBitCounts.getArray();
                    pTags[0] = rendering::ColorComponentTag::RGB_RED;
                    pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
                    pTags[2] = rendering::ColorComponentTag::RGB_BLUE;
                    pTags[3] = rendering::ColorComponentTag::ALPHA;

                    pBitCounts[0] = 
                    pBitCounts[1] = 
                    pBitCounts[2] = 
                    pBitCounts[3] = 8;
                }
            };

            struct StandardColorSpaceHolder : public rtl::StaticWithInit<uno::Reference<rendering::XIntegerBitmapColorSpace>,
                                                                         StandardColorSpaceHolder> 
            {
                uno::Reference<rendering::XIntegerBitmapColorSpace> operator()()
                {
                    return new StandardColorSpace();
                }
            }; 
        }

        uno::Reference<rendering::XIntegerBitmapColorSpace> getStdColorSpace()
        {
            return StandardColorSpaceHolder::get();
        }

        rendering::IntegerBitmapLayout getStdMemoryLayout( const geometry::IntegerSize2D& rBmpSize )
        {
            rendering::IntegerBitmapLayout aLayout;

            aLayout.ScanLines = rBmpSize.Height;
            aLayout.ScanLineBytes = rBmpSize.Width*4;
            aLayout.ScanLineStride = aLayout.ScanLineBytes;
            aLayout.PlaneStride = 0;
            aLayout.ColorSpace = getStdColorSpace();
            aLayout.Palette.clear();
            aLayout.IsMsbFirst = sal_False;

            return aLayout;
        }

        ::Color stdIntSequenceToColor( const uno::Sequence<sal_Int8>& rColor )
        {
#ifdef OSL_BIGENDIAN
            const sal_Int8* pCols( rColor.getConstArray() );
            return ::Color( pCols[3], pCols[0], pCols[1], pCols[2] );
#else
            return ::Color( *reinterpret_cast< const ::ColorData* >(rColor.getConstArray()) );
#endif            
        }

        uno::Sequence<sal_Int8> colorToStdIntSequence( const ::Color& rColor )
        {
            uno::Sequence<sal_Int8> aRet(4);
            sal_Int8* pCols( aRet.getArray() );
#ifdef OSL_BIGENDIAN
            pCols[0] = rColor.GetRed();
            pCols[1] = rColor.GetGreen();
            pCols[2] = rColor.GetBlue();
            pCols[3] = 255-rColor.GetTransparency();
#else
            *reinterpret_cast<sal_Int32*>(pCols) = rColor.GetColor();
#endif
            return aRet;
        }

        // Create a corrected view transformation out of the give one,
        // which ensures that the rectangle given by (0,0) and
        // rSpriteSize is mapped with its left,top corner to (0,0)
        // again. This is required to properly render sprite
        // animations to buffer bitmaps.
        ::basegfx::B2DHomMatrix& calcRectToOriginTransform( ::basegfx::B2DHomMatrix&			o_transform,
                                                            const ::basegfx::B2DRange&			i_srcRect,
                                                            const ::basegfx::B2DHomMatrix&		i_transformation )
        {
            if( i_srcRect.isEmpty() )
                return o_transform=i_transformation;

            // transform by given transformation
            ::basegfx::B2DRectangle aTransformedRect; 

            calcTransformedRectBounds( aTransformedRect, 
                                       i_srcRect,
                                       i_transformation );

            // now move resulting left,top point of bounds to (0,0)
            const basegfx::B2DHomMatrix aCorrectedTransform(basegfx::tools::createTranslateB2DHomMatrix(
                -aTransformedRect.getMinX(), -aTransformedRect.getMinY()));

            // prepend to original transformation
            o_transform = aCorrectedTransform * i_transformation;

            return o_transform;
        }

        ::basegfx::B2DRange& calcTransformedRectBounds( ::basegfx::B2DRange&			outRect,
                                                        const ::basegfx::B2DRange&		inRect,
                                                        const ::basegfx::B2DHomMatrix& 	transformation )
        {
            outRect.reset();

            if( inRect.isEmpty() )
                return outRect;

            // transform all four extremal points of the rectangle,
            // take bounding rect of those.
            
            // transform left-top point
            outRect.expand( transformation * inRect.getMinimum() );

            // transform bottom-right point
            outRect.expand( transformation * inRect.getMaximum() );

            ::basegfx::B2DPoint aPoint;

            // transform top-right point
            aPoint.setX( inRect.getMaxX() );
            aPoint.setY( inRect.getMinY() );

            aPoint *= transformation;
            outRect.expand( aPoint );

            // transform bottom-left point
            aPoint.setX( inRect.getMinX() );
            aPoint.setY( inRect.getMaxY() );

            aPoint *= transformation;
            outRect.expand( aPoint );

            // over and out.
            return outRect;
        }
        
        ::basegfx::B2DHomMatrix& calcRectToRectTransform( ::basegfx::B2DHomMatrix&			o_transform,
                                                          const ::basegfx::B2DRange&		destRect,
                                                          const ::basegfx::B2DRange&		srcRect,
                                                          const ::basegfx::B2DHomMatrix&	transformation )
        {
            if( srcRect.isEmpty() ||
                destRect.isEmpty() )
            {
                return o_transform=transformation;
            }

            // transform inputRect by transformation
            ::basegfx::B2DRectangle aTransformedRect; 
            calcTransformedRectBounds( aTransformedRect, 
                                       srcRect,
                                       transformation );

            // now move resulting left,top point of bounds to (0,0)
            basegfx::B2DHomMatrix aCorrectedTransform(basegfx::tools::createTranslateB2DHomMatrix(
                -aTransformedRect.getMinX(), -aTransformedRect.getMinY()));

            // scale to match outRect
            const double xDenom( aTransformedRect.getWidth() );
            const double yDenom( aTransformedRect.getHeight() );
            if( xDenom != 0.0 && yDenom != 0.0 )
                aCorrectedTransform.scale( destRect.getWidth() / xDenom, 
                                           destRect.getHeight() / yDenom );
            // TODO(E2): error handling

            // translate to final position
            aCorrectedTransform.translate( destRect.getMinX(), 
                                           destRect.getMinY() );
            
            ::basegfx::B2DHomMatrix transform( transformation );
            o_transform = aCorrectedTransform * transform;

            return o_transform;
        }

		bool isInside( const ::basegfx::B2DRange& 		rContainedRect,
                       const ::basegfx::B2DRange& 		rTransformRect, 
                       const ::basegfx::B2DHomMatrix&	rTransformation )
        {
            if( rContainedRect.isEmpty() || rTransformRect.isEmpty() )
                return false;

            ::basegfx::B2DPolygon aPoly( 
                ::basegfx::tools::createPolygonFromRect( rTransformRect ) );
            aPoly.transform( rTransformation );

            return ::basegfx::tools::isInside( aPoly,
                                               ::basegfx::tools::createPolygonFromRect(
                                                   rContainedRect ),
                                               true );
        }

        namespace
        {
            bool clipAreaImpl( ::basegfx::B2IRange*       o_pDestArea,
                               ::basegfx::B2IRange&       io_rSourceArea,
                               ::basegfx::B2IPoint&       io_rDestPoint,
                               const ::basegfx::B2IRange& rSourceBounds,
                               const ::basegfx::B2IRange& rDestBounds )
            {
                const ::basegfx::B2IPoint aSourceTopLeft( 
                    io_rSourceArea.getMinimum() );

                ::basegfx::B2IRange aLocalSourceArea( io_rSourceArea );

                // clip source area (which must be inside rSourceBounds)
                aLocalSourceArea.intersect( rSourceBounds );

                if( aLocalSourceArea.isEmpty() )
                    return false;
            
                // calc relative new source area points (relative to orig
                // source area)
                const ::basegfx::B2IVector aUpperLeftOffset( 
                    aLocalSourceArea.getMinimum()-aSourceTopLeft );
                const ::basegfx::B2IVector aLowerRightOffset( 
                    aLocalSourceArea.getMaximum()-aSourceTopLeft );

                ::basegfx::B2IRange aLocalDestArea( io_rDestPoint + aUpperLeftOffset,
                                                    io_rDestPoint + aLowerRightOffset );
            
                // clip dest area (which must be inside rDestBounds)
                aLocalDestArea.intersect( rDestBounds );
            
                if( aLocalDestArea.isEmpty() )
                    return false;

                // calc relative new dest area points (relative to orig
                // source area)
                const ::basegfx::B2IVector aDestUpperLeftOffset( 
                    aLocalDestArea.getMinimum()-io_rDestPoint );
                const ::basegfx::B2IVector aDestLowerRightOffset( 
                    aLocalDestArea.getMaximum()-io_rDestPoint );

                io_rSourceArea = ::basegfx::B2IRange( aSourceTopLeft + aDestUpperLeftOffset,
                                                      aSourceTopLeft + aDestLowerRightOffset );
                io_rDestPoint  = aLocalDestArea.getMinimum();

                if( o_pDestArea )
                    *o_pDestArea = aLocalDestArea;

                return true;
            }
        }

        bool clipScrollArea( ::basegfx::B2IRange&                  io_rSourceArea,
                             ::basegfx::B2IPoint&                  io_rDestPoint,
                             ::std::vector< ::basegfx::B2IRange >& o_ClippedAreas,
                             const ::basegfx::B2IRange&            rBounds )
        {
            ::basegfx::B2IRange aResultingDestArea;

            // compute full destination area (to determine uninitialized
            // areas below)
            const ::basegfx::B2I64Tuple& rRange( io_rSourceArea.getRange() );
            ::basegfx::B2IRange aInputDestArea( io_rDestPoint.getX(),
                                                io_rDestPoint.getY(),
                                                (io_rDestPoint.getX()
                                                 + static_cast<sal_Int32>(rRange.getX())),
                                                (io_rDestPoint.getY() 
                                                 + static_cast<sal_Int32>(rRange.getY())) );
            // limit to output area (no point updating outside of it)
            aInputDestArea.intersect( rBounds );

            // clip to rBounds
            if( !clipAreaImpl( &aResultingDestArea,
                               io_rSourceArea,
                               io_rDestPoint,
                               rBounds,
                               rBounds ) )
                return false;

            // finally, compute all areas clipped off the total
            // destination area.
            ::basegfx::computeSetDifference( o_ClippedAreas,
                                             aInputDestArea,
                                             aResultingDestArea );

            return true;
        }

        bool clipBlit( ::basegfx::B2IRange&       io_rSourceArea,
                       ::basegfx::B2IPoint&       io_rDestPoint,
                       const ::basegfx::B2IRange& rSourceBounds,
                       const ::basegfx::B2IRange& rDestBounds )
        {
            return clipAreaImpl( NULL,
                                 io_rSourceArea,
                                 io_rDestPoint,
                                 rSourceBounds,
                                 rDestBounds );
        }

        ::basegfx::B2IRange spritePixelAreaFromB2DRange( const ::basegfx::B2DRange& rRange )
        {
            if( rRange.isEmpty() )
                return ::basegfx::B2IRange();
            
            const ::basegfx::B2IPoint aTopLeft( ::basegfx::fround( rRange.getMinX() ),
                                                ::basegfx::fround( rRange.getMinY() ) );
            return ::basegfx::B2IRange( aTopLeft,
                                        aTopLeft + ::basegfx::B2IPoint( 
                                            ::basegfx::fround( rRange.getWidth() ),
                                            ::basegfx::fround( rRange.getHeight() ) ) );
        }

		uno::Sequence< uno::Any >& getDeviceInfo( const uno::Reference< rendering::XCanvas >& i_rxCanvas,
												  uno::Sequence< uno::Any >& 				  o_rxParams )
		{
			o_rxParams.realloc( 0 );
			
			if( i_rxCanvas.is() )
			{
                try
                {
                    uno::Reference< rendering::XGraphicDevice > xDevice( i_rxCanvas->getDevice(),
                                                                         uno::UNO_QUERY_THROW );
				
                    uno::Reference< lang::XServiceInfo >  xServiceInfo( xDevice, 
                                                                        uno::UNO_QUERY_THROW );
                    uno::Reference< beans::XPropertySet > xPropSet( xDevice, 
                                                                    uno::UNO_QUERY_THROW );
					
                    o_rxParams.realloc( 2 );
                
                    o_rxParams[ 0 ] = uno::makeAny( xServiceInfo->getImplementationName() );
                    o_rxParams[ 1 ] = uno::makeAny( xPropSet->getPropertyValue( 
                                                        ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("DeviceHandle") ) ) );
                }
                catch( uno::Exception& )
                {
                    // ignore, but return empty sequence
                }
			}
			
			return o_rxParams;
		}

        awt::Rectangle getAbsoluteWindowRect( const awt::Rectangle&                  rRect,
                                              const uno::Reference< awt::XWindow2 >& xWin  )
        {
            awt::Rectangle aRetVal( rRect );

            ::Window* pWindow = VCLUnoHelper::GetWindow(xWin);
            if( pWindow )
            {
                ::Point aPoint( aRetVal.X,
                                aRetVal.Y );

                aPoint = pWindow->OutputToScreenPixel( aPoint );

                aRetVal.X = aPoint.X();
                aRetVal.Y = aPoint.Y();
            }

            return aRetVal;
        }

        ::basegfx::B2DPolyPolygon getBoundMarksPolyPolygon( const ::basegfx::B2DRange& rRange )
        {
            ::basegfx::B2DPolyPolygon aPolyPoly;
            ::basegfx::B2DPolygon     aPoly;

            const double nX0( rRange.getMinX() );
            const double nY0( rRange.getMinY() );
            const double nX1( rRange.getMaxX() );
            const double nY1( rRange.getMaxY() );

            aPoly.append( ::basegfx::B2DPoint( nX0+4,
                                               nY0 ) );
            aPoly.append( ::basegfx::B2DPoint( nX0,
                                               nY0 ) );
            aPoly.append( ::basegfx::B2DPoint( nX0,
                                               nY0+4 ) );
            aPolyPoly.append( aPoly ); aPoly.clear();

            aPoly.append( ::basegfx::B2DPoint( nX1-4,
                                               nY0 ) );
            aPoly.append( ::basegfx::B2DPoint( nX1,
                                               nY0 ) );
            aPoly.append( ::basegfx::B2DPoint( nX1,
                                               nY0+4 ) );
            aPolyPoly.append( aPoly ); aPoly.clear();

            aPoly.append( ::basegfx::B2DPoint( nX0+4,
                                               nY1 ) );
            aPoly.append( ::basegfx::B2DPoint( nX0,
                                               nY1 ) );
            aPoly.append( ::basegfx::B2DPoint( nX0,
                                               nY1-4 ) );
            aPolyPoly.append( aPoly ); aPoly.clear();

            aPoly.append( ::basegfx::B2DPoint( nX1-4,
                                               nY1 ) );
            aPoly.append( ::basegfx::B2DPoint( nX1,
                                               nY1 ) );
            aPoly.append( ::basegfx::B2DPoint( nX1,
                                               nY1-4 ) );
            aPolyPoly.append( aPoly );

            return aPolyPoly;
        }

        int calcGradientStepCount( ::basegfx::B2DHomMatrix&      rTotalTransform,
                                   const rendering::ViewState&   viewState, 
                                   const rendering::RenderState& renderState,
                                   const rendering::Texture&     texture,
                                   int                           nColorSteps )
        {
            // calculate overall texture transformation (directly from
            // texture to device space).
            ::basegfx::B2DHomMatrix aMatrix;

            rTotalTransform.identity();
            ::basegfx::unotools::homMatrixFromAffineMatrix( rTotalTransform, 
                                                            texture.AffineTransform );
            ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
                                                         viewState,
                                                         renderState);
            rTotalTransform *= aMatrix; // prepend total view/render transformation

            // determine size of gradient in device coordinate system
            // (to e.g. determine sensible number of gradient steps)
            ::basegfx::B2DPoint aLeftTop( 0.0, 0.0 );
            ::basegfx::B2DPoint aLeftBottom( 0.0, 1.0 );
            ::basegfx::B2DPoint aRightTop( 1.0, 0.0 );
            ::basegfx::B2DPoint aRightBottom( 1.0, 1.0 );

            aLeftTop	*= rTotalTransform;
            aLeftBottom *= rTotalTransform;
            aRightTop 	*= rTotalTransform;
            aRightBottom*= rTotalTransform;

            // longest line in gradient bound rect
            const int nGradientSize( 
                static_cast<int>( 
                    ::std::max( 
                        ::basegfx::B2DVector(aRightBottom-aLeftTop).getLength(),
                        ::basegfx::B2DVector(aRightTop-aLeftBottom).getLength() ) + 1.0 ) );

            // typical number for pixel of the same color (strip size)
            const int nStripSize( nGradientSize < 50 ? 2 : 4 );

            // use at least three steps, and at utmost the number of color
            // steps
            return ::std::max( 3,
                               ::std::min( 
                                   nGradientSize / nStripSize,
                                   nColorSteps ) );
        }

	} // namespace tools

} // namespace canvas
