Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  dx_canvashelper.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */


#include <sal/config.h>
#include <sal/log.hxx>

#include <algorithm>

#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/point/b2dpoint.hxx>
#include <basegfx/utils/canvastools.hxx>
#include <com/sun/star/rendering/CompositeOperation.hpp>
#include <com/sun/star/rendering/PathCapType.hpp>
#include <com/sun/star/rendering/PathJoinType.hpp>
#include <com/sun/star/rendering/RepaintResult.hpp>
#include <com/sun/star/rendering/TexturingMode.hpp>
#include <comphelper/sequence.hxx>
#include <o3tl/char16_t2wchar_t.hxx>
#include <rtl/math.hxx>
#include <comphelper/diagnose_ex.hxx>

#include <canvas/canvastools.hxx>

#include "dx_canvasfont.hxx"
#include "dx_canvashelper.hxx"
#include "dx_impltools.hxx"
#include "dx_spritecanvas.hxx"
#include "dx_textlayout.hxx"
#include "dx_vcltools.hxx"

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

namespace dxcanvas
{
    namespace
    {
        Gdiplus::LineCap gdiLineCapFromCap( sal_Int8 nCapType )
        {
            switch( nCapType )
            {
                case rendering::PathCapType::BUTT:
                    return Gdiplus::LineCapFlat;

                case rendering::PathCapType::ROUND:
                    return Gdiplus::LineCapRound;

                case rendering::PathCapType::SQUARE:
                    return Gdiplus::LineCapSquare;

                default:
                    ENSURE_OR_THROW( false,
                                      "gdiLineCapFromCap(): Unexpected cap type" );
            }

            return Gdiplus::LineCapFlat;
        }

        Gdiplus::DashCap gdiDashCapFromCap( sal_Int8 nCapType )
        {
            switch( nCapType )
            {
                case rendering::PathCapType::BUTT:
                    return Gdiplus::DashCapFlat;

                case rendering::PathCapType::ROUND:
                    return Gdiplus::DashCapRound;

                // Gdiplus does not know square, using flat would make short
                // dashes disappear, so use triangle as the closest one.
                case rendering::PathCapType::SQUARE:
                    return Gdiplus::DashCapTriangle;

                default:
                    ENSURE_OR_THROW( false,
                                      "gdiDashCapFromCap(): Unexpected cap type" );
            }

            return Gdiplus::DashCapFlat;
        }

        Gdiplus::LineJoin gdiJoinFromJoin( sal_Int8 nJoinType )
        {
            switch( nJoinType )
            {
                case rendering::PathJoinType::NONE:
                    SAL_WARN( "canvas.directx""gdiJoinFromJoin(): Join NONE not possible, mapping to BEVEL (closest to NONE)" );
                    return Gdiplus::LineJoinBevel;

                case rendering::PathJoinType::MITER:
                    // in GDI+ fallback to Bevel, if miter limit is exceeded, is not done
                    // by Gdiplus::LineJoinMiter but by Gdiplus::LineJoinMiterClipped
                    return Gdiplus::LineJoinMiterClipped;

                case rendering::PathJoinType::ROUND:
                    return Gdiplus::LineJoinRound;

                case rendering::PathJoinType::BEVEL:
                    return Gdiplus::LineJoinBevel;

                default:
                    ENSURE_OR_THROW( false,
                                      "gdiJoinFromJoin(): Unexpected join type" );
            }

            return Gdiplus::LineJoinMiter;
        }
    }

    CanvasHelper::CanvasHelper() :
        mpGdiPlusUser( GDIPlusUser::createInstance() ),
        mpDevice( nullptr ),
        mpGraphicsProvider(),
        maOutputOffset()
    {
    }

    void CanvasHelper::disposing()
    {
        mpGraphicsProvider.reset();
        mpDevice = nullptr;
        mpGdiPlusUser.reset();
    }

    void CanvasHelper::setDevice( rendering::XGraphicDevice& rDevice )
    {
        mpDevice = &rDevice;
    }

    void CanvasHelper::setTarget( const GraphicsProviderSharedPtr& rTarget )
    {
        ENSURE_OR_THROW( rTarget,
                          "CanvasHelper::setTarget(): Invalid target" );
        ENSURE_OR_THROW( !mpGraphicsProvider,
                          "CanvasHelper::setTarget(): target set, old target would be overwritten" );

        mpGraphicsProvider = rTarget;
    }

    void CanvasHelper::setTarget( const GraphicsProviderSharedPtr& rTarget,
                                  const ::basegfx::B2ISize&        rOutputOffset )
    {
        ENSURE_OR_THROW( rTarget,
                         "CanvasHelper::setTarget(): invalid target" );
        ENSURE_OR_THROW( !mpGraphicsProvider,
                         "CanvasHelper::setTarget(): target set, old target would be overwritten" );

        mpGraphicsProvider = rTarget;
        maOutputOffset = rOutputOffset;
    }

    void CanvasHelper::clear()
    {
        if( needOutput() )
        {
            GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
            Gdiplus::Color aClearColor{Gdiplus::ARGB(Gdiplus::Color::White)};

            ENSURE_OR_THROW(
                Gdiplus::Ok == pGraphics->SetCompositingMode(
                    Gdiplus::CompositingModeSourceCopy ), // force set, don't blend
                "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
            ENSURE_OR_THROW(
                Gdiplus::Ok == pGraphics->Clear( aClearColor ),
                "CanvasHelper::clear(): GDI+ Clear call failed" );
        }
    }

    void CanvasHelper::drawPoint( const rendering::XCanvas*     /*pCanvas*/,
                                  const geometry::RealPoint2D&  aPoint,
                                  const rendering::ViewState&   viewState,
                                  const rendering::RenderState& renderState )
    {
        if( needOutput() )
        {
            GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );

            setupGraphicsState( pGraphics, viewState, renderState );

            Gdiplus::SolidBrush aBrush(
                Gdiplus::Color(
                    tools::sequenceToArgb(renderState.DeviceColor)) );

            // determine size of one-by-one device pixel ellipse
            Gdiplus::Matrix aMatrix;
            pGraphics->GetTransform(&aMatrix);
            aMatrix.Invert();
            Gdiplus::PointF vector(1, 1);
            aMatrix.TransformVectors(&vector);

            // paint a one-by-one circle, with the given point
            // in the middle (rounded to float)
            ENSURE_OR_THROW(
                Gdiplus::Ok == pGraphics->FillEllipse( &aBrush,
                                                       // disambiguate call
                                                       Gdiplus::REAL(aPoint.X),
                                                       Gdiplus::REAL(aPoint.Y),
                                                       Gdiplus::REAL(vector.X),
                                                       Gdiplus::REAL(vector.Y) ),
                "CanvasHelper::drawPoint(): GDI+ call failed" );
        }
    }

    void CanvasHelper::drawLine( const rendering::XCanvas*      /*pCanvas*/,
                                 const geometry::RealPoint2D&   aStartPoint,
                                 const geometry::RealPoint2D&   aEndPoint,
                                 const rendering::ViewState&    viewState,
                                 const rendering::RenderState&  renderState )
    {
        if( needOutput() )
        {
            GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );

            setupGraphicsState( pGraphics, viewState, renderState );

            Gdiplus::Pen aPen(
                Gdiplus::Color(
                    tools::sequenceToArgb(renderState.DeviceColor)),
                Gdiplus::REAL(0.0) );

            // #122683# Switched precedence of pixel offset
            // mode. Seemingly, polygon stroking needs
            // PixelOffsetModeNone to achieve visually pleasing
            // results, whereas all other operations (e.g. polygon
            // fills, bitmaps) look better with PixelOffsetModeHalf.
            const Gdiplus::PixelOffsetMode aOldMode(
                pGraphics->GetPixelOffsetMode() );
            pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );

            Gdiplus::Status hr = pGraphics->DrawLine( &aPen,
                                                      Gdiplus::REAL(aStartPoint.X), // disambiguate call
                                                      Gdiplus::REAL(aStartPoint.Y),
                                                      Gdiplus::REAL(aEndPoint.X),
                                                      Gdiplus::REAL(aEndPoint.Y) );
            pGraphics->SetPixelOffsetMode( aOldMode );

