1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "Qt5Graphics.hxx"
22 #include "Qt5Bitmap.hxx"
23 #include "Qt5Painter.hxx"
25 #include <QtGui/QPainter>
26 #include <QtGui/QScreen>
27 #include <QtGui/QWindow>
28 #include <QtWidgets/QWidget>
30 static const basegfx::B2DPoint
aHalfPointOfs(0.5, 0.5);
32 static void AddPolygonToPath(QPainterPath
& rPath
, const basegfx::B2DPolygon
& rPolygon
,
33 bool bClosePath
, bool bPixelSnap
, bool bLineDraw
)
35 // short circuit if there is nothing to do
36 const int nPointCount
= rPolygon
.count();
40 const bool bHasCurves
= rPolygon
.areControlPointsUsed();
41 for (int nPointIdx
= 0, nPrevIdx
= 0;; nPrevIdx
= nPointIdx
++)
43 int nClosedIdx
= nPointIdx
;
44 if (nPointIdx
>= nPointCount
)
46 // prepare to close last curve segment if needed
47 if (bClosePath
&& (nPointIdx
== nPointCount
))
53 basegfx::B2DPoint aPoint
= rPolygon
.getB2DPoint(nClosedIdx
);
57 // snap device coordinates to full pixels
58 aPoint
.setX(basegfx::fround(aPoint
.getX()));
59 aPoint
.setY(basegfx::fround(aPoint
.getY()));
63 aPoint
+= aHalfPointOfs
;
66 // first point => just move there
67 rPath
.moveTo(aPoint
.getX(), aPoint
.getY());
71 bool bPendingCurve
= false;
74 bPendingCurve
= rPolygon
.isNextControlPointUsed(nPrevIdx
);
75 bPendingCurve
|= rPolygon
.isPrevControlPointUsed(nClosedIdx
);
78 if (!bPendingCurve
) // line segment
79 rPath
.lineTo(aPoint
.getX(), aPoint
.getY());
80 else // cubic bezier segment
82 basegfx::B2DPoint aCP1
= rPolygon
.getNextControlPoint(nPrevIdx
);
83 basegfx::B2DPoint aCP2
= rPolygon
.getPrevControlPoint(nClosedIdx
);
86 aCP1
+= aHalfPointOfs
;
87 aCP2
+= aHalfPointOfs
;
89 rPath
.cubicTo(aCP1
.getX(), aCP1
.getY(), aCP2
.getX(), aCP2
.getY(), aPoint
.getX(),
98 static bool AddPolyPolygonToPath(QPainterPath
& rPath
, const basegfx::B2DPolyPolygon
& rPolyPoly
,
99 bool bPixelSnap
, bool bLineDraw
)
101 const int nPolyCount
= rPolyPoly
.count();
104 for (int nPolyIdx
= 0; nPolyIdx
< nPolyCount
; ++nPolyIdx
)
106 const basegfx::B2DPolygon rPolygon
= rPolyPoly
.getB2DPolygon(nPolyIdx
);
107 AddPolygonToPath(rPath
, rPolygon
, true, bPixelSnap
, bLineDraw
);
112 bool Qt5Graphics::setClipRegion(const vcl::Region
& rRegion
)
114 if (rRegion
.IsRectangle())
116 m_aClipRegion
= toQRect(rRegion
.GetBoundRect());
117 if (!m_aClipPath
.isEmpty())
120 m_aClipPath
.swap(aPath
);
123 else if (!rRegion
.HasPolyPolygonOrB2DPolyPolygon())
126 RectangleVector aRectangles
;
127 rRegion
.GetRegionRectangles(aRectangles
);
128 for (auto& rRect
: aRectangles
)
129 aQRegion
+= toQRect(rRect
);
130 m_aClipRegion
= aQRegion
;
131 if (!m_aClipPath
.isEmpty())
134 m_aClipPath
.swap(aPath
);
140 const basegfx::B2DPolyPolygon
aPolyClip(rRegion
.GetAsB2DPolyPolygon());
141 AddPolyPolygonToPath(aPath
, aPolyClip
, !getAntiAliasB2DDraw(), false);
142 m_aClipPath
.swap(aPath
);
143 if (!m_aClipRegion
.isEmpty())
146 m_aClipRegion
.swap(aRegion
);
152 void Qt5Graphics::ResetClipRegion()
155 m_aClipRegion
= QRegion(m_pQImage
->rect());
157 m_aClipRegion
= QRegion();
158 if (!m_aClipPath
.isEmpty())
161 m_aClipPath
.swap(aPath
);
165 void Qt5Graphics::drawPixel(long nX
, long nY
)
167 Qt5Painter
aPainter(*this);
168 aPainter
.drawPoint(nX
, nY
);
169 aPainter
.update(nX
, nY
, 1, 1);
172 void Qt5Graphics::drawPixel(long nX
, long nY
, Color nColor
)
174 Qt5Painter
aPainter(*this);
175 aPainter
.setPen(QColor(QRgb(nColor
)));
176 aPainter
.setPen(Qt::SolidLine
);
177 aPainter
.drawPoint(nX
, nY
);
178 aPainter
.update(nX
, nY
, 1, 1);
181 void Qt5Graphics::drawLine(long nX1
, long nY1
, long nX2
, long nY2
)
183 Qt5Painter
aPainter(*this);
184 aPainter
.drawLine(nX1
, nY1
, nX2
, nY2
);
199 aPainter
.update(nX1
, nY1
, nX2
- nX1
, nY2
- nY1
);
202 void Qt5Graphics::drawRect(long nX
, long nY
, long nWidth
, long nHeight
)
204 if (SALCOLOR_NONE
== m_aFillColor
&& SALCOLOR_NONE
== m_aLineColor
)
207 Qt5Painter
aPainter(*this, true);
208 if (SALCOLOR_NONE
!= m_aFillColor
)
209 aPainter
.fillRect(nX
, nY
, nWidth
, nHeight
, aPainter
.brush());
211 aPainter
.drawRect(nX
, nY
, nWidth
, nHeight
);
212 aPainter
.update(nX
, nY
, nWidth
, nHeight
);
215 void Qt5Graphics::drawPolyLine(sal_uInt32 nPoints
, const SalPoint
* pPtAry
)
220 Qt5Painter
aPainter(*this);
221 QPoint
* pPoints
= new QPoint
[nPoints
];
222 QPoint
aTopLeft(pPtAry
->mnX
, pPtAry
->mnY
);
223 QPoint aBottomRight
= aTopLeft
;
224 for (sal_uInt32 i
= 0; i
< nPoints
; ++i
, ++pPtAry
)
226 pPoints
[i
] = QPoint(pPtAry
->mnX
, pPtAry
->mnY
);
227 if (pPtAry
->mnX
< aTopLeft
.x())
228 aTopLeft
.setX(pPtAry
->mnX
);
229 if (pPtAry
->mnY
< aTopLeft
.y())
230 aTopLeft
.setY(pPtAry
->mnY
);
231 if (pPtAry
->mnX
> aBottomRight
.x())
232 aBottomRight
.setX(pPtAry
->mnX
);
233 if (pPtAry
->mnY
> aBottomRight
.y())
234 aBottomRight
.setY(pPtAry
->mnY
);
236 aPainter
.drawPolyline(pPoints
, nPoints
);
238 aPainter
.update(QRect(aTopLeft
, aBottomRight
));
241 void Qt5Graphics::drawPolygon(sal_uInt32 nPoints
, const SalPoint
* pPtAry
)
243 Qt5Painter
aPainter(*this, true);
244 QPolygon
aPolygon(nPoints
);
245 for (sal_uInt32 i
= 0; i
< nPoints
; ++i
, ++pPtAry
)
246 aPolygon
.setPoint(i
, pPtAry
->mnX
, pPtAry
->mnY
);
247 aPainter
.drawPolygon(aPolygon
);
248 aPainter
.update(aPolygon
.boundingRect());
251 void Qt5Graphics::drawPolyPolygon(sal_uInt32
/*nPoly*/, const sal_uInt32
* /*pPoints*/,
252 PCONSTSALPOINT
* /*pPtAry*/)
256 bool Qt5Graphics::drawPolyPolygon(const basegfx::B2DPolyPolygon
& rPolyPoly
, double fTransparency
)
258 // ignore invisible polygons
259 if (SALCOLOR_NONE
== m_aFillColor
&& SALCOLOR_NONE
== m_aLineColor
)
261 if ((fTransparency
>= 1.0) || (fTransparency
< 0))
265 // ignore empty polygons
266 if (!AddPolyPolygonToPath(aPath
, rPolyPoly
, !getAntiAliasB2DDraw(),
267 m_aLineColor
!= SALCOLOR_NONE
))
270 Qt5Painter
aPainter(*this, true, 255 * (1.0 - fTransparency
));
271 aPainter
.drawPath(aPath
);
272 aPainter
.update(aPath
.boundingRect());
276 bool Qt5Graphics::drawPolyLineBezier(sal_uInt32
/*nPoints*/, const SalPoint
* /*pPtAry*/,
277 const PolyFlags
* /*pFlgAry*/)
282 bool Qt5Graphics::drawPolygonBezier(sal_uInt32
/*nPoints*/, const SalPoint
* /*pPtAry*/,
283 const PolyFlags
* /*pFlgAry*/)
288 bool Qt5Graphics::drawPolyPolygonBezier(sal_uInt32
/*nPoly*/, const sal_uInt32
* /*pPoints*/,
289 const SalPoint
* const* /*pPtAry*/,
290 const PolyFlags
* const* /*pFlgAry*/)
295 bool Qt5Graphics::drawPolyLine(const basegfx::B2DPolygon
& rPolyLine
, double fTransparency
,
296 const basegfx::B2DVector
& rLineWidths
,
297 basegfx::B2DLineJoin eLineJoin
, css::drawing::LineCap eLineCap
,
298 double fMiterMinimumAngle
)
300 if (SALCOLOR_NONE
== m_aFillColor
&& SALCOLOR_NONE
== m_aLineColor
)
303 if (basegfx::B2DLineJoin::NONE
== eLineJoin
)
306 // short circuit if there is nothing to do
307 const int nPointCount
= rPolyLine
.count();
308 if (nPointCount
<= 0)
311 // setup poly-polygon path
313 AddPolygonToPath(aPath
, rPolyLine
, rPolyLine
.isClosed(), !getAntiAliasB2DDraw(), true);
315 Qt5Painter
aPainter(*this, false, 255 * (1.0 - fTransparency
));
317 // setup line attributes
318 QPen aPen
= aPainter
.pen();
319 aPen
.setWidth(rLineWidths
.getX());
323 case basegfx::B2DLineJoin::NONE
:
326 case basegfx::B2DLineJoin::Bevel
:
327 aPen
.setJoinStyle(Qt::BevelJoin
);
329 case basegfx::B2DLineJoin::Round
:
330 aPen
.setJoinStyle(Qt::RoundJoin
);
332 case basegfx::B2DLineJoin::Miter
:
333 aPen
.setMiterLimit(1.0 / sin(fMiterMinimumAngle
/ 2.0));
334 aPen
.setJoinStyle(Qt::MiterJoin
);
340 default: // css::drawing::LineCap_BUTT:
341 aPen
.setCapStyle(Qt::FlatCap
);
343 case css::drawing::LineCap_ROUND
:
344 aPen
.setCapStyle(Qt::RoundCap
);
346 case css::drawing::LineCap_SQUARE
:
347 aPen
.setCapStyle(Qt::SquareCap
);
351 aPainter
.setPen(aPen
);
352 aPainter
.drawPath(aPath
);
353 aPainter
.update(aPath
.boundingRect());
357 bool Qt5Graphics::drawGradient(const tools::PolyPolygon
&, const Gradient
&) { return false; }
359 void Qt5Graphics::copyArea(long nDestX
, long nDestY
, long nSrcX
, long nSrcY
, long nSrcWidth
,
360 long nSrcHeight
, bool /*bWindowInvalidate*/)
362 if (nDestX
== nSrcX
&& nDestY
== nSrcY
)
365 SalTwoRect
aTR(nSrcX
, nSrcY
, nSrcWidth
, nSrcHeight
, nDestX
, nDestY
, nSrcWidth
, nSrcHeight
);
369 void Qt5Graphics::copyBits(const SalTwoRect
& rPosAry
, SalGraphics
* pSrcGraphics
)
371 if (rPosAry
.mnSrcWidth
<= 0 || rPosAry
.mnSrcHeight
<= 0 || rPosAry
.mnDestWidth
<= 0
372 || rPosAry
.mnDestHeight
<= 0)
375 assert(rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
);
376 assert(rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
);
378 QImage aImage
, *pImage
= &aImage
;
379 if (!pSrcGraphics
|| this == pSrcGraphics
)
381 if (rPosAry
.mnDestX
== rPosAry
.mnSrcX
&& rPosAry
.mnDestY
== rPosAry
.mnSrcY
)
384 = pImage
->copy(rPosAry
.mnSrcX
, rPosAry
.mnSrcY
, rPosAry
.mnSrcWidth
, rPosAry
.mnSrcHeight
);
387 pImage
= static_cast<Qt5Graphics
*>(pSrcGraphics
)->m_pQImage
;
389 Qt5Painter
aPainter(*this);
391 QPoint(rPosAry
.mnDestX
, rPosAry
.mnDestY
), *pImage
,
392 QRect(rPosAry
.mnSrcX
, rPosAry
.mnSrcY
, rPosAry
.mnSrcWidth
, rPosAry
.mnSrcHeight
));
393 aPainter
.update(rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnDestWidth
, rPosAry
.mnDestHeight
);
396 void Qt5Graphics::drawBitmap(const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
)
398 if (rPosAry
.mnSrcWidth
<= 0 || rPosAry
.mnSrcHeight
<= 0 || rPosAry
.mnDestWidth
<= 0
399 || rPosAry
.mnDestHeight
<= 0)
402 assert(rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
);
403 assert(rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
);
405 Qt5Painter
aPainter(*this);
407 const QImage
* pImage
= static_cast<const Qt5Bitmap
*>(&rSalBitmap
)->GetQImage();
411 QPoint(rPosAry
.mnDestX
, rPosAry
.mnDestY
), *pImage
,
412 QRect(rPosAry
.mnSrcX
, rPosAry
.mnSrcY
, rPosAry
.mnSrcWidth
, rPosAry
.mnSrcHeight
));
413 aPainter
.update(rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnDestWidth
, rPosAry
.mnDestHeight
);
416 void Qt5Graphics::drawBitmap(const SalTwoRect
& rPosAry
, const SalBitmap
& /*rSalBitmap*/,
417 const SalBitmap
& /*rTransparentBitmap*/)
419 if (rPosAry
.mnSrcWidth
<= 0 || rPosAry
.mnSrcHeight
<= 0 || rPosAry
.mnDestWidth
<= 0
420 || rPosAry
.mnDestHeight
<= 0)
423 assert(rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
);
424 assert(rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
);
427 void Qt5Graphics::drawMask(const SalTwoRect
& rPosAry
, const SalBitmap
& /*rSalBitmap*/,
428 Color
/*nMaskColor*/)
430 if (rPosAry
.mnSrcWidth
<= 0 || rPosAry
.mnSrcHeight
<= 0 || rPosAry
.mnDestWidth
<= 0
431 || rPosAry
.mnDestHeight
<= 0)
434 assert(rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
);
435 assert(rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
);
438 SalBitmap
* Qt5Graphics::getBitmap(long nX
, long nY
, long nWidth
, long nHeight
)
440 return new Qt5Bitmap(m_pQImage
->copy(nX
, nY
, nWidth
, nHeight
));
443 Color
Qt5Graphics::getPixel(long nX
, long nY
) { return m_pQImage
->pixel(nX
, nY
); }
445 void Qt5Graphics::invert(long /*nX*/, long /*nY*/, long /*nWidth*/, long /*nHeight*/,
446 SalInvert
/*nFlags*/)
450 void Qt5Graphics::invert(sal_uInt32
/*nPoints*/, const SalPoint
* /*pPtAry*/, SalInvert
/*nFlags*/)
454 bool Qt5Graphics::drawEPS(long /*nX*/, long /*nY*/, long /*nWidth*/, long /*nHeight*/,
455 void* /*pPtr*/, sal_uLong
/*nSize*/)
460 bool Qt5Graphics::blendBitmap(const SalTwoRect
&, const SalBitmap
& /*rBitmap*/) { return false; }
462 bool Qt5Graphics::blendAlphaBitmap(const SalTwoRect
&, const SalBitmap
& /*rSrcBitmap*/,
463 const SalBitmap
& /*rMaskBitmap*/,
464 const SalBitmap
& /*rAlphaBitmap*/)
469 static bool getAlphaImage(const SalBitmap
& rSourceBitmap
, const SalBitmap
& rAlphaBitmap
,
472 if (rAlphaBitmap
.GetBitCount() != 8 && rAlphaBitmap
.GetBitCount() != 1)
474 SAL_WARN("vcl.gdi", "unsupported alpha depth case: " << rAlphaBitmap
.GetBitCount());
478 const QImage
* pBitmap
= static_cast<const Qt5Bitmap
*>(&rSourceBitmap
)->GetQImage();
479 const QImage
* pAlpha
= static_cast<const Qt5Bitmap
*>(&rAlphaBitmap
)->GetQImage();
480 rAlphaImage
= pBitmap
->convertToFormat(Qt5_DefaultFormat32
);
482 if (rAlphaBitmap
.GetBitCount() == 8)
484 for (int y
= 0; y
< rAlphaImage
.height(); ++y
)
486 uchar
* image_line
= rAlphaImage
.scanLine(y
);
487 const uchar
* alpha_line
= pAlpha
->scanLine(y
);
488 for (int x
= 0; x
< rAlphaImage
.width(); ++x
, image_line
+= 4)
489 image_line
[3] = 255 - alpha_line
[x
];
494 for (int y
= 0; y
< rAlphaImage
.height(); ++y
)
496 uchar
* image_line
= rAlphaImage
.scanLine(y
);
497 const uchar
* alpha_line
= pAlpha
->scanLine(y
);
498 for (int x
= 0; x
< rAlphaImage
.width(); ++x
, image_line
+= 4)
502 if (0 == (*alpha_line
& (1 << (x
% 8))))
511 bool Qt5Graphics::drawAlphaBitmap(const SalTwoRect
& rPosAry
, const SalBitmap
& rSourceBitmap
,
512 const SalBitmap
& rAlphaBitmap
)
515 if (!getAlphaImage(rSourceBitmap
, rAlphaBitmap
, aImage
))
518 Qt5Painter
aPainter(*this);
520 QPoint(rPosAry
.mnDestX
, rPosAry
.mnDestY
), aImage
,
521 QRect(rPosAry
.mnSrcX
, rPosAry
.mnSrcY
, rPosAry
.mnSrcWidth
, rPosAry
.mnSrcHeight
));
522 aPainter
.update(rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnDestWidth
, rPosAry
.mnDestHeight
);
526 bool Qt5Graphics::drawTransformedBitmap(const basegfx::B2DPoint
& rNull
, const basegfx::B2DPoint
& rX
,
527 const basegfx::B2DPoint
& rY
, const SalBitmap
& rSourceBitmap
,
528 const SalBitmap
* pAlphaBitmap
)
531 if (pAlphaBitmap
&& !getAlphaImage(rSourceBitmap
, *pAlphaBitmap
, aImage
))
535 const QImage
* pBitmap
= static_cast<const Qt5Bitmap
*>(&rSourceBitmap
)->GetQImage();
536 aImage
= pBitmap
->convertToFormat(Qt5_DefaultFormat32
);
539 Qt5Painter
aPainter(*this);
540 const basegfx::B2DVector aXRel
= rX
- rNull
;
541 const basegfx::B2DVector aYRel
= rY
- rNull
;
542 aPainter
.setTransform(QTransform(aXRel
.getX() / aImage
.width(), aXRel
.getY() / aImage
.width(),
543 aYRel
.getX() / aImage
.height(), aYRel
.getY() / aImage
.height(),
544 rNull
.getX(), rNull
.getY()));
545 aPainter
.drawImage(QPoint(0, 0), aImage
);
546 aPainter
.update(aImage
.rect());
550 bool Qt5Graphics::drawAlphaRect(long nX
, long nY
, long nWidth
, long nHeight
,
551 sal_uInt8 nTransparency
)
553 if (SALCOLOR_NONE
== m_aFillColor
&& SALCOLOR_NONE
== m_aLineColor
)
556 Qt5Painter
aPainter(*this, true, nTransparency
);
557 if (SALCOLOR_NONE
!= m_aFillColor
)
558 aPainter
.fillRect(nX
, nY
, nWidth
, nHeight
, aPainter
.brush());
560 aPainter
.drawRect(nX
, nY
, nWidth
, nHeight
);
561 aPainter
.update(nX
, nY
, nWidth
, nHeight
);
565 void Qt5Graphics::GetResolution(sal_Int32
& rDPIX
, sal_Int32
& rDPIY
)
568 if ((pForceDpi
= getenv("SAL_FORCEDPI")))
570 OString
sForceDPI(pForceDpi
);
571 rDPIX
= rDPIY
= sForceDPI
.toInt32();
575 if (!m_pFrame
|| !m_pFrame
->GetQWidget()->window()->windowHandle())
578 QScreen
* pScreen
= m_pFrame
->GetQWidget()->window()->windowHandle()->screen();
579 rDPIX
= pScreen
->physicalDotsPerInchX();
580 rDPIY
= pScreen
->physicalDotsPerInchY();
583 sal_uInt16
Qt5Graphics::GetBitCount() const { return getFormatBits(m_pQImage
->format()); }
585 long Qt5Graphics::GetGraphicsWidth() const { return m_pQImage
->width(); }
587 void Qt5Graphics::SetLineColor() { m_aLineColor
= SALCOLOR_NONE
; }
589 void Qt5Graphics::SetLineColor(Color nColor
) { m_aLineColor
= nColor
; }
591 void Qt5Graphics::SetFillColor() { m_aFillColor
= SALCOLOR_NONE
; }
593 void Qt5Graphics::SetFillColor(Color nColor
) { m_aFillColor
= nColor
; }
595 void Qt5Graphics::SetXORMode(bool bSet
)
598 m_eCompositionMode
= QPainter::CompositionMode_Xor
;
600 m_eCompositionMode
= QPainter::CompositionMode_SourceOver
;
603 void Qt5Graphics::SetROPLineColor(SalROPColor
/*nROPColor*/) {}
605 void Qt5Graphics::SetROPFillColor(SalROPColor
/*nROPColor*/) {}
607 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */