/* -*- 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 .
*/
// force alpha part of color to // opaque. transparent painting is done // explicitly via MetaActionType::Transparent
aColor.SetAlpha(255); //aColor.SetTransparency(128);
/** Create masked BitmapEx, where the white areas of rBitmap are transparent, and the other appear in rMaskColor.
*/
BitmapEx createMaskBmpEx( const Bitmap& rBitmap, const ::Color& rMaskColor )
{ const ::Color aWhite( COL_WHITE );
BitmapPalette aBiLevelPalette{
aWhite, rMaskColor
};
void VectorOfOutDevStates::popState()
{ if( getState().pushFlags != vcl::PushFlags::ALL )
{ // a state is pushed which is incomplete, i.e. does not // restore everything to the previous stack level when // popped. // That means, we take the old state, and restore every // OutDevState member whose flag is set, from the new to the // old state. Then the new state gets overwritten by the // calculated state
// preset to-be-calculated new state with old state
OutDevState aCalculatedNewState( getState() );
// selectively copy to-be-restored content over saved old // state
m_aStates.pop_back();
// decide, whether this gradient can be rendered natively // by the canvas, or must be emulated via VCL gradient // action extraction. const sal_uInt16 nSteps( rGradient.GetSteps() );
if( // step count is infinite, can use native canvas // gradients here
nSteps == 0 || // step count is sufficiently high, such that no // discernible difference should be visible.
nSteps > 64 )
{
uno::Reference< lang::XMultiServiceFactory> xFactory(
rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
basegfx::ODFGradientInfo aGradInfo;
OUString aGradientService; switch( rGradient.GetStyle() )
{ case css::awt::GradientStyle_LINEAR:
aGradInfo = basegfx::utils::createLinearODFGradientInfo(
aBounds,
nSteps,
fBorder,
fRotation); // map ODF to svg gradient orientation - x // instead of y direction
aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aRot90);
aGradientService = "LinearGradient"; break;
case css::awt::GradientStyle_AXIAL:
{ // Adapt the border so that it is suitable // for the axial gradient. An axial // gradient consists of two linear // gradients. Each of those covers half // of the total size. In order to // compensate for the condensed display of // the linear gradients, we have to // enlarge the area taken up by the actual // gradient (1-fBorder). After that we // have to turn the result back into a // border value, hence the second (left // most 1-... constdouble fAxialBorder (1-2*(1-fBorder));
aGradInfo = basegfx::utils::createAxialODFGradientInfo(
aBounds,
nSteps,
fAxialBorder,
fRotation); // map ODF to svg gradient orientation - x // instead of y direction
aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aRot90);
// map ODF axial gradient to 3-stop linear // gradient - shift left by 0.5
basegfx::B2DHomMatrix aShift;
// cannot currently use native canvas gradients, as a // finite step size is given (this funny feature is not // supported by the XCanvas API)
rParms.mrStates.pushState(vcl::PushFlags::ALL);
if( !bIsPolygonRectangle )
{ // only clip, if given polygon is not a rectangle in // the first place (the gradient is always limited to // the given bound rect)
updateClipping(
aDevicePoly,
rParms, true );
}
// setup state-local text transformation, // if the font be rotated constauto nFontAngle( rFont.GetOrientation() ); if( nFontAngle )
{ // set to unity transform rotated by font angle constdouble nAngle( toRadians(nFontAngle) );
o_rFontRotation = -nAngle;
} else
{
o_rFontRotation = 0.0;
}
// TODO(F2): use correct scale direction, font // height might be width or anything else
// TODO(Q3): This code smells of programming by // coincidence (the next two if statements)
::Size rFontSizeLog( rFont.GetFontSize() );
if (rFontSizeLog.Height() == 0)
{ // guess 16 pixel (as in VCL)
rFontSizeLog = ::Size(0, 16);
// convert to target MapUnit if not pixels
rFontSizeLog = OutputDevice::LogicToLogic(rFontSizeLog, MapMode(MapUnit::MapPixel), rParms.mrVDev.GetMapMode());
}
// #i52608# apply map mode scale also to font matrix - an // anisotrophic mapmode must be reflected in an // anisotrophic font matrix scale. const OutDevState& rState( rParms.mrStates.getState() ); if( !::basegfx::fTools::equal(
rState.mapModeTransform.get(0,0),
rState.mapModeTransform.get(1,1)) )
{ constdouble nScaleX( rState.mapModeTransform.get(0,0) ); constdouble nScaleY( rState.mapModeTransform.get(1,1) );
// note: no reason to check for division by zero, we // always have the value closer (or equal) to zero as // the nominator. if( fabs(nScaleX) < fabs(nScaleY) )
aFontMatrix.m00 *= nScaleX / nScaleY; else
aFontMatrix.m11 *= nScaleY / nScaleX;
}
aFontRequest.CellSize = (rState.mapModeTransform * vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getHeight();
// we don't have an automatic color, so black is always // drawn on white (literally copied from // vcl/source/gdi/outdev3.cxx) if( aTextColor == COL_BLACK )
{
aTextColor = COL_WHITE;
rParms.mrStates.getState().textColor =
vcl::unotools::colorToDoubleSequence(
aTextColor, xColorSpace );
}
ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect, "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
if( !bIntersect ||
(bEmptyClipRect && bEmptyClipPoly) )
{
rState.clip = rClipPoly;
} else
{ if( !bEmptyClipRect )
{ // TODO(P3): Use Liang-Barsky polygon clip here, // after all, one object is just a rectangle!
// convert rect to polygon beforehand, must revert // to general polygon clipping here.
::tools::Rectangle aRect = rState.clipRect; // #121100# VCL rectangular clips always // include one more pixel to the right // and the bottom
aRect.AdjustRight(1);
aRect.AdjustBottom(1);
rState.clip = ::basegfx::B2DPolyPolygon(
::basegfx::utils::createPolygonFromRect(
vcl::unotools::b2DRectangleFromRectangle(aRect) ) );
}
ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect, "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
if( !bIntersect ||
(bEmptyClipRect && bEmptyClipPoly) )
{
rState.clipRect = rClipRect;
rState.clip.clear();
} elseif( bEmptyClipPoly )
{
rState.clipRect.Intersection( rClipRect );
rState.clip.clear();
} else
{ // TODO(P3): Handle a fourth case here, when all clip // polygons are rectangular, once B2DMultiRange's // sweep line implementation is done.
// general case: convert to polygon and clip
// convert rect to polygon beforehand, must revert // to general polygon clipping here.
::basegfx::B2DPolyPolygon aClipPoly(
::basegfx::utils::createPolygonFromRect(
vcl::unotools::b2DRectangleFromRectangle(rClipRect) ) );
- think about mapping. _If_ we do everything in logical coordinates (which would solve the probs for stroke widths and text offsets), then we would have to recalc scaling for every drawing operation. This is because the outdev map mode might change at any time. Also keep in mind, that, although we've double precision float arithmetic now, different offsets might still generate different roundings (aka 'OutputDevice::SetPixelOffset())
*/
// alias common parameters
VectorOfOutDevStates& rStates(rFactoryParms.mrStates); const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas);
::VirtualDevice& rVDev(rFactoryParms.mrVDev); const Parameters& rParms(rFactoryParms.mrParms);
sal_Int32& io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex);
// Loop over every metaaction // ==========================
MetaAction* pCurrAct;
// TODO(P1): think about caching for( pCurrAct=rMtf.FirstAction();
pCurrAct;
pCurrAct = rMtf.NextAction() )
{ // execute every action, to keep VDev state up-to-date // currently used only for // - the map mode // - the line/fill color when processing a MetaActionType::Transparent // - SetFont to process font metric specific actions
pCurrAct->Execute( &rVDev );
// In the first part of this monster-switch, we // handle all state-changing meta actions. These // are all handled locally.
case MetaActionType::PUSH:
{
MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct);
rStates.pushState(pPushAction->GetFlags());
} break;
case MetaActionType::POP:
rStates.popState(); break;
case MetaActionType::TEXTLANGUAGE: case MetaActionType::REFPOINT: // handled via pCurrAct->Execute( &rVDev ) break;
case MetaActionType::MAPMODE: // modify current mapModeTransformation // transformation, such that subsequent // coordinates map correctly
tools::calcLogic2PixelAffineTransform( rStates.getState().mapModeTransform,
rVDev ); break;
// monitor clip regions, to assemble clip polygon on our own case MetaActionType::CLIPREGION:
{
MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct);
// intersect current clip with given rect
updateClipping(
aClipRect,
rFactoryParms, false );
} else
{ // set new clip polygon (don't intersect // with old one, just set it)
case MetaActionType::MOVECLIPREGION: // TODO(F2): NYI break;
case MetaActionType::LINECOLOR: if( !rParms.maLineColor )
{
setStateColor( static_cast<MetaLineColorAction*>(pCurrAct),
rStates.getState().isLineColorSet,
rStates.getState().lineColor,
rCanvas );
} else
{ // #120994# Do switch on/off LineColor, even when an overriding one is set bool bSetting(static_cast<MetaLineColorAction*>(pCurrAct)->IsSetting());
case MetaActionType::FILLCOLOR: if( !rParms.maFillColor )
{
setStateColor( static_cast<MetaFillColorAction*>(pCurrAct),
rStates.getState().isFillColorSet,
rStates.getState().fillColor,
rCanvas );
} else
{ // #120994# Do switch on/off FillColor, even when an overriding one is set bool bSetting(static_cast<MetaFillColorAction*>(pCurrAct)->IsSetting());
case MetaActionType::TEXTCOLOR:
{ if( !rParms.maTextColor )
{ // Text color is set unconditionally, thus, no // use of setStateColor here
::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() );
// force alpha part of color to // opaque. transparent painting is done // explicitly via MetaActionType::Transparent
aColor.SetAlpha(255);
case MetaActionType::TEXTFILLCOLOR: if( !rParms.maTextColor )
{
setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct),
rStates.getState().isTextFillColorSet,
rStates.getState().textFillColor,
rCanvas );
} else
{ // #120994# Do switch on/off TextFillColor, even when an overriding one is set bool bSetting(static_cast<MetaTextFillColorAction*>(pCurrAct)->IsSetting());
case MetaActionType::TEXTLINECOLOR: if( !rParms.maTextColor )
{
setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct),
rStates.getState().isTextLineColorSet,
rStates.getState().textLineColor,
rCanvas );
} else
{ // #120994# Do switch on/off TextLineColor, even when an overriding one is set bool bSetting(static_cast<MetaTextLineColorAction*>(pCurrAct)->IsSetting());