            ENSURE_OR_THROW(
                Gdiplus::Ok == hr,
                "CanvasHelper::drawLine(): GDI+ call failed" );
        }
    }

    void CanvasHelper::drawBezier( const rendering::XCanvas*            /*pCanvas*/,
                                   const geometry::RealBezierSegment2D& aBezierSegment,
                                   const geometry::RealPoint2D&         aEndPoint,
                                   const rendering::ViewState&          viewState,
                                   const rendering::RenderState&        renderState )
    {
        if( needOutput() )
        {
            GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );

            setupGraphicsState( pGraphics, viewState, renderState );

            Gdiplus::Pen aPen(
                Gdiplus::Color(
                    tools::sequenceToArgb(renderState.DeviceColor)),
                Gdiplus::REAL(0.0) );

            // #122683# Switched precedence of pixel offset
            // mode. Seemingly, polygon stroking needs
            // PixelOffsetModeNone to achieve visually pleasing
            // results, whereas all other operations (e.g. polygon
            // fills, bitmaps) look better with PixelOffsetModeHalf.
            const Gdiplus::PixelOffsetMode aOldMode(
                pGraphics->GetPixelOffsetMode() );
            pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );

            Gdiplus::Status hr = pGraphics->DrawBezier( &aPen,
                                                        Gdiplus::REAL(aBezierSegment.Px), // disambiguate call
                                                        Gdiplus::REAL(aBezierSegment.Py),
                                                        Gdiplus::REAL(aBezierSegment.C1x),
                                                        Gdiplus::REAL(aBezierSegment.C1y),
                                                        Gdiplus::REAL(aEndPoint.X),
                                                        Gdiplus::REAL(aEndPoint.Y),
                                                        Gdiplus::REAL(aBezierSegment.C2x),
                                                        Gdiplus::REAL(aBezierSegment.C2y) );

            pGraphics->SetPixelOffsetMode( aOldMode );

            ENSURE_OR_THROW(
                Gdiplus::Ok == hr,
                "CanvasHelper::drawBezier(): GDI+ call failed" );
        }
    }

    uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas*                          /*pCanvas*/,
                                                                                 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
                                                                                 const rendering::ViewState&                        viewState,
                                                                                 const rendering::RenderState&                      renderState )
    {
        ENSURE_OR_THROW( xPolyPolygon.is(),
                          "CanvasHelper::drawPolyPolygon: polygon is NULL");

        if( needOutput() )
        {
            GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );

            setupGraphicsState( pGraphics, viewState, renderState );

            Gdiplus::Pen aPen(
                Gdiplus::Color(
                    tools::sequenceToArgb(renderState.DeviceColor)),
                Gdiplus::REAL(0.0) );

            // #122683# Switched precedence of pixel offset
            // mode. Seemingly, polygon stroking needs
            // PixelOffsetModeNone to achieve visually pleasing
            // results, whereas all other operations (e.g. polygon
            // fills, bitmaps) look better with PixelOffsetModeHalf.
            const Gdiplus::PixelOffsetMode aOldMode(
                pGraphics->GetPixelOffsetMode() );
            pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );

            GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ) );

            // TODO(E1): Return value
            Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() );

            pGraphics->SetPixelOffsetMode( aOldMode );

            ENSURE_OR_THROW(
                Gdiplus::Ok == hr,
                "CanvasHelper::drawPolyPolygon(): GDI+ call failed" );
        }

        // TODO(P1): Provide caching here.
        return uno::Reference< rendering::XCachedPrimitive >(nullptr);
    }

    uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas*                            /*pCanvas*/,
                                                                                   const uno::Reference< rendering::XPolyPolygon2D >&   xPolyPolygon,
                                                                                   const rendering::ViewState&                          viewState,
                                                                                   const rendering::RenderState&                        renderState,
                                                                                   const rendering::StrokeAttributes&                   strokeAttributes )
    {
        ENSURE_OR_THROW( xPolyPolygon.is(),
                          "CanvasHelper::drawPolyPolygon: polygon is NULL");

        if( needOutput() )
        {
            GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );

            setupGraphicsState( pGraphics, viewState, renderState );


            // Setup stroke pen


            Gdiplus::Pen aPen(
                Gdiplus::Color(
                    tools::sequenceToArgb(renderState.DeviceColor)),
                static_cast< Gdiplus::REAL >(strokeAttributes.StrokeWidth) );

            // #122683# Switched precedence of pixel offset
            // mode. Seemingly, polygon stroking needs
            // PixelOffsetModeNone to achieve visually pleasing
            // results, whereas all other operations (e.g. polygon
            // fills, bitmaps) look better with PixelOffsetModeHalf.
            const Gdiplus::PixelOffsetMode aOldMode(
                pGraphics->GetPixelOffsetMode() );
            pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );

            const bool bIsMiter(rendering::PathJoinType::MITER == strokeAttributes.JoinType);
            const bool bIsNone(rendering::PathJoinType::NONE == strokeAttributes.JoinType);

            if(bIsMiter)
                aPen.SetMiterLimit( static_cast< Gdiplus::REAL >(strokeAttributes.MiterLimit) );

            const std::vector< Gdiplus::REAL >& rDashArray(
                ::comphelper::sequenceToContainer< std::vector< Gdiplus::REAL >, double >(
                    strokeAttributes.DashArray ) );
            if( !rDashArray.empty() )
            {
                aPen.SetDashPattern( rDashArray.data(),
                                     rDashArray.size() );
            }
            aPen.SetLineCap( gdiLineCapFromCap(strokeAttributes.StartCapType),
                             gdiLineCapFromCap(strokeAttributes.EndCapType),
                             gdiDashCapFromCap(strokeAttributes.StartCapType));
            if(!bIsNone)
                aPen.SetLineJoin( gdiJoinFromJoin(strokeAttributes.JoinType) );

            GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon, bIsNone ) );

            // TODO(E1): Return value
            Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() );

            pGraphics->SetPixelOffsetMode( aOldMode );

            ENSURE_OR_THROW(
                Gdiplus::Ok == hr,
                "CanvasHelper::strokePolyPolygon(): GDI+ call failed" );
        }

        // TODO(P1): Provide caching here.
        return uno::Reference< rendering::XCachedPrimitive >(nullptr);
    }

    uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygonconst rendering::XCanvas*                            /*pCanvas*/,
                                                                                           const uno::Reference< rendering::XPolyPolygon2D >&   /*xPolyPolygon*/,
                                                                                           const rendering::ViewState&                          /*viewState*/,
                                                                                           const rendering::RenderState&                        /*renderState*/,
                                                                                           const uno::Sequence< rendering::Texture >&           /*textures*/,
                                                                                           const rendering::StrokeAttributes&                   /*strokeAttributes*/ )
    {
        // TODO
        return uno::Reference< rendering::XCachedPrimitive >(nullptr);
    }

    uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas*                           /*pCanvas*/,
                                                                                                const uno::Reference< rendering::XPolyPolygon2D >&  /*xPolyPolygon*/,
                                                                                                const rendering::ViewState&                         /*viewState*/,
                                                                                                const rendering::RenderState&                       /*renderState*/,
                                                                                                const uno::Sequence< rendering::Texture >&          /*textures*/,
                                                                                                const uno::Reference< geometry::XMapping2D >&       /*xMapping*/,
                                                                                                const rendering::StrokeAttributes&                  /*strokeAttributes*/ )
    {
        // TODO
        return uno::Reference< rendering::XCachedPrimitive >(nullptr);
    }

    uno::Reference< rendering::XPolyPolygon2D >   CanvasHelper::queryStrokeShapes( const rendering::XCanvas*                            /*pCanvas*/,
                                                                                   const uno::Reference< rendering::XPolyPolygon2D >&   /*xPolyPolygon*/,
                                                                                   const rendering::ViewState&                          /*viewState*/,
                                                                                   const rendering::RenderState&                        /*renderState*/,
                                                                                   const rendering::StrokeAttributes&                   /*strokeAttributes*/ )
    {
        // TODO
        return uno::Reference< rendering::XPolyPolygon2D >(nullptr);
    }

    uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas*                          /*pCanvas*/,
                                                                                 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
                                                                                 const rendering::ViewState&                        viewState,
                                                                                 const rendering::RenderState&                      renderState )
    {
        ENSURE_OR_THROW( xPolyPolygon.is(),
                          "CanvasHelper::fillPolyPolygon: polygon is NULL");

        if( needOutput() )
        {
            GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );

            setupGraphicsState( pGraphics, viewState, renderState );

            Gdiplus::SolidBrush aBrush(
                tools::sequenceToArgb(renderState.DeviceColor));

            GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ) );

            // TODO(F1): FillRule
            ENSURE_OR_THROW( Gdiplus::Ok == pGraphics->FillPath( &aBrush, pPath.get() ),
                             "CanvasHelper::fillPolyPolygon(): GDI+ call failed " );
        }

        // TODO(P1): Provide caching here.
        return uno::Reference< rendering::XCachedPrimitive >(nullptr);
    }

    uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas*                             /*pCanvas*/,
                                                                                              const uno::Reference< rendering::XPolyPolygon2D >&    /*xPolyPolygon*/,
                                                                                              const rendering::ViewState&                           /*viewState*/,
                                                                                              const rendering::RenderState&                         /*renderState*/,
                                                                                              const uno::Sequence< rendering::Texture >&            /*textures*/,
                                                                                              const uno::Reference< geometry::XMapping2D >&         /*xMapping*/ )
    {
        // TODO
        return uno::Reference< rendering::XCachedPrimitive >(nullptr);
    }

    uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas*                    /*pCanvas*/,
                                                                       const rendering::FontRequest&                fontRequest,
                                                                       const uno::Sequence< beans::PropertyValue >& extraFontProperties,
                                                                       const geometry::Matrix2D&                    fontMatrix )
    {
        if( needOutput() )
        {
            return uno::Reference< rendering::XCanvasFont >(
                    new CanvasFont(fontRequest, extraFontProperties, fontMatrix ) );
        }

        return uno::Reference< rendering::XCanvasFont >();
    }

    uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas*                       /*pCanvas*/,
                                                                            const rendering::FontInfo&                      /*aFilter*/,
                                                                            const uno::Sequence< beans::PropertyValue >&    /*aFontProperties*/ )
    {
        // TODO
        return uno::Sequence< rendering::FontInfo >();
    }

    uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas*                         /*pCanvas*/,
                                                                          const rendering::StringContext&                   text,
                                                                          const uno::Reference< rendering::XCanvasFont >&   xFont,
                                                                          const rendering::ViewState&                       viewState,
                                                                          const rendering::RenderState&                     renderState,
                                                                          sal_Int8                                          /*textDirection*/ )
    {
        ENSURE_OR_THROW( xFont.is(),
                          "CanvasHelper::drawText: font is NULL");

        if( needOutput() )
        {
            GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );

            setupGraphicsState( pGraphics, viewState, renderState );

            Gdiplus::SolidBrush aBrush(
                Gdiplus::Color(
                    tools::sequenceToArgb(renderState.DeviceColor)));

            CanvasFont::ImplRef pFont(
                tools::canvasFontFromXFont(xFont) );

            // Move glyphs up, such that output happens at the font
            // baseline.
            Gdiplus::PointF aPoint( 0.0,
                                    static_cast<Gdiplus::REAL>(-(pFont->getFont()->GetSize()*
                                                                 pFont->getCellAscent() /
                                                                 pFont->getEmHeight())) );

            // TODO(F1): According to
            // http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q307208,
            // we might have to revert to GDI and ExTextOut here,
            // since GDI+ takes the scalability a little bit too
            // far...

            // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use
            // DrawDriverString here, and perform layouting myself...
            ENSURE_OR_THROW(
                Gdiplus::Ok == pGraphics->DrawString( o3tl::toW(text.Text.copy( text.StartPosition,
                                                                            text.Length ).getStr()),
                                                      text.Length,
                                                      pFont->getFont().get(),
                                                      aPoint,
                                                      &aBrush ),
                "CanvasHelper::drawText(): GDI+ call failed" );
        }

        return uno::Reference< rendering::XCachedPrimitive >(nullptr);
    }

    uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas*                       /*pCanvas*/,
                                                                                const uno::Reference< rendering::XTextLayout >& xLayoutetText,
                                                                                const rendering::ViewState&                     viewState,
                                                                                const rendering::RenderState&                   renderState )
    {
        ENSURE_OR_THROW( xLayoutetText.is(),
                          "CanvasHelper::drawTextLayout: layout is NULL");

        if( needOutput() )
        {
            TextLayout* pTextLayout =
                dynamic_cast< TextLayout* >( xLayoutetText.get() );

            ENSURE_OR_THROW( pTextLayout,
                                "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );

            pTextLayout->draw( mpGraphicsProvider->getGraphics(),
                               viewState,
                               renderState,
                               maOutputOffset,
                               mpDevice,
                               false );
        }

        return uno::Reference< rendering::XCachedPrimitive >(nullptr);
    }

    uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas*                   /*pCanvas*/,
                                                                            const uno::Reference< rendering::XBitmap >& xBitmap,
                                                                            const rendering::ViewState&                 viewState,
                                                                            const rendering::RenderState&               renderState )
    {
        ENSURE_OR_THROW( xBitmap.is(),
                          "CanvasHelper::drawBitmap: bitmap is NULL");

        if( needOutput() )
        {
            // check whether one of our own objects - need to retrieve
            // bitmap _before_ calling
            // GraphicsProvider::getGraphics(), to avoid locking our
            // own surface.
            BitmapSharedPtr pGdiBitmap;
            BitmapProvider* pBitmap = dynamic_cast< BitmapProvider* >(xBitmap.get());
            if( pBitmap )
            {
                IBitmapSharedPtr pDXBitmap( pBitmap->getBitmap() );
                if( pDXBitmap )
                    pGdiBitmap = pDXBitmap->getBitmap();
            }

            GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
            setupGraphicsState( pGraphics, viewState, renderState );

            if( pGdiBitmap )
                tools::drawGdiPlusBitmap(pGraphics,pGdiBitmap);
            else
                tools::drawVCLBitmapFromXBitmap(pGraphics,
                                                xBitmap);
        }

        // TODO(P1): Provide caching here.
        return uno::Reference< rendering::XCachedPrimitive >(nullptr);
    }

    uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas*                      pCanvas,
                                                                                     const uno::Reference< rendering::XBitmap >&    xBitmap,
                                                                                     const rendering::ViewState&                    viewState,
                                                                                     const rendering::RenderState&                  renderState )
    {
        ENSURE_OR_THROW( xBitmap.is(),
                          "CanvasHelper::drawBitmap: bitmap is NULL");

        // no color set -> this is equivalent to a plain drawBitmap(), then
        if( renderState.DeviceColor.getLength() < 3 )
            return drawBitmap( pCanvas, xBitmap, viewState, renderState );

        if( needOutput() )
        {
            GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );

            setupGraphicsState( pGraphics, viewState, renderState );

            BitmapSharedPtr pBitmap( tools::bitmapFromXBitmap( xBitmap ) );
            Gdiplus::Rect aRect( 0, 0,
                                 pBitmap->GetWidth(),
                                 pBitmap->GetHeight() );

            // Setup an ImageAttributes with an alpha-modulating
            // color matrix.
            rendering::ARGBColor aARGBColor(
                mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0]);

            Gdiplus::ImageAttributes aImgAttr;
            tools::setModulateImageAttributes( aImgAttr,
                                               aARGBColor.Red,
                                               aARGBColor.Green,
                                               aARGBColor.Blue,
                                               aARGBColor.Alpha );

            ENSURE_OR_THROW(
                Gdiplus::Ok == pGraphics->DrawImage( pBitmap.get(),
                                                     aRect,
                                                     0, 0,
                                                     pBitmap->GetWidth(),
                                                     pBitmap->GetHeight(),
                                                     Gdiplus::UnitPixel,
                                                     &aImgAttr ),
                "CanvasHelper::drawBitmapModulated(): GDI+ call failed" );
        }

        // TODO(P1): Provide caching here.
        return uno::Reference< rendering::XCachedPrimitive >(nullptr);
    }

    uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice()
    {
        return uno::Reference< rendering::XGraphicDevice >(mpDevice);
    }

    // private helper


    Gdiplus::CompositingMode CanvasHelper::calcCompositingMode( sal_Int8 nMode )
    {
        Gdiplus::CompositingMode aRet( Gdiplus::CompositingModeSourceOver );

        switch( nMode )
        {
            case rendering::CompositeOperation::OVER:
            case rendering::CompositeOperation::CLEAR:
                aRet = Gdiplus::CompositingModeSourceOver;
                break;

            case rendering::CompositeOperation::SOURCE:
                aRet = Gdiplus::CompositingModeSourceCopy;
                break;

            case rendering::CompositeOperation::DESTINATION:
            case rendering::CompositeOperation::UNDER:
            case rendering::CompositeOperation::INSIDE:
            case rendering::CompositeOperation::INSIDE_REVERSE:
            case rendering::CompositeOperation::OUTSIDE:
            case rendering::CompositeOperation::OUTSIDE_REVERSE:
            case rendering::CompositeOperation::ATOP:
            case rendering::CompositeOperation::ATOP_REVERSE:
            case rendering::CompositeOperation::XOR:
            case rendering::CompositeOperation::ADD:
            case rendering::CompositeOperation::SATURATE:
                // TODO(F2): Problem, because GDI+ only knows about two compositing modes
                aRet = Gdiplus::CompositingModeSourceOver;
                break;

            default:
                ENSURE_OR_THROW( false"CanvasHelper::calcCompositingMode: unexpected mode" );
                break;
        }

        return aRet;
    }

    void CanvasHelper::setupGraphicsState( GraphicsSharedPtr const & rGraphics,
                                           const rendering::ViewState&   viewState,
                                           const rendering::RenderState& renderState )
    {
        ENSURE_OR_THROW( needOutput(),
                          "CanvasHelper::setupGraphicsState: primary graphics invalid" );
        ENSURE_OR_THROW( mpDevice,
                          "CanvasHelper::setupGraphicsState: reference device invalid" );

        // setup view transform first. Clipping e.g. depends on it
        ::basegfx::B2DHomMatrix aTransform = ::canvas::tools::getViewStateTransform(viewState);

        // add output offset
        if( !maOutputOffset.equalZero() )
        {
            const basegfx::B2DHomMatrix aOutputOffset(basegfx::utils::createTranslateB2DHomMatrix(
                maOutputOffset.getWidth(), maOutputOffset.getHeight()));
            aTransform = aOutputOffset * aTransform;
        }

        Gdiplus::Matrix aMatrix;
        tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform );

        ENSURE_OR_THROW(
            Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
            "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" );

        // setup view and render state clipping
        ENSURE_OR_THROW(
            Gdiplus::Ok == rGraphics->ResetClip(),
            "CanvasHelper::setupGraphicsState(): Failed to reset GDI+ clip" );

        if( viewState.Clip.is() )
        {
            GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( viewState.Clip ) );

            // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
            // Try SetClip( Rect ) or similar for simple clip paths (need some support in
            // LinePolyPolygon, then)
            ENSURE_OR_THROW(
                Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
                                                   Gdiplus::CombineModeIntersect ),
                "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
        }

        // setup overall transform only now. View clip above was relative to
        // view transform
        ::canvas::tools::mergeViewAndRenderTransform(aTransform,
                                                     viewState,
                                                     renderState);

        // add output offset
        if( !maOutputOffset.equalZero() )
        {
            const basegfx::B2DHomMatrix aOutputOffset(basegfx::utils::createTranslateB2DHomMatrix(
                maOutputOffset.getWidth(), maOutputOffset.getHeight()));
            aTransform = aOutputOffset * aTransform;
        }

        tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform );

        ENSURE_OR_THROW(
            Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
            "CanvasHelper::setupGraphicsState(): Cannot set GDI+ transformation" );

        if( renderState.Clip.is() )
        {
            GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( renderState.Clip ) );

            // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
            // Try SetClip( Rect ) or similar for simple clip paths (need some support in
            // LinePolyPolygon, then)
            ENSURE_OR_THROW(
                Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
                                                   Gdiplus::CombineModeIntersect ),
                "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
        }

        // setup compositing
        const Gdiplus::CompositingMode eCompositing( calcCompositingMode( renderState.CompositeOperation ) );
        ENSURE_OR_THROW(
            Gdiplus::Ok == rGraphics->SetCompositingMode( eCompositing ),
            "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" );
    }

    void CanvasHelper::flush() const
    {
        if( needOutput() )
            mpGraphicsProvider->getGraphics()->Flush( Gdiplus::FlushIntentionSync );
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=89 H=93 G=90

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge