/* -*- 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 .
*/
namespace
{
sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA, const basegfx::BColor& rColorB, double fDelta, double fDiscreteUnit)
{ // use color distance, assume to do every color step
sal_uInt32 nSteps(basegfx::fround(rColorA.getDistance(rColorB) * 255.0));
if (nSteps)
{ // calc discrete length to change color each discrete unit (pixel) const sal_uInt32 nDistSteps(basegfx::fround(fDelta / fDiscreteUnit));
nSteps = std::min(nSteps, nDistSteps);
}
// reduce quality to 3 discrete units or every 3rd color step for rendering
nSteps /= 2;
// roughly cut when too big or too small (not full quality, reduce complexity)
nSteps = std::min(nSteps, sal_uInt32(255));
nSteps = std::max(nSteps, sal_uInt32(1));
return nSteps;
}
}
namespace
{ /** helper to convert a MapMode to a transformation */
basegfx::B2DHomMatrix getTransformFromMapMode(const MapMode& rMapMode)
{
basegfx::B2DHomMatrix aMapping; const Fraction aNoScale(1, 1); const Point& rOrigin(rMapMode.GetOrigin());
namespace drawinglayer::processor2d
{ // rendering support
// directdraw of text simple portion or decorated portion primitive. When decorated, all the extra // information is translated to VCL parameters and set at the font. // Acceptance is restricted to no shearing and positive scaling in X and Y (no font mirroring // for VCL) void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate)
{ // decompose matrix to have position and size of text
basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation
* rTextCandidate.getTextTransform());
basegfx::B2DVector aFontScaling, aTranslate; double fRotate, fShearX;
aLocalTransform.decompose(aFontScaling, aTranslate, fRotate, fShearX);
bool bPrimitiveAccepted(false);
// tdf#95581: Assume tiny shears are rounding artefacts or whatever and can be ignored, // especially if the effect is less than a pixel. if (std::abs(aFontScaling.getY() * fShearX) < 1)
{ if (aFontScaling.getX() < 0.0 && aFontScaling.getY() < 0.0)
{ // handle special case: If scale is negative in (x,y) (3rd quadrant), it can // be expressed as rotation by PI. Use this since the Font rendering will not // apply the negative scales in any form
aFontScaling = basegfx::absolute(aFontScaling);
fRotate += M_PI;
}
// tdf#153092 Ideally we don't have to scale the font and dxarray, but we might have // to nevertheless if dealing with non integer sizes constbool bScaleFont(aFontSize.getY() != std::round(aFontSize.getY())
|| comphelper::LibreOfficeKit::isActive());
vcl::Font aFont;
// Get the VCL font if (!bScaleFont)
{
aFont = primitive2d::getVclFontFromFontAttribute(
rTextCandidate.getFontAttribute(), aFontSize.getX(), aFontSize.getY(), fRotate,
rTextCandidate.getLocale());
} else
{
aFont = primitive2d::getVclFontFromFontAttribute(
rTextCandidate.getFontAttribute(), aFontScaling.getX(), aFontScaling.getY(),
fRotate, rTextCandidate.getLocale());
}
// Don't draw fonts without height
Size aResultFontSize = aFont.GetFontSize(); if (aResultFontSize.Height() <= 0) return;
// set FillColor Attribute const Color aFillColor(rTextCandidate.getTextFillColor());
aFont.SetTransparent(aFillColor.IsTransparent());
aFont.SetFillColor(aFillColor);
// handle additional font attributes const primitive2d::TextDecoratedPortionPrimitive2D* pTCPP = nullptr; if (rTextCandidate.getPrimitive2DID() == PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D)
pTCPP = static_cast<const primitive2d::TextDecoratedPortionPrimitive2D*>(
&rTextCandidate);
if (pTCPP != nullptr)
{ // set the color of text decorations const basegfx::BColor aTextlineColor
= maBColorModifierStack.getModifiedColor(pTCPP->getTextlineColor());
mpOutputDevice->SetTextLineColor(Color(aTextlineColor));
// set Overline attribute const FontLineStyle eFontOverline(
primitive2d::mapTextLineToFontLineStyle(pTCPP->getFontOverline())); if (eFontOverline != LINESTYLE_NONE)
{
aFont.SetOverline(eFontOverline); const basegfx::BColor aOverlineColor
= maBColorModifierStack.getModifiedColor(pTCPP->getOverlineColor());
mpOutputDevice->SetOverlineColor(Color(aOverlineColor)); if (pTCPP->getWordLineMode())
aFont.SetWordLineMode(true);
}
// set Underline attribute const FontLineStyle eFontLineStyle(
primitive2d::mapTextLineToFontLineStyle(pTCPP->getFontUnderline())); if (eFontLineStyle != LINESTYLE_NONE)
{
aFont.SetUnderline(eFontLineStyle); if (pTCPP->getWordLineMode())
aFont.SetWordLineMode(true);
}
// set Strikeout attribute const FontStrikeout eFontStrikeout(
primitive2d::mapTextStrikeoutToFontStrikeout(pTCPP->getTextStrikeout()));
if (eFontStrikeout != STRIKEOUT_NONE)
aFont.SetStrikeout(eFontStrikeout);
// set EmphasisMark attribute
FontEmphasisMark eFontEmphasisMark = FontEmphasisMark::NONE; switch (pTCPP->getTextEmphasisMark())
{ default:
SAL_WARN("drawinglayer", "Unknown EmphasisMark style " << pTCPP->getTextEmphasisMark());
[[fallthrough]]; case primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE:
eFontEmphasisMark = FontEmphasisMark::NONE; break; case primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT:
eFontEmphasisMark = FontEmphasisMark::Dot; break; case primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE:
eFontEmphasisMark = FontEmphasisMark::Circle; break; case primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC:
eFontEmphasisMark = FontEmphasisMark::Disc; break; case primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT:
eFontEmphasisMark = FontEmphasisMark::Accent; break;
}
if (eFontEmphasisMark != FontEmphasisMark::NONE)
{
DBG_ASSERT((pTCPP->getEmphasisMarkAbove() != pTCPP->getEmphasisMarkBelow()), "DrawingLayer: Bad EmphasisMark position!"); if (pTCPP->getEmphasisMarkAbove())
eFontEmphasisMark |= FontEmphasisMark::PosAbove; else
eFontEmphasisMark |= FontEmphasisMark::PosBelow;
aFont.SetEmphasisMark(eFontEmphasisMark);
}
// set Relief attribute
FontRelief eFontRelief = FontRelief::NONE; switch (pTCPP->getTextRelief())
{ default:
SAL_WARN("drawinglayer", "Unknown Relief style " << pTCPP->getTextRelief());
[[fallthrough]]; case primitive2d::TEXT_RELIEF_NONE:
eFontRelief = FontRelief::NONE; break; case primitive2d::TEXT_RELIEF_EMBOSSED:
eFontRelief = FontRelief::Embossed; break; case primitive2d::TEXT_RELIEF_ENGRAVED:
eFontRelief = FontRelief::Engraved; break;
}
if (eFontRelief != FontRelief::NONE)
aFont.SetRelief(eFontRelief);
// set Shadow attribute if (pTCPP->getShadow())
aFont.SetShadow(true);
}
// create integer DXArray
KernArray aDXArray;
if (!rTextCandidate.getDXArray().empty())
{ double fPixelVectorFactor(1.0); if (bScaleFont)
{ const basegfx::B2DVector aPixelVector(maCurrentTransformation
* basegfx::B2DVector(1.0, 0.0));
fPixelVectorFactor = aPixelVector.getLength();
}
aDXArray.reserve(rTextCandidate.getDXArray().size()); for (autoconst& elem : rTextCandidate.getDXArray())
aDXArray.push_back(elem * fPixelVectorFactor);
}
// set parameters and paint text snippet const basegfx::BColor aRGBFontColor(
maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
// Store previous complex text layout state, to be restored after drawing const vcl::text::ComplexTextLayoutFlags nOldLayoutMode(mpOutputDevice->GetLayoutMode());
if (rTextCandidate.getFontAttribute().getRTL())
{
vcl::text::ComplexTextLayoutFlags nRTLLayoutMode(
nOldLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong);
nRTLLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl
| vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
mpOutputDevice->SetLayoutMode(nRTLLayoutMode);
} else
{ // tdf#101686: This is LTR text, but the output device may have RTL state.
vcl::text::ComplexTextLayoutFlags nLTRLayoutMode(nOldLayoutMode);
nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiRtl;
nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong;
mpOutputDevice->SetLayoutMode(nLTRLayoutMode);
}
Point aStartPoint; bool bChangeMapMode(false); if (!bScaleFont)
{
basegfx::B2DHomMatrix aCombinedTransform(
getTransformFromMapMode(mpOutputDevice->GetMapMode())
* maCurrentTransformation);
if (!comphelper::LibreOfficeKit::isActive())
{ // aFont has an integer size; we must scale a bit for precision double nFontScalingFixY = aFontScaling.getY() / aResultFontSize.Height(); double nFontScalingFixX
= aFontScaling.getX()
/ (aResultFontSize.Width() ? aResultFontSize.Width()
: aResultFontSize.Height());
// tdf#168371 set letter spacing so that VCL knows it has to disable ligatures
aFont.SetFixKerning(rTextCandidate.getLetterSpacing());
// tdf#152990 set the font after the MapMode is (potentially) set so canvas uses the desired // font size
mpOutputDevice->SetFont(aFont);
mpOutputDevice->SetTextColor(Color(aRGBFontColor));
if (bPixelBased && getViewInformation2D().getPixelSnapHairline())
{ // #i98289# // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete // allows to suppress AntiAliasing for pure horizontal or vertical lines. This is done since // not-AntiAliased such lines look more pleasing to the eye (e.g. 2D chart content). This // NEEDS to be done in discrete coordinates, so only useful for pixel based rendering.
aLocalPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aLocalPolygon);
}
// direct draw of transformed BitmapEx primitive void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate)
{
BitmapEx aBitmapEx(rBitmapCandidate.getBitmap()); const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation
* rBitmapCandidate.getTransform());
if (maBColorModifierStack.count())
{
aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
if (aBitmapEx.IsEmpty())
{ // color gets completely replaced, get it const basegfx::BColor aModifiedColor(
maBColorModifierStack.getModifiedColor(basegfx::BColor()));
basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
aPolygon.transform(aLocalTransform);
// #122923# do no longer add Alpha channel here; the right place to do this is when really // the own transformer is used (see OutputDevice::DrawTransformedBitmapEx).
// draw using OutputDevice'sDrawTransformedBitmapEx
mpOutputDevice->DrawTransformedBitmapEx(aLocalTransform, aBitmapEx);
}
// #121194# when tiling is used and content is bitmap-based, do direct tiling in the // renderer on pixel base to ensure tight fitting. Do not do this when // the fill is rotated or sheared. if (!rFillGraphicAttribute.getTiling()) returnfalse;
// content is bitmap(ex) // // for Vector Graphic Data (SVG, EMF+) support, force decomposition when present. This will lead to use // the primitive representation of the vector data directly. // // when graphic is animated, force decomposition to use the correct graphic, else // fill style will not be animated if (GraphicType::Bitmap != rFillGraphicAttribute.getGraphic().GetType()
|| rFillGraphicAttribute.getGraphic().getVectorGraphicData()
|| rFillGraphicAttribute.getGraphic().IsAnimated()) returnfalse;
// decompose matrix to check for shear, rotate and mirroring
basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation
* rFillBitmapCandidate.getTransformation());
basegfx::B2DVector aScale, aTranslate; double fRotate, fShearX;
aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX);
// when nopt rotated/sheared if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX)) returnfalse;
// no shear or rotate, draw direct in pixel coordinates
// transform object range to device coordinates (pixels). Use // the device transformation for better accuracy
basegfx::B2DRange aObjectRange(aTranslate, aTranslate + aScale);
aObjectRange.transform(mpOutputDevice->GetViewTransformation());
// only do something when object has a size in discrete units if (nOWidth <= 0 || nOHeight <= 0) returntrue;
// transform graphic range to device coordinates (pixels). Use // the device transformation for better accuracy
basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange());
aGraphicRange.transform(mpOutputDevice->GetViewTransformation() * aLocalTransform);
// extract discrete size of graphic // caution: when getting to zero, nothing would be painted; thus, do not allow this const sal_Int32 nBWidth(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getWidth()))); const sal_Int32 nBHeight(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getHeight())));
// nBWidth, nBHeight is the pixel size of the needed bitmap. To not need to scale it // in vcl many times, create a size-optimized version const Size aNeededBitmapSizePixel(nBWidth, nBHeight);
BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx()); constbool bPreScaled(nBWidth * nBHeight < (250 * 250));
// ... but only up to a maximum size, else it gets too expensive if (bPreScaled)
{ // if color depth is below 24bit, expand before scaling for better quality. // This is even needed for low colors, else the scale will produce // a bitmap in gray or Black/White (!) if (isPalettePixelFormat(aBitmapEx.getPixelFormat()))
{
aBitmapEx.Convert(BmpConversion::N24Bit);
}
if (maBColorModifierStack.count())
{ // when color modifier, apply to bitmap
aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
// ModifyBitmapEx uses empty bitmap as sign to return that // the content will be completely replaced to mono color, use shortcut if (aBitmapEx.IsEmpty())
{ // color gets completely replaced, get it const basegfx::BColor aModifiedColor(
maBColorModifierStack.getModifiedColor(basegfx::BColor()));
basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
aPolygon.transform(aLocalTransform);
// prepare OutDev const Point aEmptyPoint(0, 0); // the visible rect, in pixels const ::tools::Rectangle aVisiblePixel(aEmptyPoint, mpOutputDevice->GetOutputSizePixel()); constbool bWasEnabled(mpOutputDevice->IsMapModeEnabled());
mpOutputDevice->EnableMapMode(false);
// check if offset is used const sal_Int32 nOffsetX(basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth)); const sal_Int32 nOffsetY(basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight));
// if the tile is a single pixel big, just flood fill with that pixel color if (nOffsetX == 0 && nOffsetY == 0 && aNeededBitmapSizePixel.getWidth() == 1
&& aNeededBitmapSizePixel.getHeight() == 1)
{
Color col = aBitmapEx.GetPixelColor(0, 0);
mpOutputDevice->SetLineColor(col);
mpOutputDevice->SetFillColor(col);
mpOutputDevice->DrawRect(aVisiblePixel);
} elseif (nOffsetX)
{ // offset in X, so iterate over Y first and draw lines for (sal_Int32 nYPos(nBTop); nYPos < nOTop + nOHeight; nYPos += nBHeight, nPosY++)
{ for (sal_Int32 nXPos((nPosY % 2) ? nBLeft - nBWidth + nOffsetX : nBLeft);
nXPos < nOLeft + nOWidth; nXPos += nBWidth)
{ const ::tools::Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel);
if (aOutRectPixel.Overlaps(aVisiblePixel))
{ if (bPreScaled)
{
mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
} else
{
mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(),
aNeededBitmapSizePixel, aBitmapEx);
}
}
}
}
} else// nOffsetY is used
{ // possible offset in Y, so iterate over X first and draw columns for (sal_Int32 nXPos(nBLeft); nXPos < nOLeft + nOWidth; nXPos += nBWidth, nPosX++)
{ for (sal_Int32 nYPos((nPosX % 2) ? nBTop - nBHeight + nOffsetY : nBTop);
nYPos < nOTop + nOHeight; nYPos += nBHeight)
{ const ::tools::Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel);
if (aOutRectPixel.Overlaps(aVisiblePixel))
{ if (bPreScaled)
{
mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
} else
{
mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(),
aNeededBitmapSizePixel, aBitmapEx);
}
}
}
}
}
// direct draw of Graphic void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D( const primitive2d::PolyPolygonGraphicPrimitive2D& rPolygonCandidate)
{ bool bDone(false); const basegfx::B2DPolyPolygon& rPolyPolygon = rPolygonCandidate.getB2DPolyPolygon();
// #121194# Todo: check if this works if (!rPolyPolygon.count())
{ // empty polyPolygon, done
bDone = true;
} else
{ const attribute::FillGraphicAttribute& rFillGraphicAttribute
= rPolygonCandidate.getFillGraphic();
// try to catch cases where the graphic will be color-modified to a single // color (e.g. shadow) switch (rFillGraphicAttribute.getGraphic().GetType())
{ case GraphicType::GdiMetafile:
{ // metafiles are potentially transparent, cannot optimize, not done break;
} case GraphicType::Bitmap:
{ if (!rFillGraphicAttribute.getGraphic().IsTransparent()
&& !rFillGraphicAttribute.getGraphic().IsAlpha()
&& !rPolygonCandidate.hasTransparency())
{ // bitmap is not transparent and has no alpha const sal_uInt32 nBColorModifierStackCount(maBColorModifierStack.count());
if (pReplacer)
{ // the bitmap fill is in unified color, so we can replace it with // a single polygon fill. The form of the fill depends on tiling if (rFillGraphicAttribute.getTiling())
{ // with tiling, fill the whole tools::PolyPolygon with the modifier color
basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon);
aLocalPolyPolygon.transform(maCurrentTransformation);
mpOutputDevice->SetLineColor();
mpOutputDevice->SetFillColor(Color(pReplacer->getBColor()));
mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
} else
{ // without tiling, only the area common to the bitmap tile and the // tools::PolyPolygon is filled. Create the bitmap tile area in object // coordinates. For this, the object transformation needs to be created // from the already scaled PolyPolygon. The tile area in object // coordinates will always be non-rotated, so it's not necessary to // work with a polygon here
basegfx::B2DRange aTileRange(
rFillGraphicAttribute.getGraphicRange()); const basegfx::B2DRange aPolyPolygonRange(
rPolyPolygon.getB2DRange()); const basegfx::B2DHomMatrix aNewObjectTransform(
basegfx::utils::createScaleTranslateB2DHomMatrix(
aPolyPolygonRange.getRange(),
aPolyPolygonRange.getMinimum()));
aTileRange.transform(aNewObjectTransform);
// now clip the object polyPolygon against the tile range // to get the common area
basegfx::B2DPolyPolygon aTarget
= basegfx::utils::clipPolyPolygonOnRange(
rPolyPolygon, aTileRange, true, false);
if (aTarget.count())
{
aTarget.transform(maCurrentTransformation);
mpOutputDevice->SetLineColor();
mpOutputDevice->SetFillColor(Color(pReplacer->getBColor()));
mpOutputDevice->DrawPolyPolygon(aTarget);
}
}
// simplified output executed, we are done
bDone = true;
}
}
} break;
} default: //GraphicType::NONE, GraphicType::Default
{ // empty graphic, we are done
bDone = true; break;
}
}
}
if (!bDone)
{ // use default decomposition
process(rPolygonCandidate);
}
}
// mask group void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& rMaskCandidate)
{ if (rMaskCandidate.getChildren().empty()) return;
// modified color group. Force output to unified color. void VclProcessor2D::RenderModifiedColorPrimitive2D( const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate)
{ if (!rModifiedCandidate.getChildren().empty())
{
maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
process(rModifiedCandidate.getChildren());
maBColorModifierStack.pop();
}
}
// unified sub-transparence. Draw to VDev first. void VclProcessor2D::RenderUnifiedTransparencePrimitive2D( const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate)
{ if (rTransCandidate.getChildren().empty()) return;
if (0.0 == rTransCandidate.getTransparence())
{ // no transparence used, so just use the content
process(rTransCandidate.getChildren());
} elseif (rTransCandidate.getTransparence() > 0.0 && rTransCandidate.getTransparence() < 1.0)
{ // transparence is in visible range
basegfx::B2DRange aRange(rTransCandidate.getChildren().getB2DRange(getViewInformation2D()));
aRange.transform(maCurrentTransformation);
impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
if (aBufferDevice.isVisible())
{ // remember last OutDev and set to content
OutputDevice* pLastOutputDevice = mpOutputDevice;
mpOutputDevice = &aBufferDevice.getContent();
// paint content to it
process(rTransCandidate.getChildren());
// back to old OutDev
mpOutputDevice = pLastOutputDevice;
// dump buffer to outdev using given transparence
aBufferDevice.paint(rTransCandidate.getTransparence());
}
}
}
// sub-transparence group. Draw to VDev first. void VclProcessor2D::RenderTransparencePrimitive2D( const primitive2d::TransparencePrimitive2D& rTransCandidate)
{ if (rTransCandidate.getChildren().empty()) return;
// remember last OutDev and set to content
OutputDevice* pLastOutputDevice = mpOutputDevice;
mpOutputDevice = &aBufferDevice.getContent();
// paint content to it
process(rTransCandidate.getChildren());
// set to mask
mpOutputDevice = &aBufferDevice.getTransparence();
// when painting transparence masks, reset the color stack
basegfx::BColorModifierStack aLastBColorModifierStack(maBColorModifierStack);
maBColorModifierStack = basegfx::BColorModifierStack();
// paint mask to it (always with transparence intensities, evtl. with AA)
process(rTransCandidate.getTransparence());
// back to old color stack
maBColorModifierStack = std::move(aLastBColorModifierStack);
// back to old OutDev
mpOutputDevice = pLastOutputDevice;
// create new transformations for CurrentTransformation // and for local ViewInformation2D
maCurrentTransformation = maCurrentTransformation * rTransformCandidate.getTransformation();
geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation()
* rTransformCandidate.getTransformation());
updateViewInformation(aViewInformation2D);
// process content
process(rTransformCandidate.getChildren());
// new XDrawPage for ViewInformation2D void VclProcessor2D::RenderPagePreviewPrimitive2D( const primitive2d::PagePreviewPrimitive2D& rPagePreviewCandidate)
{ // remember current transformation and ViewInformation const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
// create new local ViewInformation2D
geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
aViewInformation2D.setVisualizedPage(rPagePreviewCandidate.getXDrawPage());
updateViewInformation(aViewInformation2D);
// process decomposed content
process(rPagePreviewCandidate);
// do not forget evtl. moved origin in target device MapMode when // switching it off; it would be missing and lead to wrong positions. // All his could be done using logic sizes and coordinates, too, but // we want a 1:1 bitmap rendering here, so it's more safe and faster // to work with switching off MapMode usage completely. const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin());
mpOutputDevice->EnableMapMode(false);
for (autoconst& pos : rPositions)
{ const basegfx::B2DPoint aDiscreteTopLeft((maCurrentTransformation * pos)
- aDiscreteHalfSize); const Point aDiscretePoint(basegfx::fround<tools::Long>(aDiscreteTopLeft.getX()),
basegfx::fround<tools::Long>(aDiscreteTopLeft.getY()));
// point void VclProcessor2D::RenderPointArrayPrimitive2D( const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate)
{ const std::vector<basegfx::B2DPoint>& rPositions = rPointArrayCandidate.getPositions(); const basegfx::BColor aRGBColor(
maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor())); const Color aVCLColor(aRGBColor);
for (autoconst& pos : rPositions)
{ const basegfx::B2DPoint aViewPosition(maCurrentTransformation * pos); const Point aPos(basegfx::fround<tools::Long>(aViewPosition.getX()),
basegfx::fround<tools::Long>(aViewPosition.getY()));
mpOutputDevice->DrawPixel(aPos, aVCLColor);
}
}
void VclProcessor2D::RenderPolygonStrokePrimitive2D( const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate)
{ // #i101491# method restructured to clearly use the DrawPolyLine // calls starting from a defined line width const attribute::LineAttribute& rLineAttribute = rPolygonStrokeCandidate.getLineAttribute(); constdouble fLineWidth(rLineAttribute.getWidth()); bool bDone(false);
if (nCount)
{ constbool bAntiAliased(getViewInformation2D().getUseAntiAliasing());
aHairlinePolyPolygon.transform(maCurrentTransformation);
if (bAntiAliased)
{ if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.0))
{ // line in range ]0.0 .. 1.0[ // paint as simple hairline for (sal_uInt32 a(0); a < nCount; a++)
{
mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a), 0.0);
}
bDone = true;
} elseif (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.0))
{ // line in range [1.0 .. 2.0[ // paint as 2x2 with dynamic line distance
basegfx::B2DHomMatrix aMat; constdouble fDistance(fDiscreteLineWidth - 1.0); constdouble fHalfDistance(fDistance * 0.5);
for (sal_uInt32 a(0); a < nCount; a++)
{
basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
bDone = true;
} elseif (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 3.0))
{ // line in range [2.0 .. 3.0] // paint as cross in a 3x3 with dynamic line distance
basegfx::B2DHomMatrix aMat; constdouble fDistance((fDiscreteLineWidth - 1.0) * 0.5);
for (sal_uInt32 a(0); a < nCount; a++)
{
basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
bDone = true;
} else
{ // #i101491# line width above 3.0
}
} else
{ if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.5))
{ // line width below 1.5, draw the basic hairline polygon for (sal_uInt32 a(0); a < nCount; a++)
{
mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a), 0.0);
}
bDone = true;
} elseif (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.5))
{ // line width is in range ]1.5 .. 2.5], use four hairlines // drawn in a square for (sal_uInt32 a(0); a < nCount; a++)
{
basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
basegfx::B2DHomMatrix aMat;
bDone = true;
} else
{ // #i101491# line width is above 2.5
}
}
if (!bDone && rPolygonStrokeCandidate.getB2DPolygon().count() > 1000)
{ // #i101491# If the polygon complexity uses more than a given amount, do // use OutputDevice::DrawPolyLine directly; this will avoid buffering all // decompositions in primitives (memory) and fallback to old line painting // for very complex polygons, too for (sal_uInt32 a(0); a < nCount; a++)
{
mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a),
fDiscreteLineWidth, rLineAttribute.getLineJoin(),
rLineAttribute.getLineCap(),
rLineAttribute.getMiterMinimumAngle());
}
bDone = true;
}
}
}
if (!bDone)
{ // remember that we enter a PolygonStrokePrimitive2D decomposition, // used for AA thick line drawing
mnPolygonStrokePrimitive2D++;
// line width is big enough for standard filled polygon visualisation or zero
process(rPolygonStrokeCandidate);
void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEpsPrimitive2D)
{ // The new decomposition of Metafiles made it necessary to add an Eps // primitive to handle embedded Eps data. On some devices, this can be // painted directly (mac, printer). // To be able to handle the replacement correctly, i need to handle it myself // since DrawEPS will not be able e.g. to rotate the replacement. To be able // to do that, i added a boolean return to OutputDevice::DrawEPS(..) // to know when EPS was handled directly already.
basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0);
aRange.transform(maCurrentTransformation * rEpsPrimitive2D.getEpsTransform());
if (!bEPSPaintedDirectly)
{ // use the decomposition which will correctly handle the // fallback visualisation using full transformation (e.g. rotation)
process(rEpsPrimitive2D);
}
}
// calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2) const basegfx::B2DVector aDiscreteVector(
getViewInformation2D().getInverseObjectToViewTransformation()
* basegfx::B2DVector(1.0, 1.0)); constdouble fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / M_SQRT2));
// use color distance and discrete lengths to calculate step count const sal_uInt32 nSteps(calculateStepsForSvgGradient(aColorA, aColorB, fDelta, fDiscreteUnit));
// switch off line painting
mpOutputDevice->SetLineColor();
// prepare polygon in needed width at start position (with discrete overlap) const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(
basegfx::B2DRange(rCandidate.getOffsetA() - fDiscreteUnit, 0.0,
rCandidate.getOffsetA() + (fDelta / nSteps) + fDiscreteUnit, 1.0)));
// calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2) const basegfx::B2DVector aDiscreteVector(
getViewInformation2D().getInverseObjectToViewTransformation()
* basegfx::B2DVector(1.0, 1.0)); constdouble fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / M_SQRT2));
// use color distance and discrete lengths to calculate step count const sal_uInt32 nSteps(
calculateStepsForSvgGradient(aColorA, aColorB, fDeltaScale, fDiscreteUnit));
// switch off line painting
mpOutputDevice->SetLineColor();
// prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes) double fUnitScale(0.0); constdouble fUnitStep(1.0 / nSteps);
for (sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
{
basegfx::B2DHomMatrix aTransform; constdouble fEndScale(rCandidate.getScaleB() - (fDeltaScale * fUnitScale));
if (rCandidate.isTranslateSet())
{ const basegfx::B2DVector aTranslate(basegfx::interpolate(
rCandidate.getTranslateB(), rCandidate.getTranslateA(), fUnitScale));
VclProcessor2D::VclProcessor2D(const geometry::ViewInformation2D& rViewInformation,
OutputDevice& rOutDev)
: BaseProcessor2D(rViewInformation)
, mpOutputDevice(&rOutDev)
, maBColorModifierStack()
, mnPolygonStrokePrimitive2D(0)
{ // set digit language, derived from SvtCTLOptions to have the correct // number display for arabic/hindi numerals
rOutDev.SetDigitLanguage(drawinglayer::detail::getDigitLanguage());
}
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.