Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/source/graphic/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 32 kB image not shown  

Quelle  GraphicObject.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 <algorithm>

#include <o3tl/string_view.hxx>
#include <osl/diagnose.h>
#include <tools/fract.hxx>
#include <tools/helpers.hxx>
#include <utility>
#include <vcl/svapp.hxx>
#include <vcl/metaact.hxx>
#include <vcl/GraphicObject.hxx>
#include <vcl/GraphicLoader.hxx>
#include <vcl/outdev.hxx>

#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/graphic/XGraphic.hpp>
#include <memory>


using namespace css;
using com::sun::star::uno::Reference;
using com::sun::star::uno::XInterface;
using com::sun::star::uno::UNO_QUERY;
using com::sun::star::uno::Sequence;
using com::sun::star::container::XNameContainer;
using com::sun::star::beans::XPropertySet;

#define WATERMARK_LUM_OFFSET        50
#define WATERMARK_CON_OFFSET        -70

namespace vcl::graphic
{

void SearchForGraphics(uno::Reference<uno::XInterface> const & xInterface,
                       std::vector<uno::Reference<css::graphic::XGraphic>> & raGraphicList)
{
    uno::Reference<beans::XPropertySet> xPropertySet(xInterface, UNO_QUERY);
    if (xPropertySet.is())
    {
        if (xPropertySet->getPropertySetInfo()->hasPropertyByName(u"ImageURL"_ustr))
        {
            OUString sURL;
            xPropertySet->getPropertyValue(u"ImageURL"_ustr) >>= sURL;
            if (!sURL.isEmpty() && !GraphicObject::isGraphicObjectUniqueIdURL(sURL))
            {
                Graphic aGraphic = vcl::graphic::loadFromURL(sURL);
                if (!aGraphic.IsNone())
                {
                    raGraphicList.push_back(aGraphic.GetXGraphic());
                }
            }
        } else if (xPropertySet->getPropertySetInfo()->hasPropertyByName(u"Graphic"_ustr))
        {
            uno::Reference<css::graphic::XGraphic> xGraphic;
            xPropertySet->getPropertyValue(u"Graphic"_ustr) >>= xGraphic;
            if (xGraphic.is())
            {
                raGraphicList.push_back(xGraphic);
            }
        }
    }
    Reference<XNameContainer> xContainer(xInterface, UNO_QUERY);
    if (xContainer.is())
    {
        const css::uno::Sequence<OUString> aElementNames = xContainer->getElementNames();
        for (OUString const & rName : aElementNames)
        {
            uno::Reference<XInterface> xInnerInterface;
            xContainer->getByName(rName) >>= xInnerInterface;
            SearchForGraphics(xInnerInterface, raGraphicList);
        }
    }
}

// end namespace vcl::graphic

namespace
{

bool lclDrawObj(OutputDevice& rOut, const Point& rPt, const Size& rSz,
                GraphicObject const & rObj, const GraphicAttr& rAttr)
{
    Point   aPt( rPt );
    Size    aSz( rSz );
    bool    bRet = false;

    if( ( rObj.GetType() == GraphicType::Bitmap ) || ( rObj.GetType() == GraphicType::GdiMetafile ) )
    {
        // simple output of transformed graphic
        const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) );

        if( aGraphic.IsSupportedGraphic() )
        {
            const Degree10 nRot10 = rAttr.GetRotation() % 3600_deg10;

            if( nRot10 )
            {
                tools::Polygon aPoly( tools::Rectangle( aPt, aSz ) );

                aPoly.Rotate( aPt, nRot10 );
                const tools::Rectangle aRotBoundRect( aPoly.GetBoundRect() );
                aPt = aRotBoundRect.TopLeft();
                aSz = aRotBoundRect.GetSize();
            }

            aGraphic.Draw(rOut, aPt, aSz);
        }

        bRet = true;
    }

    return bRet;
}

void lclImplAdjust( BitmapEx& rBmpEx, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags )
{
    GraphicAttr aAttr( rAttr );

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() )
    {
        switch( aAttr.GetDrawMode() )
        {
            case GraphicDrawMode::Mono:
                rBmpEx.Convert( BmpConversion::N1BitThreshold );
            break;

            case GraphicDrawMode::Greys:
                rBmpEx.Convert( BmpConversion::N8BitGreys );
            break;

            case GraphicDrawMode::Watermark:
            {
                aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
                aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
            }
            break;

            default:
            break;
        }
    }

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() )
    {
        rBmpEx.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
                       aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
                       aAttr.GetGamma(), aAttr.IsInvert() );
    }

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() )
    {
        rBmpEx.Mirror( aAttr.GetMirrorFlags() );
    }

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() )
    {
        rBmpEx.Rotate( aAttr.GetRotation(), COL_TRANSPARENT );
    }

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() )
    {
        rBmpEx.AdjustTransparency(255 - aAttr.GetAlpha());
    }
}

void lclImplAdjust( GDIMetaFile& rMtf, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags )
{
    GraphicAttr aAttr( rAttr );

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() )
    {
        switch( aAttr.GetDrawMode() )
        {
            case GraphicDrawMode::Mono:
                rMtf.Convert( MtfConversion::N1BitThreshold );
            break;

            case GraphicDrawMode::Greys:
                rMtf.Convert( MtfConversion::N8BitGreys );
            break;

            case GraphicDrawMode::Watermark:
            {
                aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
                aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
            }
            break;

            default:
            break;
        }
    }

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() )
    {
        rMtf.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
                     aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
                     aAttr.GetGamma(), aAttr.IsInvert() );
    }

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() )
    {
        rMtf.Mirror( aAttr.GetMirrorFlags() );
    }

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() )
    {
        rMtf.Rotate( aAttr.GetRotation() );
    }

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() )
    {
        OSL_FAIL( "Missing implementation: Mtf-Transparency" );
    }
}

void lclImplAdjust( Animation& rAnimation, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags )
{
    GraphicAttr aAttr( rAttr );

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() )
    {
        switch( aAttr.GetDrawMode() )
        {
            case GraphicDrawMode::Mono:
                rAnimation.Convert( BmpConversion::N1BitThreshold );
            break;

            case GraphicDrawMode::Greys:
                rAnimation.Convert( BmpConversion::N8BitGreys );
            break;

            case GraphicDrawMode::Watermark:
            {
                aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
                aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
            }
            break;

            default:
            break;
        }
    }

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() )
    {
        rAnimation.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
                           aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
                           aAttr.GetGamma(), aAttr.IsInvert() );
    }

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() )
    {
        rAnimation.Mirror( aAttr.GetMirrorFlags() );
    }

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() )
    {
        OSL_FAIL( "Missing implementation: Animation-Rotation" );
    }

    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() )
    {
        OSL_FAIL( "Missing implementation: Animation-Transparency" );
    }
}

// end anonymous namespace

struct GrfSimpleCacheObj
{
    Graphic     maGraphic;
    GraphicAttr maAttr;

                GrfSimpleCacheObj( Graphic aGraphic, const GraphicAttr& rAttr ) :
                    maGraphic(std::move( aGraphic )), maAttr( rAttr ) {}
};

GraphicObject::GraphicObject()
{
}

GraphicObject::GraphicObject(Graphic aGraphic)
    : maGraphic(std::move(aGraphic))
{
}

GraphicObject::GraphicObject(const GraphicObject& rGraphicObj)
    : maGraphic(rGraphicObj.GetGraphic())
    , maAttr(rGraphicObj.maAttr)
    , maUserData(rGraphicObj.maUserData)
{
}

GraphicObject::~GraphicObject()
{
}

GraphicType GraphicObject::GetType() const
{
    return maGraphic.GetType();
}

Size GraphicObject::GetPrefSize() const
{
    return maGraphic.GetPrefSize();
}

MapMode GraphicObject::GetPrefMapMode() const
{
    return maGraphic.GetPrefMapMode();
}

bool GraphicObject::IsTransparent() const
{
    return maGraphic.IsTransparent();
}

bool GraphicObject::IsAnimated() const
{
    return maGraphic.IsAnimated();
}

bool GraphicObject::IsEPS() const
{
    return maGraphic.IsEPS();
}

bool GraphicObject::ImplGetCropParams(const OutputDevice& rOut, Point& rPt, Size& rSz, const GraphicAttr* pAttr,
                                      tools::PolyPolygon& rClipPolyPoly, bool& bRectClipRegion) const
{
    bool bRet = false;

    if( GetType() != GraphicType::NONE )
    {
        tools::Polygon aClipPoly( tools::Rectangle( rPt, rSz ) );
        const Degree10  nRot10 = pAttr->GetRotation() % 3600_deg10;
        const Point     aOldOrigin( rPt );
        const MapMode   aMap100( MapUnit::Map100thMM );
        Size            aSize100;
        tools::Long            nTotalWidth, nTotalHeight;

        if( nRot10 )
        {
            aClipPoly.Rotate( rPt, nRot10 );
            bRectClipRegion = false;
        }
        else
            bRectClipRegion = true;

        rClipPolyPoly = tools::PolyPolygon(aClipPoly);

        if (maGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
            aSize100 = Application::GetDefaultDevice()->PixelToLogic( maGraphic.GetPrefSize(), aMap100 );
        else
        {
            MapMode m(maGraphic.GetPrefMapMode());
            aSize100 = rOut.LogicToLogic( maGraphic.GetPrefSize(), &m, &aMap100 );
        }

        nTotalWidth = aSize100.Width() - pAttr->GetLeftCrop() - pAttr->GetRightCrop();
        nTotalHeight = aSize100.Height() - pAttr->GetTopCrop() - pAttr->GetBottomCrop();

        if( !aSize100.IsEmpty() && nTotalWidth > 0 && nTotalHeight > 0 )
        {
            double fScale = static_cast<double>(aSize100.Width()) / nTotalWidth;
            const tools::Long nNewLeft = basegfx::fround<tools::Long>( ( ( pAttr->GetMirrorFlags() & ;BmpMirrorFlags::Horizontal ) ? pAttr->GetRightCrop() : pAttr->GetLeftCrop() ) * -fScale );
            const tools::Long nNewRight = nNewLeft + basegfx::fround<tools::Long>( aSize100.Width() * fScale ) - 1;

            fScale = static_cast<double>(rSz.Width()) / aSize100.Width();
            rPt.AdjustX(basegfx::fround<tools::Long>(nNewLeft * fScale));
            rSz.setWidth(basegfx::fround<tools::Long>((nNewRight - nNewLeft + 1) * fScale));

            fScale = static_cast<double>(aSize100.Height()) / nTotalHeight;
            const tools::Long nNewTop = basegfx::fround<tools::Long>( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Vertical ) ? pAttr->GetBottomCrop() : pAttr->GetTopCrop() ) * -fScale );
            const tools::Long nNewBottom = nNewTop + basegfx::fround<tools::Long>( aSize100.Height() * fScale ) - 1;

            fScale = static_cast<double>(rSz.Height()) / aSize100.Height();
            rPt.AdjustY(basegfx::fround<tools::Long>(nNewTop * fScale));
            rSz.setHeight(basegfx::fround<tools::Long>((nNewBottom - nNewTop + 1) * fScale));

            if( nRot10 )
            {
                tools::Polygon aOriginPoly( 1 );

                aOriginPoly[ 0 ] = rPt;
                aOriginPoly.Rotate( aOldOrigin, nRot10 );
                rPt = aOriginPoly[ 0 ];
            }

            bRet = true;
        }
    }

    return bRet;
}

GraphicObject& GraphicObject::operator=( const GraphicObject& rGraphicObj )
{
    if( &rGraphicObj != this )
    {
        mxSimpleCache.reset();
        maGraphic = rGraphicObj.GetGraphic();
        maAttr = rGraphicObj.maAttr;
        maUserData = rGraphicObj.maUserData;
    }

    return *this;
}

bool GraphicObject::operator==( const GraphicObject& rGraphicObj ) const
{
    return rGraphicObj.maGraphic == maGraphic
        && rGraphicObj.maAttr == maAttr;
}

OString GraphicObject::GetUniqueID() const
{
    return GetGraphic().getUniqueID();
}

void GraphicObject::SetAttr( const GraphicAttr& rAttr )
{
    maAttr = rAttr;

    if (mxSimpleCache && (mxSimpleCache->maAttr != rAttr))
        mxSimpleCache.reset();
}

void GraphicObject::SetUserData()
{
    maUserData.clear();
}

void GraphicObject::SetUserData( const OUString& rUserData )
{
    maUserData = rUserData;
}

bool GraphicObject::Draw(OutputDevice& rOut, const Point& rPt, const Size& rSz,
                         const GraphicAttr* pAttr) const
{
    GraphicAttr         aAttr( pAttr ? *pAttr : GetAttr() );
    Point               aPt( rPt );
    Size                aSz( rSz );
    const DrawModeFlags nOldDrawMode = rOut.GetDrawMode();
    bool                bCropped = aAttr.IsCropped();
    bool bRet;

    rOut.SetDrawMode(nOldDrawMode & ~DrawModeFlags( DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient ));

    // mirrored horizontally
    if( aSz.Width() < 0 )
    {
        aPt.AdjustX(aSz.Width() + 1 );
        aSz.setWidth( -aSz.Width() );
        aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BmpMirrorFlags::Horizontal );
    }

    // mirrored vertically
    if( aSz.Height() < 0 )
    {
        aPt.AdjustY(aSz.Height() + 1 );
        aSz.setHeight( -aSz.Height() );
        aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BmpMirrorFlags::Vertical );
    }

    if( bCropped )
    {
        tools::PolyPolygon aClipPolyPoly;
        bool        bRectClip;
        const bool  bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip);

        rOut.Push(vcl::PushFlags::CLIPREGION);

        if( bCrop )
        {
            if( bRectClip )
            {
                // #i29534# Store crop rect for later forwarding to
                // PDF writer
                tools::Rectangle aCropRect = aClipPolyPoly.GetBoundRect();
                rOut.IntersectClipRegion(aCropRect);
            }
            else
            {
                rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly));
            }
        }
    }

    bRet = lclDrawObj(rOut, aPt, aSz, *this, aAttr);

    if( bCropped )
        rOut.Pop();

    rOut.SetDrawMode( nOldDrawMode );

    return bRet;
}

void GraphicObject::DrawTiled(OutputDevice& rOut, const tools::Rectangle& rArea, const Size& rSize,
                              const Size& rOffset, int nTileCacheSize1D)
{
    if (rSize.IsEmpty())
        return;

    const MapMode   aOutMapMode(rOut.GetMapMode());
    // #106258# Clamp size to 1 for zero values. This is okay, since
    // logical size of zero is handled above already
    const Size      aOutTileSize( ::std::max( tools::Long(1), rOut.LogicToPixel( rSize, aOutMapMode ).Width() ),
                                  ::std::max( tools::Long(1), rOut.LogicToPixel( rSize, aOutMapMode ).Height() ) );

    //#i69780 clip final tile size to a sane max size
    while ((static_cast<sal_Int64>(rSize.Width()) * nTileCacheSize1D) > SAL_MAX_UINT16)
        nTileCacheSize1D /= 2;
    while ((static_cast<sal_Int64>(rSize.Height()) * nTileCacheSize1D) > SAL_MAX_UINT16)
        nTileCacheSize1D /= 2;

    ImplDrawTiled(rOut, rArea, aOutTileSize, rOffset, nullptr, nTileCacheSize1D);
}

bool GraphicObject::StartAnimation(OutputDevice& rOut, const Point& rPt, const Size& rSz,
                                   tools::Long nRendererId,
                                   OutputDevice* pFirstFrameOutDev)
{
    bool bRet = false;

    GetGraphic();

    const GraphicAttr aAttr( GetAttr() );

    if (IsAnimated())
    {
        Point   aPt( rPt );
        Size    aSz( rSz );
        bool    bCropped = aAttr.IsCropped();

        if( bCropped )
        {
            tools::PolyPolygon aClipPolyPoly;
            bool        bRectClip;
            const bool  bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip);

            rOut.Push(vcl::PushFlags::CLIPREGION);

            if( bCrop )
            {
                if( bRectClip )
                    rOut.IntersectClipRegion(aClipPolyPoly.GetBoundRect());
                else
                    rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly));
            }
        }

        if (!mxSimpleCache || (mxSimpleCache->maAttr != aAttr) || pFirstFrameOutDev)
        {
            mxSimpleCache.reset(new GrfSimpleCacheObj(GetTransformedGraphic(&aAttr), aAttr));
            mxSimpleCache->maGraphic.SetAnimationNotifyHdl(GetGraphic().GetAnimationNotifyHdl());
        }

        mxSimpleCache->maGraphic.StartAnimation(rOut, aPt, aSz, nRendererId, pFirstFrameOutDev);

        if( bCropped )
            rOut.Pop();

        bRet = true;
    }
    else
        bRet = Draw(rOut, rPt, rSz, &aAttr);

    return bRet;
}

void GraphicObject::StopAnimation( const OutputDevice* pOut, tools::Long nRendererId )
{
    if (mxSimpleCache)
        mxSimpleCache->maGraphic.StopAnimation(pOut, nRendererId);
}

const Graphic& GraphicObject::GetGraphic() const
{
    return maGraphic;
}

void GraphicObject::SetGraphic( const Graphic& rGraphic)
{
    maGraphic = rGraphic;
}

Graphic GraphicObject::GetTransformedGraphic( const Size& rDestSize, const MapMode&&nbsp;rDestMap, const GraphicAttr& rAttr ) const
{
    // #104550# Extracted from svx/source/svdraw/svdograf.cxx
    Graphic             aTransGraphic( GetGraphic() );
    const GraphicType   eType = GetType();
    const Size          aSrcSize( aTransGraphic.GetPrefSize() );

    // #104115# Convert the crop margins to graphic object mapmode
    const MapMode aMapGraph( aTransGraphic.GetPrefMapMode() );
    const MapMode aMap100( MapUnit::Map100thMM );

    Size aCropLeftTop;
    Size aCropRightBottom;

    if( GraphicType::GdiMetafile == eType )
    {
        GDIMetaFile aMtf( aTransGraphic.GetGDIMetaFile() );

        if (aMapGraph.GetMapUnit() == MapUnit::MapPixel)
        {
            // crops are in 1/100th mm -> to aMapGraph -> to MapUnit::MapPixel
            aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
                Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
                aMap100);
            aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
                Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
                aMap100);
        }
        else
        {
            // crops are in GraphicObject units -> to aMapGraph
            aCropLeftTop = OutputDevice::LogicToLogic(
                Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
                aMap100,
                aMapGraph);
            aCropRightBottom = OutputDevice::LogicToLogic(
                Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
                aMap100,
                aMapGraph);
        }

        // #104115# If the metafile is cropped, give it a special
        // treatment: clip against the remaining area, scale up such
        // that this area later fills the desired size, and move the
        // origin to the upper left edge of that area.
        if( rAttr.IsCropped() )
        {
            const MapMode aMtfMapMode( aMtf.GetPrefMapMode() );

            tools::Rectangle aClipRect( aMtfMapMode.GetOrigin().X() + aCropLeftTop.Width(),
                                 aMtfMapMode.GetOrigin().Y() + aCropLeftTop.Height(),
                                 aMtfMapMode.GetOrigin().X() + aSrcSize.Width() - aCropRightBottom.Width(),
                                 aMtfMapMode.GetOrigin().Y() + aSrcSize.Height() - aCropRightBottom.Height() );

            // #104115# To correctly crop rotated metafiles, clip by view rectangle
            aMtf.AddAction( new MetaISectRectClipRegionAction( aClipRect ), 0 );

            // #104115# To crop the metafile, scale larger than the output rectangle
            aMtf.Scale( static_cast<double>(rDestSize.Width()) / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()),
                        static_cast<double>(rDestSize.Height()) / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) );

            // #104115# Adapt the pref size by hand (scale changes it
            // proportionally, but we want it to be smaller than the
            // former size, to crop the excess out)
            aMtf.SetPrefSize( Size( static_cast<tools::Long>(static_cast<double>(rDestSize.Width()) *  (1.0 + (aCropLeftTop.Width() + aCropRightBottom.Width()) / aSrcSize.Width())  + .5),
                                    static_cast<tools::Long>(static_cast<double>(rDestSize.Height()) * (1.0 + (aCropLeftTop.Height() + aCropRightBottom.Height()) / aSrcSize.Height()) + .5) ) );

            // #104115# Adapt the origin of the new mapmode, such that it
            // is shifted to the place where the cropped output starts
            Point aNewOrigin( static_cast<tools::Long>(static_cast<double>(aMtfMapMode.GetOrigin().X()) + rDestSize.Width() * aCropLeftTop.Width() / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()) + .5),
                              static_cast<tools::Long>(static_cast<double>(aMtfMapMode.GetOrigin().Y()) + rDestSize.Height() * aCropLeftTop.Height() / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) + .5) );
            MapMode aNewMap( rDestMap );
            aNewMap.SetOrigin( OutputDevice::LogicToLogic(aNewOrigin, aMtfMapMode, rDestMap) );
            aMtf.SetPrefMapMode( aNewMap );
        }
        else
        {
            aMtf.Scale( Fraction( rDestSize.Width(), aSrcSize.Width() ), Fraction( rDestSize.Height(), aSrcSize.Height() ) );
            aMtf.SetPrefMapMode( rDestMap );
        }

        aTransGraphic = aMtf;
    }
    else if( GraphicType::Bitmap == eType )
    {
        BitmapEx aBitmapEx( aTransGraphic.GetBitmapEx() );
        tools::Rectangle aCropRect;

        // convert crops to pixel
        if(rAttr.IsCropped())
        {
            if (aMapGraph.GetMapUnit() == MapUnit::MapPixel)
            {
                // crops are in 1/100th mm -> to MapUnit::MapPixel
                aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
                    Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
                    aMap100);
                aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
                    Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
                    aMap100);
            }
            else
            {
                // crops are in GraphicObject units -> to MapUnit::MapPixel
                aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
                    Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
                    aMapGraph);
                aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
                    Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
                    aMapGraph);
            }

            // convert from prefmapmode to pixel
            Size aSrcSizePixel(
                Application::GetDefaultDevice()->LogicToPixel(
                    aSrcSize,
                    aMapGraph));

            if(rAttr.IsCropped()
                && (aSrcSizePixel.Width() != aBitmapEx.GetSizePixel().Width() || aSrcSizePixel.Height() != aBitmapEx.GetSizePixel().Height())
                && aSrcSizePixel.Width())
            {
                // the size in pixels calculated from Graphic's internal MapMode (aTransGraphic.GetPrefMapMode())
                // and its internal size (aTransGraphic.GetPrefSize()) is different from its real pixel size.
                // This can be interpreted as this values to be set wrong, but needs to be corrected since e.g.
                // existing cropping is calculated based on this logic values already.
                // aBitmapEx.Scale(aSrcSizePixel);

                // another possibility is to adapt the values created so far with a factor; this
                // will keep the original Bitmap untouched and thus quality will not change
                // caution: convert to double first, else pretty big errors may occur
                const double fFactorX(static_cast<double>(aBitmapEx.GetSizePixel().Width()) / aSrcSizePixel.Width());
                const double fFactorY(static_cast<double>(aBitmapEx.GetSizePixel().Height()) / aSrcSizePixel.Height());

                aCropLeftTop.setWidth( basegfx::fround<tools::Long>(aCropLeftTop.Width() * fFactorX) );
                aCropLeftTop.setHeight( basegfx::fround<tools::Long>(aCropLeftTop.Height() * fFactorY) );
                aCropRightBottom.setWidth( basegfx::fround<tools::Long>(aCropRightBottom.Width() * fFactorX) );
                aCropRightBottom.setHeight( basegfx::fround<tools::Long>(aCropRightBottom.Height() * fFactorY) );

                aSrcSizePixel = aBitmapEx.GetSizePixel();
            }

            // setup crop rectangle in pixel
            aCropRect = tools::Rectangle( aCropLeftTop.Width(), aCropLeftTop.Height(),
                                 aSrcSizePixel.Width() - aCropRightBottom.Width(),
                                 aSrcSizePixel.Height() - aCropRightBottom.Height() );
        }

        // #105641# Also crop animations
        if( aTransGraphic.IsAnimated() )
        {
            Animation aAnim( aTransGraphic.GetAnimation() );

            for( size_t nFrame=0; nFrame<aAnim.Count(); ++nFrame )
            {
                AnimationFrame aAnimationFrame( aAnim.Get( nFrame ) );

                if( !aCropRect.Contains( tools::Rectangle(aAnimationFrame.maPositionPixel, aAnimationFrame.maSizePixel) ) )
                {
                    // setup actual cropping (relative to frame position)
                    tools::Rectangle aCropRectRel( aCropRect );
                    aCropRectRel.Move( -aAnimationFrame.maPositionPixel.X(),
                                       -aAnimationFrame.maPositionPixel.Y() );

                    // cropping affects this frame, apply it then
                    // do _not_ apply enlargement, this is done below
                    ImplTransformBitmap( aAnimationFrame.maBitmapEx, rAttr, Size(), Size(),
                                         aCropRectRel, rDestSize, false );

                    aAnim.Replace( aAnimationFrame, nFrame );
                }
                // else: bitmap completely within crop area,
                // i.e. nothing is cropped away
            }

            // now, apply enlargement (if any) through global animation size
            if( aCropLeftTop.Width() < 0 ||
                aCropLeftTop.Height() < 0 ||
                aCropRightBottom.Width() < 0 ||
                aCropRightBottom.Height() < 0 )
            {
                Size aNewSize( aAnim.GetDisplaySizePixel() );
                aNewSize.AdjustWidth(aCropRightBottom.Width() < 0 ? -aCropRightBottom.Width() : 0 );
                aNewSize.AdjustWidth(aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0 );
                aNewSize.AdjustHeight(aCropRightBottom.Height() < 0 ? -aCropRightBottom.Height() : 0 );
                aNewSize.AdjustHeight(aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 );
                aAnim.SetDisplaySizePixel( aNewSize );
            }

            // if topleft has changed, we must move all frames to the
            // right and bottom, resp.
            if( aCropLeftTop.Width() < 0 ||
                aCropLeftTop.Height() < 0 )
            {
                Point aPosOffset( aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0,
                                  aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 );

                for( size_t nFrame=0; nFrame<aAnim.Count(); ++nFrame )
                {
                    AnimationFrame aAnimationFrame( aAnim.Get( nFrame ) );

                    aAnimationFrame.maPositionPixel += aPosOffset;

                    aAnim.Replace( aAnimationFrame, nFrame );
                }
            }

            aTransGraphic = aAnim;
        }
        else
        {
            ImplTransformBitmap( aBitmapEx, rAttr, aCropLeftTop, aCropRightBottom,
                                 aCropRect, rDestSize, true );

            aTransGraphic = aBitmapEx;
        }

        aTransGraphic.SetPrefSize( rDestSize );
        aTransGraphic.SetPrefMapMode( rDestMap );
    }

    GraphicObject aGrfObj( aTransGraphic );
    aTransGraphic = aGrfObj.GetTransformedGraphic( &rAttr );

    return aTransGraphic;
}

Graphic GraphicObject::GetTransformedGraphic( const GraphicAttr* pAttr ) const
{
    GetGraphic();

    Graphic     aGraphic;
    GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );

    if (maGraphic.IsSupportedGraphic())
    {
        if( aAttr.IsSpecialDrawMode() || aAttr.IsAdjusted() || aAttr.IsMirrored() || aAttr.IsRotated() || aAttr.IsTransparent() )
        {
            if( GetType() == GraphicType::Bitmap )
            {
                if( IsAnimated() )
                {
                    Animation aAnimation( maGraphic.GetAnimation() );
                    lclImplAdjust( aAnimation, aAttr, GraphicAdjustmentFlags::ALL );
                    aAnimation.SetLoopCount(maGraphic.GetAnimationLoopCount());
                    aGraphic = aAnimation;
                }
                else
                {
                    BitmapEx aBmpEx( maGraphic.GetBitmapEx() );
                    lclImplAdjust( aBmpEx, aAttr, GraphicAdjustmentFlags::ALL );
                    aGraphic = aBmpEx;
                }
            }
            else
            {
                GDIMetaFile aMtf( maGraphic.GetGDIMetaFile() );
                lclImplAdjust( aMtf, aAttr, GraphicAdjustmentFlags::ALL );
                aGraphic = aMtf;
            }
        }
        else
        {
            if( ( GetType() == GraphicType::Bitmap ) && IsAnimated() )
            {
                Animation aAnimation( maGraphic.GetAnimation() );
                aAnimation.SetLoopCount(maGraphic.GetAnimationLoopCount());
                aGraphic = aAnimation;
            }
            else
                aGraphic = maGraphic;
        }
    }

    return aGraphic;
}

