2017-10-21 13:50:30 +00:00
|
|
|
/* -*- 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 .
|
|
|
|
*/
|
|
|
|
|
2018-06-01 15:28:26 +02:00
|
|
|
#include <Qt5Graphics.hxx>
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2018-06-01 15:28:26 +02:00
|
|
|
#include <Qt5Bitmap.hxx>
|
|
|
|
#include <Qt5Painter.hxx>
|
2017-10-30 18:33:06 +01:00
|
|
|
|
2018-07-28 15:57:23 +02:00
|
|
|
#include <sal/log.hxx>
|
|
|
|
|
2017-10-30 18:33:06 +01:00
|
|
|
#include <QtGui/QPainter>
|
|
|
|
#include <QtGui/QScreen>
|
|
|
|
#include <QtGui/QWindow>
|
|
|
|
#include <QtWidgets/QWidget>
|
|
|
|
|
2018-08-31 09:31:24 +02:00
|
|
|
#include <basegfx/polygon/b2dpolygontools.hxx>
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
static const basegfx::B2DPoint aHalfPointOfs(0.5, 0.5);
|
2017-10-30 20:31:42 +01:00
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
static void AddPolygonToPath(QPainterPath& rPath, const basegfx::B2DPolygon& rPolygon,
|
|
|
|
bool bClosePath, bool bPixelSnap, bool bLineDraw)
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
2018-10-13 17:05:11 +02:00
|
|
|
const int nPointCount = rPolygon.count();
|
2017-10-30 20:31:42 +01:00
|
|
|
// short circuit if there is nothing to do
|
2018-10-13 17:05:11 +02:00
|
|
|
if (nPointCount == 0)
|
2017-10-30 20:31:42 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
const bool bHasCurves = rPolygon.areControlPointsUsed();
|
2017-11-08 11:28:04 +01:00
|
|
|
for (int nPointIdx = 0, nPrevIdx = 0;; nPrevIdx = nPointIdx++)
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
|
|
|
int nClosedIdx = nPointIdx;
|
2017-11-08 11:28:04 +01:00
|
|
|
if (nPointIdx >= nPointCount)
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
|
|
|
// prepare to close last curve segment if needed
|
2017-11-08 11:28:04 +01:00
|
|
|
if (bClosePath && (nPointIdx == nPointCount))
|
2017-10-30 20:31:42 +01:00
|
|
|
nClosedIdx = 0;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
basegfx::B2DPoint aPoint = rPolygon.getB2DPoint(nClosedIdx);
|
2017-10-30 20:31:42 +01:00
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
if (bPixelSnap)
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
|
|
|
// snap device coordinates to full pixels
|
2017-11-08 11:28:04 +01:00
|
|
|
aPoint.setX(basegfx::fround(aPoint.getX()));
|
|
|
|
aPoint.setY(basegfx::fround(aPoint.getY()));
|
2017-10-30 20:31:42 +01:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
if (bLineDraw)
|
2017-10-30 20:31:42 +01:00
|
|
|
aPoint += aHalfPointOfs;
|
2017-11-08 11:28:04 +01:00
|
|
|
if (!nPointIdx)
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
|
|
|
// first point => just move there
|
2017-11-08 11:28:04 +01:00
|
|
|
rPath.moveTo(aPoint.getX(), aPoint.getY());
|
2017-10-30 20:31:42 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bPendingCurve = false;
|
2017-11-08 11:28:04 +01:00
|
|
|
if (bHasCurves)
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
bPendingCurve = rPolygon.isNextControlPointUsed(nPrevIdx);
|
|
|
|
bPendingCurve |= rPolygon.isPrevControlPointUsed(nClosedIdx);
|
2017-10-30 20:31:42 +01:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
if (!bPendingCurve) // line segment
|
|
|
|
rPath.lineTo(aPoint.getX(), aPoint.getY());
|
|
|
|
else // cubic bezier segment
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
basegfx::B2DPoint aCP1 = rPolygon.getNextControlPoint(nPrevIdx);
|
|
|
|
basegfx::B2DPoint aCP2 = rPolygon.getPrevControlPoint(nClosedIdx);
|
|
|
|
if (bLineDraw)
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
|
|
|
aCP1 += aHalfPointOfs;
|
|
|
|
aCP2 += aHalfPointOfs;
|
|
|
|
}
|
2017-11-08 11:28:04 +01:00
|
|
|
rPath.cubicTo(aCP1.getX(), aCP1.getY(), aCP2.getX(), aCP2.getY(), aPoint.getX(),
|
|
|
|
aPoint.getY());
|
2017-10-30 20:31:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
if (bClosePath)
|
2017-10-30 20:31:42 +01:00
|
|
|
rPath.closeSubpath();
|
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
static bool AddPolyPolygonToPath(QPainterPath& rPath, const basegfx::B2DPolyPolygon& rPolyPoly,
|
|
|
|
bool bPixelSnap, bool bLineDraw)
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
2018-10-13 19:08:40 +02:00
|
|
|
if (rPolyPoly.count() == 0)
|
2017-10-30 20:31:42 +01:00
|
|
|
return false;
|
2018-10-12 20:06:18 +02:00
|
|
|
for (auto const& rPolygon : rPolyPoly)
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
AddPolygonToPath(rPath, rPolygon, true, bPixelSnap, bLineDraw);
|
2017-10-30 20:31:42 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
bool Qt5Graphics::setClipRegion(const vcl::Region& rRegion)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
if (rRegion.IsRectangle())
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
m_aClipRegion = toQRect(rRegion.GetBoundRect());
|
|
|
|
if (!m_aClipPath.isEmpty())
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
|
|
|
QPainterPath aPath;
|
2017-11-08 11:28:04 +01:00
|
|
|
m_aClipPath.swap(aPath);
|
2017-10-30 20:31:42 +01:00
|
|
|
}
|
|
|
|
}
|
2017-11-08 11:28:04 +01:00
|
|
|
else if (!rRegion.HasPolyPolygonOrB2DPolyPolygon())
|
2017-10-30 18:33:06 +01:00
|
|
|
{
|
|
|
|
QRegion aQRegion;
|
|
|
|
RectangleVector aRectangles;
|
2017-11-08 11:28:04 +01:00
|
|
|
rRegion.GetRegionRectangles(aRectangles);
|
|
|
|
for (auto& rRect : aRectangles)
|
|
|
|
aQRegion += toQRect(rRect);
|
2017-10-30 18:33:06 +01:00
|
|
|
m_aClipRegion = aQRegion;
|
2017-11-08 11:28:04 +01:00
|
|
|
if (!m_aClipPath.isEmpty())
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
|
|
|
QPainterPath aPath;
|
2017-11-08 11:28:04 +01:00
|
|
|
m_aClipPath.swap(aPath);
|
2017-10-30 20:31:42 +01:00
|
|
|
}
|
2017-10-30 18:33:06 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-10-30 20:31:42 +01:00
|
|
|
QPainterPath aPath;
|
2017-11-08 11:28:04 +01:00
|
|
|
const basegfx::B2DPolyPolygon aPolyClip(rRegion.GetAsB2DPolyPolygon());
|
|
|
|
AddPolyPolygonToPath(aPath, aPolyClip, !getAntiAliasB2DDraw(), false);
|
|
|
|
m_aClipPath.swap(aPath);
|
|
|
|
if (!m_aClipRegion.isEmpty())
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
|
|
|
QRegion aRegion;
|
2017-11-08 11:28:04 +01:00
|
|
|
m_aClipRegion.swap(aRegion);
|
2017-10-30 20:31:42 +01:00
|
|
|
}
|
2017-10-30 18:33:06 +01:00
|
|
|
}
|
|
|
|
return true;
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 18:45:46 +01:00
|
|
|
void Qt5Graphics::ResetClipRegion()
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-12-09 23:12:02 +00:00
|
|
|
if (m_pQImage)
|
|
|
|
m_aClipRegion = QRegion(m_pQImage->rect());
|
|
|
|
else
|
|
|
|
m_aClipRegion = QRegion();
|
2017-11-08 11:28:04 +01:00
|
|
|
if (!m_aClipPath.isEmpty())
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
|
|
|
QPainterPath aPath;
|
2017-11-08 11:28:04 +01:00
|
|
|
m_aClipPath.swap(aPath);
|
2017-10-30 20:31:42 +01:00
|
|
|
}
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
void Qt5Graphics::drawPixel(long nX, long nY)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
Qt5Painter aPainter(*this);
|
|
|
|
aPainter.drawPoint(nX, nY);
|
|
|
|
aPainter.update(nX, nY, 1, 1);
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2018-03-14 11:11:02 +02:00
|
|
|
void Qt5Graphics::drawPixel(long nX, long nY, Color nColor)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
Qt5Painter aPainter(*this);
|
2018-05-17 12:55:55 +02:00
|
|
|
aPainter.setPen(toQColor(nColor));
|
2017-11-08 11:28:04 +01:00
|
|
|
aPainter.setPen(Qt::SolidLine);
|
|
|
|
aPainter.drawPoint(nX, nY);
|
|
|
|
aPainter.update(nX, nY, 1, 1);
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
void Qt5Graphics::drawLine(long nX1, long nY1, long nX2, long nY2)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
Qt5Painter aPainter(*this);
|
|
|
|
aPainter.drawLine(nX1, nY1, nX2, nY2);
|
2017-10-30 16:11:09 +01:00
|
|
|
|
|
|
|
long tmp;
|
2017-11-08 11:28:04 +01:00
|
|
|
if (nX1 > nX2)
|
2017-10-30 16:11:09 +01:00
|
|
|
{
|
|
|
|
tmp = nX1;
|
|
|
|
nX1 = nX2;
|
|
|
|
nX2 = tmp;
|
|
|
|
}
|
2017-11-08 11:28:04 +01:00
|
|
|
if (nY1 > nY2)
|
2017-10-30 16:11:09 +01:00
|
|
|
{
|
|
|
|
tmp = nY1;
|
|
|
|
nY1 = nY2;
|
|
|
|
nY2 = tmp;
|
|
|
|
}
|
2018-07-05 18:15:50 +02:00
|
|
|
aPainter.update(nX1, nY1, nX2 - nX1 + 1, nY2 - nY1 + 1);
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
void Qt5Graphics::drawRect(long nX, long nY, long nWidth, long nHeight)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
|
2017-10-30 16:11:09 +01:00
|
|
|
return;
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
Qt5Painter aPainter(*this, true);
|
|
|
|
if (SALCOLOR_NONE != m_aFillColor)
|
|
|
|
aPainter.fillRect(nX, nY, nWidth, nHeight, aPainter.brush());
|
2018-07-05 18:15:50 +02:00
|
|
|
if (SALCOLOR_NONE != m_aLineColor)
|
2017-11-08 11:28:04 +01:00
|
|
|
aPainter.drawRect(nX, nY, nWidth, nHeight);
|
|
|
|
aPainter.update(nX, nY, nWidth, nHeight);
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
void Qt5Graphics::drawPolyLine(sal_uInt32 nPoints, const SalPoint* pPtAry)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
if (0 == nPoints)
|
2017-10-30 16:11:09 +01:00
|
|
|
return;
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
Qt5Painter aPainter(*this);
|
|
|
|
QPoint* pPoints = new QPoint[nPoints];
|
|
|
|
QPoint aTopLeft(pPtAry->mnX, pPtAry->mnY);
|
2017-10-30 16:11:09 +01:00
|
|
|
QPoint aBottomRight = aTopLeft;
|
2017-11-08 11:28:04 +01:00
|
|
|
for (sal_uInt32 i = 0; i < nPoints; ++i, ++pPtAry)
|
2017-10-30 16:11:09 +01:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
pPoints[i] = QPoint(pPtAry->mnX, pPtAry->mnY);
|
|
|
|
if (pPtAry->mnX < aTopLeft.x())
|
|
|
|
aTopLeft.setX(pPtAry->mnX);
|
|
|
|
if (pPtAry->mnY < aTopLeft.y())
|
|
|
|
aTopLeft.setY(pPtAry->mnY);
|
|
|
|
if (pPtAry->mnX > aBottomRight.x())
|
|
|
|
aBottomRight.setX(pPtAry->mnX);
|
|
|
|
if (pPtAry->mnY > aBottomRight.y())
|
|
|
|
aBottomRight.setY(pPtAry->mnY);
|
2017-10-30 16:11:09 +01:00
|
|
|
}
|
2017-11-08 11:28:04 +01:00
|
|
|
aPainter.drawPolyline(pPoints, nPoints);
|
|
|
|
delete[] pPoints;
|
|
|
|
aPainter.update(QRect(aTopLeft, aBottomRight));
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
void Qt5Graphics::drawPolygon(sal_uInt32 nPoints, const SalPoint* pPtAry)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
Qt5Painter aPainter(*this, true);
|
|
|
|
QPolygon aPolygon(nPoints);
|
|
|
|
for (sal_uInt32 i = 0; i < nPoints; ++i, ++pPtAry)
|
|
|
|
aPolygon.setPoint(i, pPtAry->mnX, pPtAry->mnY);
|
|
|
|
aPainter.drawPolygon(aPolygon);
|
|
|
|
aPainter.update(aPolygon.boundingRect());
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2018-07-05 18:24:28 +02:00
|
|
|
void Qt5Graphics::drawPolyPolygon(sal_uInt32 nPolyCount, const sal_uInt32* pPoints,
|
|
|
|
PCONSTSALPOINT* ppPtAry)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2018-07-05 18:24:28 +02:00
|
|
|
// ignore invisible polygons
|
|
|
|
if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
|
|
|
|
return;
|
|
|
|
|
|
|
|
QPainterPath aPath;
|
|
|
|
for (sal_uInt32 nPoly = 0; nPoly < nPolyCount; nPoly++)
|
|
|
|
{
|
|
|
|
const sal_uInt32 nPoints = pPoints[nPoly];
|
|
|
|
if (nPoints > 1)
|
|
|
|
{
|
|
|
|
const SalPoint* pPtAry = ppPtAry[nPoly];
|
|
|
|
aPath.moveTo(pPtAry->mnX, pPtAry->mnY);
|
|
|
|
pPtAry++;
|
|
|
|
for (sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++)
|
|
|
|
aPath.lineTo(pPtAry->mnX, pPtAry->mnY);
|
|
|
|
aPath.closeSubpath();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt5Painter aPainter(*this, true);
|
|
|
|
aPainter.drawPath(aPath);
|
|
|
|
aPainter.update(aPath.boundingRect());
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 18:15:02 +02:00
|
|
|
bool Qt5Graphics::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
|
|
|
|
const basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-10-30 20:31:42 +01:00
|
|
|
// ignore invisible polygons
|
2017-11-08 11:28:04 +01:00
|
|
|
if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
|
2017-10-30 20:31:42 +01:00
|
|
|
return true;
|
2017-11-08 11:28:04 +01:00
|
|
|
if ((fTransparency >= 1.0) || (fTransparency < 0))
|
2017-10-30 20:31:42 +01:00
|
|
|
return true;
|
|
|
|
|
2018-09-06 18:15:02 +02:00
|
|
|
// Fallback: Transform to DeviceCoordinates
|
|
|
|
basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
|
|
|
|
aPolyPolygon.transform(rObjectToDevice);
|
|
|
|
|
2017-10-30 20:31:42 +01:00
|
|
|
QPainterPath aPath;
|
|
|
|
// ignore empty polygons
|
2018-09-06 18:15:02 +02:00
|
|
|
if (!AddPolyPolygonToPath(aPath, aPolyPolygon, !getAntiAliasB2DDraw(),
|
2017-11-08 11:28:04 +01:00
|
|
|
m_aLineColor != SALCOLOR_NONE))
|
2017-10-30 20:31:42 +01:00
|
|
|
return true;
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
Qt5Painter aPainter(*this, true, 255 * (1.0 - fTransparency));
|
|
|
|
aPainter.drawPath(aPath);
|
|
|
|
aPainter.update(aPath.boundingRect());
|
2017-10-30 20:31:42 +01:00
|
|
|
return true;
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2018-01-08 09:38:24 +02:00
|
|
|
bool Qt5Graphics::drawPolyLineBezier(sal_uInt32 /*nPoints*/, const SalPoint* /*pPtAry*/,
|
|
|
|
const PolyFlags* /*pFlgAry*/)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-01-08 09:38:24 +02:00
|
|
|
bool Qt5Graphics::drawPolygonBezier(sal_uInt32 /*nPoints*/, const SalPoint* /*pPtAry*/,
|
|
|
|
const PolyFlags* /*pFlgAry*/)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-01-08 09:38:24 +02:00
|
|
|
bool Qt5Graphics::drawPolyPolygonBezier(sal_uInt32 /*nPoly*/, const sal_uInt32* /*pPoints*/,
|
|
|
|
const SalPoint* const* /*pPtAry*/,
|
|
|
|
const PolyFlags* const* /*pFlgAry*/)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Support buffering SystemDependent GraphicData
This is a first step to allow buffering of system
dependent data, especially (but not only) for the
system-dependent implementations of graphic output.
For example, for B2DPolygon and Win output, it allows
buffering the Gdiplus::GraphicsPath instead of re-
creating it all the time.
To support that, the change includes forwarding the
current transformation to the renderers in SalGraphics.
The current state in VCL is to transform all and
everything to device coordinates at every single
paint.
I have currently started to do this for ::drawPolyLine
implementations. The fallbacks for all systems will
at the start of that method just transform the data
to device coordinates, so all works as before.
This may also be done for FilledPolygon paint in a later
step, but most urgent is FatLine painting.
An arrangement of shared_ptr/weak_ptr is used so that
either the instance buffering (in the example B2DPolygon)
or the instance managing it can delete it. The instance
managing it currently uses a 1s Timer and a cycle-lifetime
management, but that can be extended in the future
to e.g. include size hints, too.
The mechanism it designed to support multiple Data per
buffering element, e.g. for B2DPolygon at the same time
system-dependent instances of Gdiplus and Cairo can be
buffered, but also PDF-data.
This is achieved semi-automatic by using
typeid(class).hash_code() as key for organization.
The mechanism will be used for now at B2DPolygon, but
is not limited to. There is already a similar but less
general buffer (see GdiPlusBuffer) that can and will
be converted to use this new mechanism.
Added vcl/headless Cairo renderer to support given
ObjectToDevice transformation (not to transform given
B2DPolygon)
Added support for CairoPath buffered at B2DPolygon,
seems to work well. Need to do more tests
Moved usage to templates suggested by Noel Grandin
(Noel Grandin <noelgrandin@gmail.com>), thanks for
these suggestions. Adapted Win usage to that, too.
Converted Win-specific GdiPlus BitmapBuffer to new
mechanism, works well. Checked, the manager holds
now a mix of bitmap and path data under Win
Added a cleanup mechanism to flush all buffered data
at DeInitVCL() using flushAll() at
SystemDependentDataBuffer
Adapted Linux-versions of ::drawPolyLine to support
PixelSnapHairline, for now in a simplified version
that still allows buffering. This will also be used
(and use buffering) for the Cairo-fallback in
X11SalGraphics
Change-Id: I88d7e438a20b96ddab7707050893bdd590c098c7
Reviewed-on: https://gerrit.libreoffice.org/59555
Tested-by: Armin Le Grand <Armin.Le.Grand@cib.de>
Reviewed-by: Armin Le Grand <Armin.Le.Grand@cib.de>
2018-08-24 13:01:08 +02:00
|
|
|
bool Qt5Graphics::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
|
|
|
|
const basegfx::B2DPolygon& rPolyLine, double fTransparency,
|
2017-11-08 11:28:04 +01:00
|
|
|
const basegfx::B2DVector& rLineWidths,
|
|
|
|
basegfx::B2DLineJoin eLineJoin, css::drawing::LineCap eLineCap,
|
Support buffering SystemDependent GraphicData
This is a first step to allow buffering of system
dependent data, especially (but not only) for the
system-dependent implementations of graphic output.
For example, for B2DPolygon and Win output, it allows
buffering the Gdiplus::GraphicsPath instead of re-
creating it all the time.
To support that, the change includes forwarding the
current transformation to the renderers in SalGraphics.
The current state in VCL is to transform all and
everything to device coordinates at every single
paint.
I have currently started to do this for ::drawPolyLine
implementations. The fallbacks for all systems will
at the start of that method just transform the data
to device coordinates, so all works as before.
This may also be done for FilledPolygon paint in a later
step, but most urgent is FatLine painting.
An arrangement of shared_ptr/weak_ptr is used so that
either the instance buffering (in the example B2DPolygon)
or the instance managing it can delete it. The instance
managing it currently uses a 1s Timer and a cycle-lifetime
management, but that can be extended in the future
to e.g. include size hints, too.
The mechanism it designed to support multiple Data per
buffering element, e.g. for B2DPolygon at the same time
system-dependent instances of Gdiplus and Cairo can be
buffered, but also PDF-data.
This is achieved semi-automatic by using
typeid(class).hash_code() as key for organization.
The mechanism will be used for now at B2DPolygon, but
is not limited to. There is already a similar but less
general buffer (see GdiPlusBuffer) that can and will
be converted to use this new mechanism.
Added vcl/headless Cairo renderer to support given
ObjectToDevice transformation (not to transform given
B2DPolygon)
Added support for CairoPath buffered at B2DPolygon,
seems to work well. Need to do more tests
Moved usage to templates suggested by Noel Grandin
(Noel Grandin <noelgrandin@gmail.com>), thanks for
these suggestions. Adapted Win usage to that, too.
Converted Win-specific GdiPlus BitmapBuffer to new
mechanism, works well. Checked, the manager holds
now a mix of bitmap and path data under Win
Added a cleanup mechanism to flush all buffered data
at DeInitVCL() using flushAll() at
SystemDependentDataBuffer
Adapted Linux-versions of ::drawPolyLine to support
PixelSnapHairline, for now in a simplified version
that still allows buffering. This will also be used
(and use buffering) for the Cairo-fallback in
X11SalGraphics
Change-Id: I88d7e438a20b96ddab7707050893bdd590c098c7
Reviewed-on: https://gerrit.libreoffice.org/59555
Tested-by: Armin Le Grand <Armin.Le.Grand@cib.de>
Reviewed-by: Armin Le Grand <Armin.Le.Grand@cib.de>
2018-08-24 13:01:08 +02:00
|
|
|
double fMiterMinimumAngle, bool bPixelSnapHairline)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
|
2017-10-30 20:31:42 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
// short circuit if there is nothing to do
|
Support buffering SystemDependent GraphicData
This is a first step to allow buffering of system
dependent data, especially (but not only) for the
system-dependent implementations of graphic output.
For example, for B2DPolygon and Win output, it allows
buffering the Gdiplus::GraphicsPath instead of re-
creating it all the time.
To support that, the change includes forwarding the
current transformation to the renderers in SalGraphics.
The current state in VCL is to transform all and
everything to device coordinates at every single
paint.
I have currently started to do this for ::drawPolyLine
implementations. The fallbacks for all systems will
at the start of that method just transform the data
to device coordinates, so all works as before.
This may also be done for FilledPolygon paint in a later
step, but most urgent is FatLine painting.
An arrangement of shared_ptr/weak_ptr is used so that
either the instance buffering (in the example B2DPolygon)
or the instance managing it can delete it. The instance
managing it currently uses a 1s Timer and a cycle-lifetime
management, but that can be extended in the future
to e.g. include size hints, too.
The mechanism it designed to support multiple Data per
buffering element, e.g. for B2DPolygon at the same time
system-dependent instances of Gdiplus and Cairo can be
buffered, but also PDF-data.
This is achieved semi-automatic by using
typeid(class).hash_code() as key for organization.
The mechanism will be used for now at B2DPolygon, but
is not limited to. There is already a similar but less
general buffer (see GdiPlusBuffer) that can and will
be converted to use this new mechanism.
Added vcl/headless Cairo renderer to support given
ObjectToDevice transformation (not to transform given
B2DPolygon)
Added support for CairoPath buffered at B2DPolygon,
seems to work well. Need to do more tests
Moved usage to templates suggested by Noel Grandin
(Noel Grandin <noelgrandin@gmail.com>), thanks for
these suggestions. Adapted Win usage to that, too.
Converted Win-specific GdiPlus BitmapBuffer to new
mechanism, works well. Checked, the manager holds
now a mix of bitmap and path data under Win
Added a cleanup mechanism to flush all buffered data
at DeInitVCL() using flushAll() at
SystemDependentDataBuffer
Adapted Linux-versions of ::drawPolyLine to support
PixelSnapHairline, for now in a simplified version
that still allows buffering. This will also be used
(and use buffering) for the Cairo-fallback in
X11SalGraphics
Change-Id: I88d7e438a20b96ddab7707050893bdd590c098c7
Reviewed-on: https://gerrit.libreoffice.org/59555
Tested-by: Armin Le Grand <Armin.Le.Grand@cib.de>
Reviewed-by: Armin Le Grand <Armin.Le.Grand@cib.de>
2018-08-24 13:01:08 +02:00
|
|
|
if (0 == rPolyLine.count())
|
|
|
|
{
|
2017-10-30 20:31:42 +01:00
|
|
|
return true;
|
Support buffering SystemDependent GraphicData
This is a first step to allow buffering of system
dependent data, especially (but not only) for the
system-dependent implementations of graphic output.
For example, for B2DPolygon and Win output, it allows
buffering the Gdiplus::GraphicsPath instead of re-
creating it all the time.
To support that, the change includes forwarding the
current transformation to the renderers in SalGraphics.
The current state in VCL is to transform all and
everything to device coordinates at every single
paint.
I have currently started to do this for ::drawPolyLine
implementations. The fallbacks for all systems will
at the start of that method just transform the data
to device coordinates, so all works as before.
This may also be done for FilledPolygon paint in a later
step, but most urgent is FatLine painting.
An arrangement of shared_ptr/weak_ptr is used so that
either the instance buffering (in the example B2DPolygon)
or the instance managing it can delete it. The instance
managing it currently uses a 1s Timer and a cycle-lifetime
management, but that can be extended in the future
to e.g. include size hints, too.
The mechanism it designed to support multiple Data per
buffering element, e.g. for B2DPolygon at the same time
system-dependent instances of Gdiplus and Cairo can be
buffered, but also PDF-data.
This is achieved semi-automatic by using
typeid(class).hash_code() as key for organization.
The mechanism will be used for now at B2DPolygon, but
is not limited to. There is already a similar but less
general buffer (see GdiPlusBuffer) that can and will
be converted to use this new mechanism.
Added vcl/headless Cairo renderer to support given
ObjectToDevice transformation (not to transform given
B2DPolygon)
Added support for CairoPath buffered at B2DPolygon,
seems to work well. Need to do more tests
Moved usage to templates suggested by Noel Grandin
(Noel Grandin <noelgrandin@gmail.com>), thanks for
these suggestions. Adapted Win usage to that, too.
Converted Win-specific GdiPlus BitmapBuffer to new
mechanism, works well. Checked, the manager holds
now a mix of bitmap and path data under Win
Added a cleanup mechanism to flush all buffered data
at DeInitVCL() using flushAll() at
SystemDependentDataBuffer
Adapted Linux-versions of ::drawPolyLine to support
PixelSnapHairline, for now in a simplified version
that still allows buffering. This will also be used
(and use buffering) for the Cairo-fallback in
X11SalGraphics
Change-Id: I88d7e438a20b96ddab7707050893bdd590c098c7
Reviewed-on: https://gerrit.libreoffice.org/59555
Tested-by: Armin Le Grand <Armin.Le.Grand@cib.de>
Reviewed-by: Armin Le Grand <Armin.Le.Grand@cib.de>
2018-08-24 13:01:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
|
|
|
|
basegfx::B2DPolygon aPolyLine(rPolyLine);
|
|
|
|
aPolyLine.transform(rObjectToDevice);
|
|
|
|
if (bPixelSnapHairline)
|
|
|
|
{
|
|
|
|
aPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine);
|
|
|
|
}
|
|
|
|
const basegfx::B2DVector aLineWidths(rObjectToDevice * rLineWidths);
|
2017-10-30 20:31:42 +01:00
|
|
|
|
|
|
|
// setup poly-polygon path
|
|
|
|
QPainterPath aPath;
|
Support buffering SystemDependent GraphicData
This is a first step to allow buffering of system
dependent data, especially (but not only) for the
system-dependent implementations of graphic output.
For example, for B2DPolygon and Win output, it allows
buffering the Gdiplus::GraphicsPath instead of re-
creating it all the time.
To support that, the change includes forwarding the
current transformation to the renderers in SalGraphics.
The current state in VCL is to transform all and
everything to device coordinates at every single
paint.
I have currently started to do this for ::drawPolyLine
implementations. The fallbacks for all systems will
at the start of that method just transform the data
to device coordinates, so all works as before.
This may also be done for FilledPolygon paint in a later
step, but most urgent is FatLine painting.
An arrangement of shared_ptr/weak_ptr is used so that
either the instance buffering (in the example B2DPolygon)
or the instance managing it can delete it. The instance
managing it currently uses a 1s Timer and a cycle-lifetime
management, but that can be extended in the future
to e.g. include size hints, too.
The mechanism it designed to support multiple Data per
buffering element, e.g. for B2DPolygon at the same time
system-dependent instances of Gdiplus and Cairo can be
buffered, but also PDF-data.
This is achieved semi-automatic by using
typeid(class).hash_code() as key for organization.
The mechanism will be used for now at B2DPolygon, but
is not limited to. There is already a similar but less
general buffer (see GdiPlusBuffer) that can and will
be converted to use this new mechanism.
Added vcl/headless Cairo renderer to support given
ObjectToDevice transformation (not to transform given
B2DPolygon)
Added support for CairoPath buffered at B2DPolygon,
seems to work well. Need to do more tests
Moved usage to templates suggested by Noel Grandin
(Noel Grandin <noelgrandin@gmail.com>), thanks for
these suggestions. Adapted Win usage to that, too.
Converted Win-specific GdiPlus BitmapBuffer to new
mechanism, works well. Checked, the manager holds
now a mix of bitmap and path data under Win
Added a cleanup mechanism to flush all buffered data
at DeInitVCL() using flushAll() at
SystemDependentDataBuffer
Adapted Linux-versions of ::drawPolyLine to support
PixelSnapHairline, for now in a simplified version
that still allows buffering. This will also be used
(and use buffering) for the Cairo-fallback in
X11SalGraphics
Change-Id: I88d7e438a20b96ddab7707050893bdd590c098c7
Reviewed-on: https://gerrit.libreoffice.org/59555
Tested-by: Armin Le Grand <Armin.Le.Grand@cib.de>
Reviewed-by: Armin Le Grand <Armin.Le.Grand@cib.de>
2018-08-24 13:01:08 +02:00
|
|
|
AddPolygonToPath(aPath, aPolyLine, aPolyLine.isClosed(), !getAntiAliasB2DDraw(), true);
|
2017-10-30 20:31:42 +01:00
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
Qt5Painter aPainter(*this, false, 255 * (1.0 - fTransparency));
|
2017-10-30 20:31:42 +01:00
|
|
|
|
|
|
|
// setup line attributes
|
|
|
|
QPen aPen = aPainter.pen();
|
Support buffering SystemDependent GraphicData
This is a first step to allow buffering of system
dependent data, especially (but not only) for the
system-dependent implementations of graphic output.
For example, for B2DPolygon and Win output, it allows
buffering the Gdiplus::GraphicsPath instead of re-
creating it all the time.
To support that, the change includes forwarding the
current transformation to the renderers in SalGraphics.
The current state in VCL is to transform all and
everything to device coordinates at every single
paint.
I have currently started to do this for ::drawPolyLine
implementations. The fallbacks for all systems will
at the start of that method just transform the data
to device coordinates, so all works as before.
This may also be done for FilledPolygon paint in a later
step, but most urgent is FatLine painting.
An arrangement of shared_ptr/weak_ptr is used so that
either the instance buffering (in the example B2DPolygon)
or the instance managing it can delete it. The instance
managing it currently uses a 1s Timer and a cycle-lifetime
management, but that can be extended in the future
to e.g. include size hints, too.
The mechanism it designed to support multiple Data per
buffering element, e.g. for B2DPolygon at the same time
system-dependent instances of Gdiplus and Cairo can be
buffered, but also PDF-data.
This is achieved semi-automatic by using
typeid(class).hash_code() as key for organization.
The mechanism will be used for now at B2DPolygon, but
is not limited to. There is already a similar but less
general buffer (see GdiPlusBuffer) that can and will
be converted to use this new mechanism.
Added vcl/headless Cairo renderer to support given
ObjectToDevice transformation (not to transform given
B2DPolygon)
Added support for CairoPath buffered at B2DPolygon,
seems to work well. Need to do more tests
Moved usage to templates suggested by Noel Grandin
(Noel Grandin <noelgrandin@gmail.com>), thanks for
these suggestions. Adapted Win usage to that, too.
Converted Win-specific GdiPlus BitmapBuffer to new
mechanism, works well. Checked, the manager holds
now a mix of bitmap and path data under Win
Added a cleanup mechanism to flush all buffered data
at DeInitVCL() using flushAll() at
SystemDependentDataBuffer
Adapted Linux-versions of ::drawPolyLine to support
PixelSnapHairline, for now in a simplified version
that still allows buffering. This will also be used
(and use buffering) for the Cairo-fallback in
X11SalGraphics
Change-Id: I88d7e438a20b96ddab7707050893bdd590c098c7
Reviewed-on: https://gerrit.libreoffice.org/59555
Tested-by: Armin Le Grand <Armin.Le.Grand@cib.de>
Reviewed-by: Armin Le Grand <Armin.Le.Grand@cib.de>
2018-08-24 13:01:08 +02:00
|
|
|
aPen.setWidth(aLineWidths.getX());
|
2017-10-30 20:31:42 +01:00
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
switch (eLineJoin)
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
case basegfx::B2DLineJoin::Bevel:
|
|
|
|
aPen.setJoinStyle(Qt::BevelJoin);
|
|
|
|
break;
|
|
|
|
case basegfx::B2DLineJoin::Round:
|
|
|
|
aPen.setJoinStyle(Qt::RoundJoin);
|
|
|
|
break;
|
2018-08-09 14:53:50 +02:00
|
|
|
case basegfx::B2DLineJoin::NONE:
|
2017-11-08 11:28:04 +01:00
|
|
|
case basegfx::B2DLineJoin::Miter:
|
|
|
|
aPen.setMiterLimit(1.0 / sin(fMiterMinimumAngle / 2.0));
|
|
|
|
aPen.setJoinStyle(Qt::MiterJoin);
|
|
|
|
break;
|
2017-10-30 20:31:42 +01:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
switch (eLineCap)
|
2017-10-30 20:31:42 +01:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
default: // css::drawing::LineCap_BUTT:
|
|
|
|
aPen.setCapStyle(Qt::FlatCap);
|
|
|
|
break;
|
|
|
|
case css::drawing::LineCap_ROUND:
|
|
|
|
aPen.setCapStyle(Qt::RoundCap);
|
|
|
|
break;
|
|
|
|
case css::drawing::LineCap_SQUARE:
|
|
|
|
aPen.setCapStyle(Qt::SquareCap);
|
|
|
|
break;
|
2017-10-30 20:31:42 +01:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
aPainter.setPen(aPen);
|
|
|
|
aPainter.drawPath(aPath);
|
|
|
|
aPainter.update(aPath.boundingRect());
|
2017-10-30 20:31:42 +01:00
|
|
|
return true;
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
bool Qt5Graphics::drawGradient(const tools::PolyPolygon&, const Gradient&) { return false; }
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2018-07-05 18:06:54 +02:00
|
|
|
void Qt5Graphics::drawScaledImage(const SalTwoRect& rPosAry, const QImage& rImage)
|
|
|
|
{
|
|
|
|
Qt5Painter aPainter(*this);
|
|
|
|
QRect aSrcRect(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight);
|
|
|
|
QRect aDestRect(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight);
|
|
|
|
aPainter.drawImage(aDestRect, rImage, aSrcRect);
|
|
|
|
aPainter.update(aDestRect);
|
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
void Qt5Graphics::copyArea(long nDestX, long nDestY, long nSrcX, long nSrcY, long nSrcWidth,
|
2018-01-08 09:38:24 +02:00
|
|
|
long nSrcHeight, bool /*bWindowInvalidate*/)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
if (nDestX == nSrcX && nDestY == nSrcY)
|
2017-10-30 18:33:06 +01:00
|
|
|
return;
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
SalTwoRect aTR(nSrcX, nSrcY, nSrcWidth, nSrcHeight, nDestX, nDestY, nSrcWidth, nSrcHeight);
|
|
|
|
copyBits(aTR, this);
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
void Qt5Graphics::copyBits(const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
if (rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0
|
|
|
|
|| rPosAry.mnDestHeight <= 0)
|
2017-10-30 18:33:06 +01:00
|
|
|
return;
|
|
|
|
|
2018-07-05 18:06:54 +02:00
|
|
|
QImage aImage, *pImage;
|
|
|
|
SalTwoRect aPosAry = rPosAry;
|
2017-11-08 11:28:04 +01:00
|
|
|
if (!pSrcGraphics || this == pSrcGraphics)
|
2017-10-30 18:33:06 +01:00
|
|
|
{
|
2018-07-06 13:12:05 +02:00
|
|
|
pImage = m_pQImage;
|
2017-11-08 11:28:04 +01:00
|
|
|
aImage
|
|
|
|
= pImage->copy(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight);
|
2018-07-05 18:06:54 +02:00
|
|
|
pImage = &aImage;
|
|
|
|
aPosAry.mnSrcX = 0;
|
|
|
|
aPosAry.mnSrcY = 0;
|
2017-10-30 18:33:06 +01:00
|
|
|
}
|
|
|
|
else
|
2017-11-08 11:28:04 +01:00
|
|
|
pImage = static_cast<Qt5Graphics*>(pSrcGraphics)->m_pQImage;
|
|
|
|
|
2018-07-05 18:06:54 +02:00
|
|
|
drawScaledImage(aPosAry, *pImage);
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
void Qt5Graphics::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
if (rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0
|
|
|
|
|| rPosAry.mnDestHeight <= 0)
|
2017-10-30 18:33:06 +01:00
|
|
|
return;
|
|
|
|
|
2018-07-05 17:45:14 +02:00
|
|
|
Qt5Bitmap aRGBABitmap;
|
|
|
|
if (rSalBitmap.GetBitCount() == 4)
|
|
|
|
aRGBABitmap.Create(rSalBitmap, 32);
|
|
|
|
const QImage* pImage = (rSalBitmap.GetBitCount() != 4)
|
|
|
|
? static_cast<const Qt5Bitmap*>(&rSalBitmap)->GetQImage()
|
|
|
|
: aRGBABitmap.GetQImage();
|
2017-11-08 11:28:04 +01:00
|
|
|
assert(pImage);
|
2017-10-30 18:33:06 +01:00
|
|
|
|
2018-07-05 18:06:54 +02:00
|
|
|
drawScaledImage(rPosAry, *pImage);
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2018-01-08 09:38:24 +02:00
|
|
|
void Qt5Graphics::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& /*rSalBitmap*/,
|
|
|
|
const SalBitmap& /*rTransparentBitmap*/)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
if (rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0
|
|
|
|
|| rPosAry.mnDestHeight <= 0)
|
2017-10-24 19:49:45 +02:00
|
|
|
return;
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
assert(rPosAry.mnSrcWidth == rPosAry.mnDestWidth);
|
|
|
|
assert(rPosAry.mnSrcHeight == rPosAry.mnDestHeight);
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2018-01-08 09:38:24 +02:00
|
|
|
void Qt5Graphics::drawMask(const SalTwoRect& rPosAry, const SalBitmap& /*rSalBitmap*/,
|
2018-03-14 11:11:02 +02:00
|
|
|
Color /*nMaskColor*/)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
if (rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0
|
|
|
|
|| rPosAry.mnDestHeight <= 0)
|
2017-10-24 19:49:45 +02:00
|
|
|
return;
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
assert(rPosAry.mnSrcWidth == rPosAry.mnDestWidth);
|
|
|
|
assert(rPosAry.mnSrcHeight == rPosAry.mnDestHeight);
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2018-06-08 12:29:09 +02:00
|
|
|
std::shared_ptr<SalBitmap> Qt5Graphics::getBitmap(long nX, long nY, long nWidth, long nHeight)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2018-06-08 12:29:09 +02:00
|
|
|
return std::make_shared<Qt5Bitmap>(m_pQImage->copy(nX, nY, nWidth, nHeight));
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2018-03-14 11:11:02 +02:00
|
|
|
Color Qt5Graphics::getPixel(long nX, long nY) { return m_pQImage->pixel(nX, nY); }
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2018-07-05 18:10:27 +02:00
|
|
|
void Qt5Graphics::invert(long nX, long nY, long nWidth, long nHeight, SalInvert nFlags)
|
2018-01-08 09:38:24 +02:00
|
|
|
{
|
2018-07-05 18:10:27 +02:00
|
|
|
Qt5Painter aPainter(*this);
|
|
|
|
if (SalInvert::N50 & nFlags)
|
|
|
|
{
|
|
|
|
aPainter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
|
2018-12-18 10:28:53 +00:00
|
|
|
QBrush aBrush(Qt::white, Qt::Dense4Pattern);
|
|
|
|
aPainter.fillRect(nX, nY, nWidth, nHeight, aBrush);
|
2018-07-05 18:10:27 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (SalInvert::TrackFrame & nFlags)
|
|
|
|
{
|
|
|
|
aPainter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
|
2018-12-18 10:28:53 +00:00
|
|
|
QPen aPen(Qt::white);
|
|
|
|
aPen.setStyle(Qt::DotLine);
|
|
|
|
aPainter.setPen(aPen);
|
2018-07-05 18:10:27 +02:00
|
|
|
aPainter.drawRect(nX, nY, nWidth, nHeight);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aPainter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
|
|
|
|
aPainter.fillRect(nX, nY, nWidth, nHeight, Qt::white);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
aPainter.update(nX, nY, nWidth, nHeight);
|
2018-01-08 09:38:24 +02:00
|
|
|
}
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2018-01-08 09:38:24 +02:00
|
|
|
void Qt5Graphics::invert(sal_uInt32 /*nPoints*/, const SalPoint* /*pPtAry*/, SalInvert /*nFlags*/)
|
|
|
|
{
|
|
|
|
}
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2018-01-08 09:38:24 +02:00
|
|
|
bool Qt5Graphics::drawEPS(long /*nX*/, long /*nY*/, long /*nWidth*/, long /*nHeight*/,
|
2019-02-25 14:30:01 +02:00
|
|
|
void* /*pPtr*/, sal_uInt32 /*nSize*/)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-01-08 09:38:24 +02:00
|
|
|
bool Qt5Graphics::blendBitmap(const SalTwoRect&, const SalBitmap& /*rBitmap*/) { return false; }
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2018-01-08 09:38:24 +02:00
|
|
|
bool Qt5Graphics::blendAlphaBitmap(const SalTwoRect&, const SalBitmap& /*rSrcBitmap*/,
|
|
|
|
const SalBitmap& /*rMaskBitmap*/,
|
|
|
|
const SalBitmap& /*rAlphaBitmap*/)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
static bool getAlphaImage(const SalBitmap& rSourceBitmap, const SalBitmap& rAlphaBitmap,
|
|
|
|
QImage& rAlphaImage)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-10-30 20:50:19 +01:00
|
|
|
if (rAlphaBitmap.GetBitCount() != 8 && rAlphaBitmap.GetBitCount() != 1)
|
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
SAL_WARN("vcl.gdi", "unsupported alpha depth case: " << rAlphaBitmap.GetBitCount());
|
2017-10-30 20:50:19 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-05 17:45:14 +02:00
|
|
|
Qt5Bitmap aRGBABitmap;
|
|
|
|
if (rSourceBitmap.GetBitCount() == 4)
|
|
|
|
aRGBABitmap.Create(rSourceBitmap, 32);
|
|
|
|
const QImage* pBitmap = (rSourceBitmap.GetBitCount() != 4)
|
|
|
|
? static_cast<const Qt5Bitmap*>(&rSourceBitmap)->GetQImage()
|
|
|
|
: aRGBABitmap.GetQImage();
|
2017-11-08 11:28:04 +01:00
|
|
|
const QImage* pAlpha = static_cast<const Qt5Bitmap*>(&rAlphaBitmap)->GetQImage();
|
|
|
|
rAlphaImage = pBitmap->convertToFormat(Qt5_DefaultFormat32);
|
2017-10-30 20:50:19 +01:00
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
if (rAlphaBitmap.GetBitCount() == 8)
|
2017-10-30 20:50:19 +01:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
for (int y = 0; y < rAlphaImage.height(); ++y)
|
2017-10-30 20:50:19 +01:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
uchar* image_line = rAlphaImage.scanLine(y);
|
|
|
|
const uchar* alpha_line = pAlpha->scanLine(y);
|
|
|
|
for (int x = 0; x < rAlphaImage.width(); ++x, image_line += 4)
|
|
|
|
image_line[3] = 255 - alpha_line[x];
|
2017-10-30 20:50:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
for (int y = 0; y < rAlphaImage.height(); ++y)
|
2017-10-30 20:50:19 +01:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
uchar* image_line = rAlphaImage.scanLine(y);
|
|
|
|
const uchar* alpha_line = pAlpha->scanLine(y);
|
|
|
|
for (int x = 0; x < rAlphaImage.width(); ++x, image_line += 4)
|
2017-10-30 20:50:19 +01:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
if (x && !(x % 8))
|
2017-10-30 20:50:19 +01:00
|
|
|
++alpha_line;
|
2018-07-05 18:15:50 +02:00
|
|
|
if (0 != (*alpha_line & (1 << (7 - x % 8))))
|
2017-12-09 23:12:02 +00:00
|
|
|
image_line[3] = 0;
|
2017-10-30 20:50:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
bool Qt5Graphics::drawAlphaBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSourceBitmap,
|
|
|
|
const SalBitmap& rAlphaBitmap)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-10-30 20:50:19 +01:00
|
|
|
QImage aImage;
|
2017-11-08 11:28:04 +01:00
|
|
|
if (!getAlphaImage(rSourceBitmap, rAlphaBitmap, aImage))
|
2017-10-30 20:50:19 +01:00
|
|
|
return false;
|
2018-07-05 18:06:54 +02:00
|
|
|
drawScaledImage(rPosAry, aImage);
|
2017-10-30 20:50:19 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
bool Qt5Graphics::drawTransformedBitmap(const basegfx::B2DPoint& rNull, const basegfx::B2DPoint& rX,
|
|
|
|
const basegfx::B2DPoint& rY, const SalBitmap& rSourceBitmap,
|
|
|
|
const SalBitmap* pAlphaBitmap)
|
2017-10-30 20:50:19 +01:00
|
|
|
{
|
|
|
|
QImage aImage;
|
2017-11-08 11:28:04 +01:00
|
|
|
if (pAlphaBitmap && !getAlphaImage(rSourceBitmap, *pAlphaBitmap, aImage))
|
2017-10-30 20:50:19 +01:00
|
|
|
return false;
|
|
|
|
else
|
|
|
|
{
|
2018-07-05 17:45:14 +02:00
|
|
|
Qt5Bitmap aRGBABitmap;
|
|
|
|
if (rSourceBitmap.GetBitCount() == 4)
|
|
|
|
aRGBABitmap.Create(rSourceBitmap, 32);
|
|
|
|
const QImage* pBitmap = (rSourceBitmap.GetBitCount() != 4)
|
|
|
|
? static_cast<const Qt5Bitmap*>(&rSourceBitmap)->GetQImage()
|
|
|
|
: aRGBABitmap.GetQImage();
|
2017-11-08 11:28:04 +01:00
|
|
|
aImage = pBitmap->convertToFormat(Qt5_DefaultFormat32);
|
2017-10-30 20:50:19 +01:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
Qt5Painter aPainter(*this);
|
2017-10-30 20:50:19 +01:00
|
|
|
const basegfx::B2DVector aXRel = rX - rNull;
|
|
|
|
const basegfx::B2DVector aYRel = rY - rNull;
|
2017-11-08 11:28:04 +01:00
|
|
|
aPainter.setTransform(QTransform(aXRel.getX() / aImage.width(), aXRel.getY() / aImage.width(),
|
|
|
|
aYRel.getX() / aImage.height(), aYRel.getY() / aImage.height(),
|
|
|
|
rNull.getX(), rNull.getY()));
|
|
|
|
aPainter.drawImage(QPoint(0, 0), aImage);
|
|
|
|
aPainter.update(aImage.rect());
|
2017-10-30 20:50:19 +01:00
|
|
|
return true;
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
bool Qt5Graphics::drawAlphaRect(long nX, long nY, long nWidth, long nHeight,
|
|
|
|
sal_uInt8 nTransparency)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
|
2017-10-30 20:50:19 +01:00
|
|
|
return true;
|
2018-08-09 14:53:50 +02:00
|
|
|
assert(nTransparency <= 100);
|
|
|
|
if (nTransparency > 100)
|
|
|
|
nTransparency = 100;
|
|
|
|
Qt5Painter aPainter(*this, true, (100 - nTransparency) * (255.0 / 100));
|
2017-11-08 11:28:04 +01:00
|
|
|
if (SALCOLOR_NONE != m_aFillColor)
|
|
|
|
aPainter.fillRect(nX, nY, nWidth, nHeight, aPainter.brush());
|
2018-07-05 18:15:50 +02:00
|
|
|
if (SALCOLOR_NONE != m_aLineColor)
|
2017-11-08 11:28:04 +01:00
|
|
|
aPainter.drawRect(nX, nY, nWidth, nHeight);
|
|
|
|
aPainter.update(nX, nY, nWidth, nHeight);
|
2017-10-30 20:50:19 +01:00
|
|
|
return true;
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
void Qt5Graphics::GetResolution(sal_Int32& rDPIX, sal_Int32& rDPIY)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-10-30 18:33:06 +01:00
|
|
|
char* pForceDpi;
|
|
|
|
if ((pForceDpi = getenv("SAL_FORCEDPI")))
|
|
|
|
{
|
|
|
|
OString sForceDPI(pForceDpi);
|
|
|
|
rDPIX = rDPIY = sForceDPI.toInt32();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
if (!m_pFrame || !m_pFrame->GetQWidget()->window()->windowHandle())
|
2017-10-30 18:33:06 +01:00
|
|
|
return;
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
QScreen* pScreen = m_pFrame->GetQWidget()->window()->windowHandle()->screen();
|
2019-03-29 02:58:59 +01:00
|
|
|
rDPIX = pScreen->logicalDotsPerInchX() * pScreen->devicePixelRatio() + 0.5;
|
|
|
|
rDPIY = pScreen->logicalDotsPerInchY() * pScreen->devicePixelRatio() + 0.5;
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
sal_uInt16 Qt5Graphics::GetBitCount() const { return getFormatBits(m_pQImage->format()); }
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
long Qt5Graphics::GetGraphicsWidth() const { return m_pQImage->width(); }
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
void Qt5Graphics::SetLineColor() { m_aLineColor = SALCOLOR_NONE; }
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2018-03-14 11:11:02 +02:00
|
|
|
void Qt5Graphics::SetLineColor(Color nColor) { m_aLineColor = nColor; }
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2017-11-08 11:28:04 +01:00
|
|
|
void Qt5Graphics::SetFillColor() { m_aFillColor = SALCOLOR_NONE; }
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2018-03-14 11:11:02 +02:00
|
|
|
void Qt5Graphics::SetFillColor(Color nColor) { m_aFillColor = nColor; }
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2018-12-05 09:42:53 +01:00
|
|
|
void Qt5Graphics::SetXORMode(bool bSet, bool)
|
2017-10-21 13:50:30 +00:00
|
|
|
{
|
2017-11-08 11:28:04 +01:00
|
|
|
if (bSet)
|
2017-10-30 20:19:45 +01:00
|
|
|
m_eCompositionMode = QPainter::CompositionMode_Xor;
|
|
|
|
else
|
|
|
|
m_eCompositionMode = QPainter::CompositionMode_SourceOver;
|
2017-10-21 13:50:30 +00:00
|
|
|
}
|
|
|
|
|
2018-01-08 09:38:24 +02:00
|
|
|
void Qt5Graphics::SetROPLineColor(SalROPColor /*nROPColor*/) {}
|
2017-10-21 13:50:30 +00:00
|
|
|
|
2018-01-08 09:38:24 +02:00
|
|
|
void Qt5Graphics::SetROPFillColor(SalROPColor /*nROPColor*/) {}
|
2017-10-21 13:50:30 +00:00
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|