blob: 0c86025e795c1c1844a60cdb238e102a8640bf9a [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"
#include <stdio.h>
#include <string.h>
#include <tools/svwin.h>
#include <tools/debug.hxx>
#include <win/wincomp.hxx>
#include <win/saldata.hxx>
#include <win/salgdi.h>
#include <win/salbmp.h>
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
#if defined _MSC_VER
#pragma warning(push, 1)
#endif
#include <GdiPlus.h>
#include <GdiPlusEnums.h>
#include <GdiPlusColor.h>
#if defined _MSC_VER
#pragma warning(pop)
#endif
#include <basegfx/polygon/b2dpolygon.hxx>
// -----------------------------------------------------------------------
void impAddB2DPolygonToGDIPlusGraphicsPathReal(Gdiplus::GraphicsPath& rPath, const basegfx::B2DPolygon& rPolygon, bool bNoLineJoin)
{
sal_uInt32 nCount(rPolygon.count());
if(nCount)
{
const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
const bool bControls(rPolygon.areControlPointsUsed());
basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
Gdiplus::PointF aFCurr(Gdiplus::REAL(aCurr.getX()), Gdiplus::REAL(aCurr.getY()));
for(sal_uInt32 a(0); a < nEdgeCount; a++)
{
const sal_uInt32 nNextIndex((a + 1) % nCount);
const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
const Gdiplus::PointF aFNext(Gdiplus::REAL(aNext.getX()), Gdiplus::REAL(aNext.getY()));
if(bControls && (rPolygon.isNextControlPointUsed(a) || rPolygon.isPrevControlPointUsed(nNextIndex)))
{
const basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
const basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
rPath.AddBezier(
aFCurr,
Gdiplus::PointF(Gdiplus::REAL(aCa.getX()), Gdiplus::REAL(aCa.getY())),
Gdiplus::PointF(Gdiplus::REAL(aCb.getX()), Gdiplus::REAL(aCb.getY())),
aFNext);
}
else
{
rPath.AddLine(aFCurr, aFNext);
}
if(a + 1 < nEdgeCount)
{
aFCurr = aFNext;
if(bNoLineJoin)
{
rPath.StartFigure();
}
}
}
}
}
void impAddB2DPolygonToGDIPlusGraphicsPathInteger(Gdiplus::GraphicsPath& rPath, const basegfx::B2DPolygon& rPolygon, bool bNoLineJoin)
{
sal_uInt32 nCount(rPolygon.count());
if(nCount)
{
const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
const bool bControls(rPolygon.areControlPointsUsed());
basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
Gdiplus::Point aICurr(INT(aCurr.getX()), INT(aCurr.getY()));
for(sal_uInt32 a(0); a < nEdgeCount; a++)
{
const sal_uInt32 nNextIndex((a + 1) % nCount);
const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
const Gdiplus::Point aINext(INT(aNext.getX()), INT(aNext.getY()));
if(bControls && (rPolygon.isNextControlPointUsed(a) || rPolygon.isPrevControlPointUsed(nNextIndex)))
{
const basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
const basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
rPath.AddBezier(
aICurr,
Gdiplus::Point(INT(aCa.getX()), INT(aCa.getY())),
Gdiplus::Point(INT(aCb.getX()), INT(aCb.getY())),
aINext);
}
else
{
rPath.AddLine(aICurr, aINext);
}
if(a + 1 < nEdgeCount)
{
aICurr = aINext;
if(bNoLineJoin)
{
rPath.StartFigure();
}
}
}
}
}
bool WinSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency)
{
const sal_uInt32 nCount(rPolyPolygon.count());
if(mbBrush && nCount && (fTransparency >= 0.0 && fTransparency < 1.0))
{
Gdiplus::Graphics aGraphics(getHDC());
const sal_uInt8 aTrans((sal_uInt8)255 - (sal_uInt8)basegfx::fround(fTransparency * 255.0));
Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maFillColor), SALCOLOR_GREEN(maFillColor), SALCOLOR_BLUE(maFillColor));
Gdiplus::SolidBrush aTestBrush(aTestColor);
Gdiplus::GraphicsPath aPath;
for(sal_uInt32 a(0); a < nCount; a++)
{
if(0 != a)
{
aPath.StartFigure(); // #i101491# not needed for first run
}
impAddB2DPolygonToGDIPlusGraphicsPathReal(aPath, rPolyPolygon.getB2DPolygon(a), false);
aPath.CloseFigure();
}
if(getAntiAliasB2DDraw())
{
aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
}
else
{
aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
}
if(mbPrinter)
{
// #121591#
// Normally GdiPlus should not be used for printing at all since printers cannot
// print transparent filled polygon geometry and normally this does not happen
// since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
// and no transparent parts should remain for printing. But this can be overriden
// by the user and thus happens. This call can only come (currently) from
// OutputDevice::DrawTransparent, see comments sthere with the same TaskID.
// If it is used, the mapping for the printer is wrong and needs to be corrected. I
// checked that there is *no* transformation set (testcode commented out below) and
// estimated that a stable factor dependent of the printer's DPI is used. Create
// and set a transformation here to correct this
const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());
// test code to check the current transformation at the graphics device
//Gdiplus::Matrix matrix;
//aGraphics.GetTransform(&matrix);
//Gdiplus::REAL elements[6];
//matrix.GetElements(elements);
Gdiplus::Matrix aPrinterTransform;
aPrinterTransform.Scale(Gdiplus::REAL(100.0) / aDpiX, Gdiplus::REAL(100.0) / aDpiY);
aGraphics.SetTransform(&aPrinterTransform);
}
aGraphics.FillPath(&aTestBrush, &aPath);
}
return true;
}
bool WinSalGraphics::drawPolyLine(
const basegfx::B2DPolygon& rPolygon,
double fTransparency,
const basegfx::B2DVector& rLineWidths,
basegfx::B2DLineJoin eLineJoin,
com::sun::star::drawing::LineCap eLineCap)
{
const sal_uInt32 nCount(rPolygon.count());
if(mbPen && nCount)
{
Gdiplus::Graphics aGraphics(getHDC());
const sal_uInt8 aTrans = (sal_uInt8)basegfx::fround( 255 * (1.0 - fTransparency) );
Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maLineColor), SALCOLOR_GREEN(maLineColor), SALCOLOR_BLUE(maLineColor));
Gdiplus::Pen aTestPen(aTestColor, Gdiplus::REAL(rLineWidths.getX()));
Gdiplus::GraphicsPath aPath;
bool bNoLineJoin(false);
switch(eLineJoin)
{
default : // basegfx::B2DLINEJOIN_NONE :
{
if(basegfx::fTools::more(rLineWidths.getX(), 0.0))
{
bNoLineJoin = true;
}
break;
}
case basegfx::B2DLINEJOIN_BEVEL :
{
aTestPen.SetLineJoin(Gdiplus::LineJoinBevel);
break;
}
case basegfx::B2DLINEJOIN_MIDDLE :
case basegfx::B2DLINEJOIN_MITER :
{
const Gdiplus::REAL aMiterLimit(15.0);
aTestPen.SetMiterLimit(aMiterLimit);
aTestPen.SetLineJoin(Gdiplus::LineJoinMiter);
break;
}
case basegfx::B2DLINEJOIN_ROUND :
{
aTestPen.SetLineJoin(Gdiplus::LineJoinRound);
break;
}
}
switch(eLineCap)
{
default: /*com::sun::star::drawing::LineCap_BUTT*/
{
// nothing to do
break;
}
case com::sun::star::drawing::LineCap_ROUND:
{
aTestPen.SetStartCap(Gdiplus::LineCapRound);
aTestPen.SetEndCap(Gdiplus::LineCapRound);
break;
}
case com::sun::star::drawing::LineCap_SQUARE:
{
aTestPen.SetStartCap(Gdiplus::LineCapSquare);
aTestPen.SetEndCap(Gdiplus::LineCapSquare);
break;
}
}
if(nCount > 250 && basegfx::fTools::more(rLineWidths.getX(), 1.5))
{
impAddB2DPolygonToGDIPlusGraphicsPathInteger(aPath, rPolygon, bNoLineJoin);
}
else
{
impAddB2DPolygonToGDIPlusGraphicsPathReal(aPath, rPolygon, bNoLineJoin);
}
if(rPolygon.isClosed() && !bNoLineJoin)
{
// #i101491# needed to create the correct line joins
aPath.CloseFigure();
}
if(getAntiAliasB2DDraw())
{
aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
}
else
{
aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
}
aGraphics.DrawPath(&aTestPen, &aPath);
}
return true;
}
// -----------------------------------------------------------------------
void paintToGdiPlus(
Gdiplus::Graphics& rGraphics,
const SalTwoRect& rTR,
Gdiplus::Bitmap& rBitmap)
{
// only parts of source are used
Gdiplus::PointF aDestPoints[3];
Gdiplus::ImageAttributes aAttributes;
// define target region as paralellogram
aDestPoints[0].X = Gdiplus::REAL(rTR.mnDestX);
aDestPoints[0].Y = Gdiplus::REAL(rTR.mnDestY);
aDestPoints[1].X = Gdiplus::REAL(rTR.mnDestX + rTR.mnDestWidth);
aDestPoints[1].Y = Gdiplus::REAL(rTR.mnDestY);
aDestPoints[2].X = Gdiplus::REAL(rTR.mnDestX);
aDestPoints[2].Y = Gdiplus::REAL(rTR.mnDestY + rTR.mnDestHeight);
aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
rGraphics.DrawImage(
&rBitmap,
aDestPoints,
3,
Gdiplus::REAL(rTR.mnSrcX),
Gdiplus::REAL(rTR.mnSrcY),
Gdiplus::REAL(rTR.mnSrcWidth),
Gdiplus::REAL(rTR.mnSrcHeight),
Gdiplus::UnitPixel,
&aAttributes,
0,
0);
}
// -----------------------------------------------------------------------
void setInterpolationMode(
Gdiplus::Graphics& rGraphics,
const long& rSrcWidth,
const long& rDestWidth,
const long& rSrcHeight,
const long& rDestHeight)
{
const bool bSameWidth(rSrcWidth == rDestWidth);
const bool bSameHeight(rSrcHeight == rDestHeight);
if(bSameWidth && bSameHeight)
{
rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeInvalid);
}
else if(rDestWidth > rSrcWidth && rDestHeight > rSrcHeight)
{
rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
}
else if(rDestWidth < rSrcWidth && rDestHeight < rSrcHeight)
{
rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
}
else
{
rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
}
}
bool WinSalGraphics::tryDrawBitmapGdiPlus(const SalTwoRect& rTR, const SalBitmap& rSrcBitmap)
{
if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
{
const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
GdiPlusBmpPtr aARGB(rSalBitmap.ImplGetGdiPlusBitmap());
if(aARGB.get())
{
Gdiplus::Graphics aGraphics(getHDC());
setInterpolationMode(
aGraphics,
rTR.mnSrcWidth,
rTR.mnDestWidth,
rTR.mnSrcHeight,
rTR.mnDestHeight);
paintToGdiPlus(
aGraphics,
rTR,
*aARGB.get());
return true;
}
}
return false;
}
bool WinSalGraphics::drawAlphaBitmap(
const SalTwoRect& rTR,
const SalBitmap& rSrcBitmap,
const SalBitmap& rAlphaBmp)
{
if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
{
const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
const WinSalBitmap& rSalAlpha = static_cast< const WinSalBitmap& >(rAlphaBmp);
GdiPlusBmpPtr aARGB(rSalBitmap.ImplGetGdiPlusBitmap(&rSalAlpha));
if(aARGB.get())
{
Gdiplus::Graphics aGraphics(getHDC());
setInterpolationMode(
aGraphics,
rTR.mnSrcWidth,
rTR.mnDestWidth,
rTR.mnSrcHeight,
rTR.mnDestHeight);
paintToGdiPlus(
aGraphics,
rTR,
*aARGB.get());
return true;
}
}
return false;
}
// -----------------------------------------------------------------------
bool WinSalGraphics::drawTransformedBitmap(
const basegfx::B2DPoint& rNull,
const basegfx::B2DPoint& rX,
const basegfx::B2DPoint& rY,
const SalBitmap& rSourceBitmap,
const SalBitmap* pAlphaBitmap)
{
const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSourceBitmap);
const WinSalBitmap* pSalAlpha = static_cast< const WinSalBitmap* >(pAlphaBitmap);
GdiPlusBmpPtr aARGB(rSalBitmap.ImplGetGdiPlusBitmap(pSalAlpha));
if(aARGB.get())
{
const long nSrcWidth(aARGB->GetWidth());
const long nSrcHeight(aARGB->GetHeight());
if(nSrcWidth && nSrcHeight)
{
const long nDestWidth(basegfx::fround(basegfx::B2DVector(rX - rNull).getLength()));
const long nDestHeight(basegfx::fround(basegfx::B2DVector(rY - rNull).getLength()));
if(nDestWidth && nDestHeight)
{
Gdiplus::Graphics aGraphics(getHDC());
Gdiplus::PointF aDestPoints[3];
Gdiplus::ImageAttributes aAttributes;
setInterpolationMode(
aGraphics,
nSrcWidth,
nDestWidth,
nSrcHeight,
nDestHeight);
// this mode is only capable of drawing the whole bitmap to a paralellogram
aDestPoints[0].X = Gdiplus::REAL(rNull.getX());
aDestPoints[0].Y = Gdiplus::REAL(rNull.getY());
aDestPoints[1].X = Gdiplus::REAL(rX.getX());
aDestPoints[1].Y = Gdiplus::REAL(rX.getY());
aDestPoints[2].X = Gdiplus::REAL(rY.getX());
aDestPoints[2].Y = Gdiplus::REAL(rY.getY());
aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
aGraphics.DrawImage(
aARGB.get(),
aDestPoints,
3,
Gdiplus::REAL(0.0),
Gdiplus::REAL(0.0),
Gdiplus::REAL(nSrcWidth),
Gdiplus::REAL(nSrcHeight),
Gdiplus::UnitPixel,
&aAttributes,
0,
0);
}
}
return true;
}
return false;
}
// -----------------------------------------------------------------------
// eof