bool GraphicObject::isGraphicObjectUniqueIdURL(std::u16string_view rURL)
{
    return o3tl::starts_with(rURL, u"vnd.sun.star.GraphicObject:");
}

// calculate scalings between real image size and logic object size. This
// is necessary since the crop values are relative to original bitmap size
basegfx::B2DVector GraphicObject::calculateCropScaling(
    double fWidth,
    double fHeight,
    double fLeftCrop,
    double fTopCrop,
    double fRightCrop,
    double fBottomCrop) const
{
    const MapMode aMapMode100thmm(MapUnit::Map100thMM);
    Size aBitmapSize(GetPrefSize());
    double fFactorX(1.0);
    double fFactorY(1.0);

    if(MapUnit::MapPixel == GetPrefMapMode().GetMapUnit())
    {
        aBitmapSize = Application::GetDefaultDevice()->PixelToLogic(aBitmapSize, aMapMode100thmm);
    }
    else
    {
        aBitmapSize = OutputDevice::LogicToLogic(aBitmapSize, GetPrefMapMode(), aMapMode100thmm);
    }

    const double fDivX(aBitmapSize.Width() - fLeftCrop - fRightCrop);
    const double fDivY(aBitmapSize.Height() - fTopCrop - fBottomCrop);

    if(!basegfx::fTools::equalZero(fDivX))
    {
        fFactorX = fabs(fWidth) / fDivX;
    }

    if(!basegfx::fTools::equalZero(fDivY))
    {
        fFactorY = fabs(fHeight) / fDivY;
    }

    return basegfx::B2DVector(fFactorX,fFactorY);
}


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

Messung V0.5
C=95 H=98 G=96

¤ Dauer der Verarbeitung: 0.10 Sekunden  ¤

*© 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.