1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <Qt5Graphics.hxx>
21 
22 #include <Qt5Bitmap.hxx>
23 #include <Qt5Painter.hxx>
24 
25 #include <sal/log.hxx>
26 
27 #include <QtGui/QPainter>
28 #include <QtGui/QScreen>
29 #include <QtGui/QWindow>
30 #include <QtWidgets/QWidget>
31 
32 #include <numeric>
33 #include <basegfx/polygon/b2dpolygontools.hxx>
34 #include <basegfx/polygon/b2dpolypolygontools.hxx>
35 
Qt5GraphicsBackend(Qt5Frame * pFrame,QImage * pQImage)36 Qt5GraphicsBackend::Qt5GraphicsBackend(Qt5Frame* pFrame, QImage* pQImage)
37     : m_pFrame(pFrame)
38     , m_pQImage(pQImage)
39     , m_aLineColor(0x00, 0x00, 0x00)
40     , m_aFillColor(0xFF, 0xFF, 0XFF)
41     , m_eCompositionMode(QPainter::CompositionMode_SourceOver)
42 {
43     ResetClipRegion();
44 }
45 
~Qt5GraphicsBackend()46 Qt5GraphicsBackend::~Qt5GraphicsBackend() {}
47 
48 const basegfx::B2DPoint aHalfPointOfs(0.5, 0.5);
49 
AddPolygonToPath(QPainterPath & rPath,const basegfx::B2DPolygon & rPolygon,bool bClosePath,bool bPixelSnap,bool bLineDraw)50 static void AddPolygonToPath(QPainterPath& rPath, const basegfx::B2DPolygon& rPolygon,
51                              bool bClosePath, bool bPixelSnap, bool bLineDraw)
52 {
53     const int nPointCount = rPolygon.count();
54     // short circuit if there is nothing to do
55     if (nPointCount == 0)
56         return;
57 
58     const bool bHasCurves = rPolygon.areControlPointsUsed();
59     for (int nPointIdx = 0, nPrevIdx = 0;; nPrevIdx = nPointIdx++)
60     {
61         int nClosedIdx = nPointIdx;
62         if (nPointIdx >= nPointCount)
63         {
64             // prepare to close last curve segment if needed
65             if (bClosePath && (nPointIdx == nPointCount))
66                 nClosedIdx = 0;
67             else
68                 break;
69         }
70 
71         basegfx::B2DPoint aPoint = rPolygon.getB2DPoint(nClosedIdx);
72 
73         if (bPixelSnap)
74         {
75             // snap device coordinates to full pixels
76             aPoint.setX(basegfx::fround(aPoint.getX()));
77             aPoint.setY(basegfx::fround(aPoint.getY()));
78         }
79 
80         if (bLineDraw)
81             aPoint += aHalfPointOfs;
82         if (!nPointIdx)
83         {
84             // first point => just move there
85             rPath.moveTo(aPoint.getX(), aPoint.getY());
86             continue;
87         }
88 
89         bool bPendingCurve = false;
90         if (bHasCurves)
91         {
92             bPendingCurve = rPolygon.isNextControlPointUsed(nPrevIdx);
93             bPendingCurve |= rPolygon.isPrevControlPointUsed(nClosedIdx);
94         }
95 
96         if (!bPendingCurve) // line segment
97             rPath.lineTo(aPoint.getX(), aPoint.getY());
98         else // cubic bezier segment
99         {
100             basegfx::B2DPoint aCP1 = rPolygon.getNextControlPoint(nPrevIdx);
101             basegfx::B2DPoint aCP2 = rPolygon.getPrevControlPoint(nClosedIdx);
102             if (bLineDraw)
103             {
104                 aCP1 += aHalfPointOfs;
105                 aCP2 += aHalfPointOfs;
106             }
107             rPath.cubicTo(aCP1.getX(), aCP1.getY(), aCP2.getX(), aCP2.getY(), aPoint.getX(),
108                           aPoint.getY());
109         }
110     }
111 
112     if (bClosePath)
113         rPath.closeSubpath();
114 }
115 
AddPolyPolygonToPath(QPainterPath & rPath,const basegfx::B2DPolyPolygon & rPolyPoly,bool bPixelSnap,bool bLineDraw)116 static bool AddPolyPolygonToPath(QPainterPath& rPath, const basegfx::B2DPolyPolygon& rPolyPoly,
117                                  bool bPixelSnap, bool bLineDraw)
118 {
119     if (rPolyPoly.count() == 0)
120         return false;
121     for (auto const& rPolygon : rPolyPoly)
122     {
123         AddPolygonToPath(rPath, rPolygon, true, bPixelSnap, bLineDraw);
124     }
125     return true;
126 }
127 
setClipRegion(const vcl::Region & rRegion)128 bool Qt5GraphicsBackend::setClipRegion(const vcl::Region& rRegion)
129 {
130     if (rRegion.IsRectangle())
131     {
132         m_aClipRegion = toQRect(rRegion.GetBoundRect());
133         if (!m_aClipPath.isEmpty())
134         {
135             QPainterPath aPath;
136             m_aClipPath.swap(aPath);
137         }
138     }
139     else if (!rRegion.HasPolyPolygonOrB2DPolyPolygon())
140     {
141         QRegion aQRegion;
142         RectangleVector aRectangles;
143         rRegion.GetRegionRectangles(aRectangles);
144         for (const auto& rRect : aRectangles)
145             aQRegion += toQRect(rRect);
146         m_aClipRegion = aQRegion;
147         if (!m_aClipPath.isEmpty())
148         {
149             QPainterPath aPath;
150             m_aClipPath.swap(aPath);
151         }
152     }
153     else
154     {
155         QPainterPath aPath;
156         const basegfx::B2DPolyPolygon aPolyClip(rRegion.GetAsB2DPolyPolygon());
157         AddPolyPolygonToPath(aPath, aPolyClip, !getAntiAlias(), false);
158         m_aClipPath.swap(aPath);
159         if (!m_aClipRegion.isEmpty())
160         {
161             QRegion aRegion;
162             m_aClipRegion.swap(aRegion);
163         }
164     }
165     return true;
166 }
167 
ResetClipRegion()168 void Qt5GraphicsBackend::ResetClipRegion()
169 {
170     if (m_pQImage)
171         m_aClipRegion = QRegion(m_pQImage->rect());
172     else
173         m_aClipRegion = QRegion();
174     if (!m_aClipPath.isEmpty())
175     {
176         QPainterPath aPath;
177         m_aClipPath.swap(aPath);
178     }
179 }
180 
drawPixel(tools::Long nX,tools::Long nY)181 void Qt5GraphicsBackend::drawPixel(tools::Long nX, tools::Long nY)
182 {
183     Qt5Painter aPainter(*this);
184     aPainter.drawPoint(nX, nY);
185     aPainter.update(nX, nY, 1, 1);
186 }
187 
drawPixel(tools::Long nX,tools::Long nY,Color nColor)188 void Qt5GraphicsBackend::drawPixel(tools::Long nX, tools::Long nY, Color nColor)
189 {
190     Qt5Painter aPainter(*this);
191     aPainter.setPen(toQColor(nColor));
192     aPainter.setPen(Qt::SolidLine);
193     aPainter.drawPoint(nX, nY);
194     aPainter.update(nX, nY, 1, 1);
195 }
196 
drawLine(tools::Long nX1,tools::Long nY1,tools::Long nX2,tools::Long nY2)197 void Qt5GraphicsBackend::drawLine(tools::Long nX1, tools::Long nY1, tools::Long nX2,
198                                   tools::Long nY2)
199 {
200     Qt5Painter aPainter(*this);
201     aPainter.drawLine(nX1, nY1, nX2, nY2);
202 
203     tools::Long tmp;
204     if (nX1 > nX2)
205     {
206         tmp = nX1;
207         nX1 = nX2;
208         nX2 = tmp;
209     }
210     if (nY1 > nY2)
211     {
212         tmp = nY1;
213         nY1 = nY2;
214         nY2 = tmp;
215     }
216     aPainter.update(nX1, nY1, nX2 - nX1 + 1, nY2 - nY1 + 1);
217 }
218 
drawRect(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight)219 void Qt5GraphicsBackend::drawRect(tools::Long nX, tools::Long nY, tools::Long nWidth,
220                                   tools::Long nHeight)
221 {
222     if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
223         return;
224 
225     Qt5Painter aPainter(*this, true);
226     if (SALCOLOR_NONE != m_aFillColor)
227         aPainter.fillRect(nX, nY, nWidth, nHeight, aPainter.brush());
228     if (SALCOLOR_NONE != m_aLineColor)
229         aPainter.drawRect(nX, nY, nWidth - 1, nHeight - 1);
230     aPainter.update(nX, nY, nWidth, nHeight);
231 }
232 
drawPolyLine(sal_uInt32 nPoints,const Point * pPtAry)233 void Qt5GraphicsBackend::drawPolyLine(sal_uInt32 nPoints, const Point* pPtAry)
234 {
235     if (0 == nPoints)
236         return;
237 
238     Qt5Painter aPainter(*this);
239     QPoint* pPoints = new QPoint[nPoints];
240     QPoint aTopLeft(pPtAry->getX(), pPtAry->getY());
241     QPoint aBottomRight = aTopLeft;
242     for (sal_uInt32 i = 0; i < nPoints; ++i, ++pPtAry)
243     {
244         pPoints[i] = QPoint(pPtAry->getX(), pPtAry->getY());
245         if (pPtAry->getX() < aTopLeft.x())
246             aTopLeft.setX(pPtAry->getX());
247         if (pPtAry->getY() < aTopLeft.y())
248             aTopLeft.setY(pPtAry->getY());
249         if (pPtAry->getX() > aBottomRight.x())
250             aBottomRight.setX(pPtAry->getX());
251         if (pPtAry->getY() > aBottomRight.y())
252             aBottomRight.setY(pPtAry->getY());
253     }
254     aPainter.drawPolyline(pPoints, nPoints);
255     delete[] pPoints;
256     aPainter.update(QRect(aTopLeft, aBottomRight));
257 }
258 
drawPolygon(sal_uInt32 nPoints,const Point * pPtAry)259 void Qt5GraphicsBackend::drawPolygon(sal_uInt32 nPoints, const Point* pPtAry)
260 {
261     Qt5Painter aPainter(*this, true);
262     QPolygon aPolygon(nPoints);
263     for (sal_uInt32 i = 0; i < nPoints; ++i, ++pPtAry)
264         aPolygon.setPoint(i, pPtAry->getX(), pPtAry->getY());
265     aPainter.drawPolygon(aPolygon);
266     aPainter.update(aPolygon.boundingRect());
267 }
268 
drawPolyPolygon(sal_uInt32 nPolyCount,const sal_uInt32 * pPoints,const Point ** ppPtAry)269 void Qt5GraphicsBackend::drawPolyPolygon(sal_uInt32 nPolyCount, const sal_uInt32* pPoints,
270                                          const Point** ppPtAry)
271 {
272     // ignore invisible polygons
273     if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
274         return;
275 
276     QPainterPath aPath;
277     for (sal_uInt32 nPoly = 0; nPoly < nPolyCount; nPoly++)
278     {
279         const sal_uInt32 nPoints = pPoints[nPoly];
280         if (nPoints > 1)
281         {
282             const Point* pPtAry = ppPtAry[nPoly];
283             aPath.moveTo(pPtAry->getX(), pPtAry->getY());
284             pPtAry++;
285             for (sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++)
286                 aPath.lineTo(pPtAry->getX(), pPtAry->getY());
287             aPath.closeSubpath();
288         }
289     }
290 
291     Qt5Painter aPainter(*this, true);
292     aPainter.drawPath(aPath);
293     aPainter.update(aPath.boundingRect());
294 }
295 
drawPolyPolygon(const basegfx::B2DHomMatrix & rObjectToDevice,const basegfx::B2DPolyPolygon & rPolyPolygon,double fTransparency)296 bool Qt5GraphicsBackend::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
297                                          const basegfx::B2DPolyPolygon& rPolyPolygon,
298                                          double fTransparency)
299 {
300     // ignore invisible polygons
301     if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
302         return true;
303     if ((fTransparency >= 1.0) || (fTransparency < 0))
304         return true;
305 
306     // Fallback: Transform to DeviceCoordinates
307     basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
308     aPolyPolygon.transform(rObjectToDevice);
309 
310     QPainterPath aPath;
311     // ignore empty polygons
312     if (!AddPolyPolygonToPath(aPath, aPolyPolygon, !getAntiAlias(), m_aLineColor != SALCOLOR_NONE))
313         return true;
314 
315     Qt5Painter aPainter(*this, true, 255 * (1.0 - fTransparency));
316     aPainter.drawPath(aPath);
317     aPainter.update(aPath.boundingRect());
318     return true;
319 }
320 
drawPolyLineBezier(sal_uInt32,const Point *,const PolyFlags *)321 bool Qt5GraphicsBackend::drawPolyLineBezier(sal_uInt32 /*nPoints*/, const Point* /*pPtAry*/,
322                                             const PolyFlags* /*pFlgAry*/)
323 {
324     return false;
325 }
326 
drawPolygonBezier(sal_uInt32,const Point *,const PolyFlags *)327 bool Qt5GraphicsBackend::drawPolygonBezier(sal_uInt32 /*nPoints*/, const Point* /*pPtAry*/,
328                                            const PolyFlags* /*pFlgAry*/)
329 {
330     return false;
331 }
332 
drawPolyPolygonBezier(sal_uInt32,const sal_uInt32 *,const Point * const *,const PolyFlags * const *)333 bool Qt5GraphicsBackend::drawPolyPolygonBezier(sal_uInt32 /*nPoly*/, const sal_uInt32* /*pPoints*/,
334                                                const Point* const* /*pPtAry*/,
335                                                const PolyFlags* const* /*pFlgAry*/)
336 {
337     return false;
338 }
339 
drawPolyLine(const basegfx::B2DHomMatrix & rObjectToDevice,const basegfx::B2DPolygon & rPolyLine,double fTransparency,double fLineWidth,const std::vector<double> * pStroke,basegfx::B2DLineJoin eLineJoin,css::drawing::LineCap eLineCap,double fMiterMinimumAngle,bool bPixelSnapHairline)340 bool Qt5GraphicsBackend::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
341                                       const basegfx::B2DPolygon& rPolyLine, double fTransparency,
342                                       double fLineWidth,
343                                       const std::vector<double>* pStroke, // MM01
344                                       basegfx::B2DLineJoin eLineJoin,
345                                       css::drawing::LineCap eLineCap, double fMiterMinimumAngle,
346                                       bool bPixelSnapHairline)
347 {
348     if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
349     {
350         return true;
351     }
352 
353     // MM01 check done for simple reasons
354     if (!rPolyLine.count() || fTransparency < 0.0 || fTransparency > 1.0)
355     {
356         return true;
357     }
358 
359     // MM01 need to do line dashing as fallback stuff here now
360     const double fDotDashLength(
361         nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0);
362     const bool bStrokeUsed(0.0 != fDotDashLength);
363     assert(!bStrokeUsed || (bStrokeUsed && pStroke));
364     basegfx::B2DPolyPolygon aPolyPolygonLine;
365 
366     if (bStrokeUsed)
367     {
368         // apply LineStyle
369         basegfx::utils::applyLineDashing(rPolyLine, // source
370                                          *pStroke, // pattern
371                                          &aPolyPolygonLine, // target for lines
372                                          nullptr, // target for gaps
373                                          fDotDashLength); // full length if available
374     }
375     else
376     {
377         // no line dashing, just copy
378         aPolyPolygonLine.append(rPolyLine);
379     }
380 
381     // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
382     aPolyPolygonLine.transform(rObjectToDevice);
383     if (bPixelSnapHairline)
384     {
385         aPolyPolygonLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine);
386     }
387 
388     // tdf#124848 get correct LineWidth in discrete coordinates,
389     if (fLineWidth == 0) // hairline
390         fLineWidth = 1.0;
391     else // Adjust line width for object-to-device scale.
392         fLineWidth = (rObjectToDevice * basegfx::B2DVector(fLineWidth, 0)).getLength();
393 
394     // setup poly-polygon path
395     QPainterPath aPath;
396 
397     // MM01 todo - I assume that this is OKAY to be done in one run for Qt5,
398     // but this NEEDS to be checked/verified
399     for (sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
400     {
401         const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
402         AddPolygonToPath(aPath, aPolyLine, aPolyLine.isClosed(), !getAntiAlias(), true);
403     }
404 
405     Qt5Painter aPainter(*this, false, 255 * (1.0 - fTransparency));
406 
407     // setup line attributes
408     QPen aPen = aPainter.pen();
409     aPen.setWidth(fLineWidth);
410 
411     switch (eLineJoin)
412     {
413         case basegfx::B2DLineJoin::Bevel:
414             aPen.setJoinStyle(Qt::BevelJoin);
415             break;
416         case basegfx::B2DLineJoin::Round:
417             aPen.setJoinStyle(Qt::RoundJoin);
418             break;
419         case basegfx::B2DLineJoin::NONE:
420         case basegfx::B2DLineJoin::Miter:
421             aPen.setMiterLimit(1.0 / sin(fMiterMinimumAngle / 2.0));
422             aPen.setJoinStyle(Qt::MiterJoin);
423             break;
424     }
425 
426     switch (eLineCap)
427     {
428         default: // css::drawing::LineCap_BUTT:
429             aPen.setCapStyle(Qt::FlatCap);
430             break;
431         case css::drawing::LineCap_ROUND:
432             aPen.setCapStyle(Qt::RoundCap);
433             break;
434         case css::drawing::LineCap_SQUARE:
435             aPen.setCapStyle(Qt::SquareCap);
436             break;
437     }
438 
439     aPainter.setPen(aPen);
440     aPainter.drawPath(aPath);
441     aPainter.update(aPath.boundingRect());
442     return true;
443 }
444 
drawGradient(const tools::PolyPolygon &,const Gradient &)445 bool Qt5GraphicsBackend::drawGradient(const tools::PolyPolygon& /*rPolyPolygon*/,
446                                       const Gradient& /*rGradient*/)
447 {
448     return false;
449 }
450 
implDrawGradient(basegfx::B2DPolyPolygon const &,SalGradient const &)451 bool Qt5GraphicsBackend::implDrawGradient(basegfx::B2DPolyPolygon const& /*rPolyPolygon*/,
452                                           SalGradient const& /*rGradient*/)
453 {
454     return false;
455 }
456 
drawScaledImage(const SalTwoRect & rPosAry,const QImage & rImage)457 void Qt5GraphicsBackend::drawScaledImage(const SalTwoRect& rPosAry, const QImage& rImage)
458 {
459     Qt5Painter aPainter(*this);
460     QRect aSrcRect(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight);
461     QRect aDestRect(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight);
462     aPainter.drawImage(aDestRect, rImage, aSrcRect);
463     aPainter.update(aDestRect);
464 }
465 
copyArea(tools::Long nDestX,tools::Long nDestY,tools::Long nSrcX,tools::Long nSrcY,tools::Long nSrcWidth,tools::Long nSrcHeight,bool)466 void Qt5GraphicsBackend::copyArea(tools::Long nDestX, tools::Long nDestY, tools::Long nSrcX,
467                                   tools::Long nSrcY, tools::Long nSrcWidth, tools::Long nSrcHeight,
468                                   bool /*bWindowInvalidate*/)
469 {
470     if (nDestX == nSrcX && nDestY == nSrcY)
471         return;
472 
473     SalTwoRect aTR(nSrcX, nSrcY, nSrcWidth, nSrcHeight, nDestX, nDestY, nSrcWidth, nSrcHeight);
474 
475     QImage* pImage = m_pQImage;
476     QImage aImage = pImage->copy(aTR.mnSrcX, aTR.mnSrcY, aTR.mnSrcWidth, aTR.mnSrcHeight);
477     pImage = &aImage;
478     aTR.mnSrcX = 0;
479     aTR.mnSrcY = 0;
480 
481     drawScaledImage(aTR, *pImage);
482 }
483 
copyBits(const SalTwoRect & rPosAry,SalGraphics * pSrcGraphics)484 void Qt5GraphicsBackend::copyBits(const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics)
485 {
486     if (rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0
487         || rPosAry.mnDestHeight <= 0)
488         return;
489 
490     QImage aImage, *pImage;
491     SalTwoRect aPosAry = rPosAry;
492 
493     if (!pSrcGraphics)
494     {
495         pImage = m_pQImage;
496         aImage
497             = pImage->copy(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight);
498         pImage = &aImage;
499         aPosAry.mnSrcX = 0;
500         aPosAry.mnSrcY = 0;
501     }
502     else
503         pImage = static_cast<Qt5Graphics*>(pSrcGraphics)->getQImage();
504 
505     drawScaledImage(aPosAry, *pImage);
506 }
507 
drawBitmap(const SalTwoRect & rPosAry,const SalBitmap & rSalBitmap)508 void Qt5GraphicsBackend::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap)
509 {
510     if (rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0
511         || rPosAry.mnDestHeight <= 0)
512         return;
513 
514     const QImage* pImage = static_cast<const Qt5Bitmap*>(&rSalBitmap)->GetQImage();
515 
516     assert(pImage);
517 
518     drawScaledImage(rPosAry, *pImage);
519 }
520 
drawBitmap(const SalTwoRect & rPosAry,const SalBitmap &,const SalBitmap &)521 void Qt5GraphicsBackend::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& /*rSalBitmap*/,
522                                     const SalBitmap& /*rTransparentBitmap*/)
523 {
524     if (rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0
525         || rPosAry.mnDestHeight <= 0)
526         return;
527 
528     assert(rPosAry.mnSrcWidth == rPosAry.mnDestWidth);
529     assert(rPosAry.mnSrcHeight == rPosAry.mnDestHeight);
530 }
531 
drawMask(const SalTwoRect & rPosAry,const SalBitmap &,Color)532 void Qt5GraphicsBackend::drawMask(const SalTwoRect& rPosAry, const SalBitmap& /*rSalBitmap*/,
533                                   Color /*nMaskColor*/)
534 {
535     if (rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0
536         || rPosAry.mnDestHeight <= 0)
537         return;
538 
539     assert(rPosAry.mnSrcWidth == rPosAry.mnDestWidth);
540     assert(rPosAry.mnSrcHeight == rPosAry.mnDestHeight);
541 }
542 
getBitmap(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight)543 std::shared_ptr<SalBitmap> Qt5GraphicsBackend::getBitmap(tools::Long nX, tools::Long nY,
544                                                          tools::Long nWidth, tools::Long nHeight)
545 {
546     return std::make_shared<Qt5Bitmap>(m_pQImage->copy(nX, nY, nWidth, nHeight));
547 }
548 
getPixel(tools::Long nX,tools::Long nY)549 Color Qt5GraphicsBackend::getPixel(tools::Long nX, tools::Long nY)
550 {
551     return Color(ColorTransparency, m_pQImage->pixel(nX, nY));
552 }
553 
invert(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight,SalInvert nFlags)554 void Qt5GraphicsBackend::invert(tools::Long nX, tools::Long nY, tools::Long nWidth,
555                                 tools::Long nHeight, SalInvert nFlags)
556 {
557     Qt5Painter aPainter(*this);
558     if (SalInvert::N50 & nFlags)
559     {
560         aPainter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
561         QBrush aBrush(Qt::white, Qt::Dense4Pattern);
562         aPainter.fillRect(nX, nY, nWidth, nHeight, aBrush);
563     }
564     else
565     {
566         if (SalInvert::TrackFrame & nFlags)
567         {
568             aPainter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
569             QPen aPen(Qt::white);
570             aPen.setStyle(Qt::DotLine);
571             aPainter.setPen(aPen);
572             aPainter.drawRect(nX, nY, nWidth, nHeight);
573         }
574         else
575         {
576             aPainter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
577             aPainter.fillRect(nX, nY, nWidth, nHeight, Qt::white);
578         }
579     }
580     aPainter.update(nX, nY, nWidth, nHeight);
581 }
582 
invert(sal_uInt32,const Point *,SalInvert)583 void Qt5GraphicsBackend::invert(sal_uInt32 /*nPoints*/, const Point* /*pPtAry*/,
584                                 SalInvert /*nFlags*/)
585 {
586 }
587 
drawEPS(tools::Long,tools::Long,tools::Long,tools::Long,void *,sal_uInt32)588 bool Qt5GraphicsBackend::drawEPS(tools::Long /*nX*/, tools::Long /*nY*/, tools::Long /*nWidth*/,
589                                  tools::Long /*nHeight*/, void* /*pPtr*/, sal_uInt32 /*nSize*/)
590 {
591     return false;
592 }
593 
blendBitmap(const SalTwoRect &,const SalBitmap &)594 bool Qt5GraphicsBackend::blendBitmap(const SalTwoRect&, const SalBitmap& /*rBitmap*/)
595 {
596     return false;
597 }
598 
blendAlphaBitmap(const SalTwoRect &,const SalBitmap &,const SalBitmap &,const SalBitmap &)599 bool Qt5GraphicsBackend::blendAlphaBitmap(const SalTwoRect&, const SalBitmap& /*rSrcBitmap*/,
600                                           const SalBitmap& /*rMaskBitmap*/,
601                                           const SalBitmap& /*rAlphaBitmap*/)
602 {
603     return false;
604 }
605 
getAlphaImage(const SalBitmap & rSourceBitmap,const SalBitmap & rAlphaBitmap,QImage & rAlphaImage)606 static bool getAlphaImage(const SalBitmap& rSourceBitmap, const SalBitmap& rAlphaBitmap,
607                           QImage& rAlphaImage)
608 {
609     if (rAlphaBitmap.GetBitCount() != 8 && rAlphaBitmap.GetBitCount() != 1)
610     {
611         SAL_WARN("vcl.gdi", "unsupported alpha depth case: " << rAlphaBitmap.GetBitCount());
612         return false;
613     }
614 
615     const QImage* pBitmap = static_cast<const Qt5Bitmap*>(&rSourceBitmap)->GetQImage();
616     const QImage* pAlpha = static_cast<const Qt5Bitmap*>(&rAlphaBitmap)->GetQImage();
617     rAlphaImage = pBitmap->convertToFormat(Qt5_DefaultFormat32);
618 
619     if (rAlphaBitmap.GetBitCount() == 8)
620     {
621         for (int y = 0; y < rAlphaImage.height(); ++y)
622         {
623             uchar* image_line = rAlphaImage.scanLine(y);
624             const uchar* alpha_line = pAlpha->scanLine(y);
625             for (int x = 0; x < rAlphaImage.width(); ++x, image_line += 4)
626                 image_line[3] = 255 - alpha_line[x];
627         }
628     }
629     else
630     {
631         for (int y = 0; y < rAlphaImage.height(); ++y)
632         {
633             uchar* image_line = rAlphaImage.scanLine(y);
634             const uchar* alpha_line = pAlpha->scanLine(y);
635             for (int x = 0; x < rAlphaImage.width(); ++x, image_line += 4)
636             {
637                 if (x && !(x % 8))
638                     ++alpha_line;
639                 if (0 != (*alpha_line & (1 << (7 - x % 8))))
640                     image_line[3] = 0;
641             }
642         }
643     }
644 
645     return true;
646 }
647 
drawAlphaBitmap(const SalTwoRect & rPosAry,const SalBitmap & rSourceBitmap,const SalBitmap & rAlphaBitmap)648 bool Qt5GraphicsBackend::drawAlphaBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSourceBitmap,
649                                          const SalBitmap& rAlphaBitmap)
650 {
651     QImage aImage;
652     if (!getAlphaImage(rSourceBitmap, rAlphaBitmap, aImage))
653         return false;
654     drawScaledImage(rPosAry, aImage);
655     return true;
656 }
657 
drawTransformedBitmap(const basegfx::B2DPoint & rNull,const basegfx::B2DPoint & rX,const basegfx::B2DPoint & rY,const SalBitmap & rSourceBitmap,const SalBitmap * pAlphaBitmap,double fAlpha)658 bool Qt5GraphicsBackend::drawTransformedBitmap(const basegfx::B2DPoint& rNull,
659                                                const basegfx::B2DPoint& rX,
660                                                const basegfx::B2DPoint& rY,
661                                                const SalBitmap& rSourceBitmap,
662                                                const SalBitmap* pAlphaBitmap, double fAlpha)
663 {
664     if (fAlpha != 1.0)
665         return false;
666     QImage aImage;
667     if (pAlphaBitmap && !getAlphaImage(rSourceBitmap, *pAlphaBitmap, aImage))
668         return false;
669     else
670     {
671         const QImage* pBitmap = static_cast<const Qt5Bitmap*>(&rSourceBitmap)->GetQImage();
672         aImage = pBitmap->convertToFormat(Qt5_DefaultFormat32);
673     }
674 
675     Qt5Painter aPainter(*this);
676     const basegfx::B2DVector aXRel = rX - rNull;
677     const basegfx::B2DVector aYRel = rY - rNull;
678     aPainter.setTransform(QTransform(aXRel.getX() / aImage.width(), aXRel.getY() / aImage.width(),
679                                      aYRel.getX() / aImage.height(), aYRel.getY() / aImage.height(),
680                                      rNull.getX(), rNull.getY()));
681     aPainter.drawImage(QPoint(0, 0), aImage);
682     aPainter.update(aImage.rect());
683     return true;
684 }
685 
hasFastDrawTransformedBitmap() const686 bool Qt5GraphicsBackend::hasFastDrawTransformedBitmap() const { return false; }
687 
drawAlphaRect(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight,sal_uInt8 nTransparency)688 bool Qt5GraphicsBackend::drawAlphaRect(tools::Long nX, tools::Long nY, tools::Long nWidth,
689                                        tools::Long nHeight, sal_uInt8 nTransparency)
690 {
691     if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
692         return true;
693     assert(nTransparency <= 100);
694     if (nTransparency > 100)
695         nTransparency = 100;
696     Qt5Painter aPainter(*this, true, (100 - nTransparency) * (255.0 / 100));
697     if (SALCOLOR_NONE != m_aFillColor)
698         aPainter.fillRect(nX, nY, nWidth, nHeight, aPainter.brush());
699     if (SALCOLOR_NONE != m_aLineColor)
700         aPainter.drawRect(nX, nY, nWidth - 1, nHeight - 1);
701     aPainter.update(nX, nY, nWidth, nHeight);
702     return true;
703 }
704 
GetBitCount() const705 sal_uInt16 Qt5GraphicsBackend::GetBitCount() const { return getFormatBits(m_pQImage->format()); }
706 
GetGraphicsWidth() const707 tools::Long Qt5GraphicsBackend::GetGraphicsWidth() const { return m_pQImage->width(); }
708 
SetLineColor()709 void Qt5GraphicsBackend::SetLineColor() { m_aLineColor = SALCOLOR_NONE; }
710 
SetLineColor(Color nColor)711 void Qt5GraphicsBackend::SetLineColor(Color nColor) { m_aLineColor = nColor; }
712 
SetFillColor()713 void Qt5GraphicsBackend::SetFillColor() { m_aFillColor = SALCOLOR_NONE; }
714 
SetFillColor(Color nColor)715 void Qt5GraphicsBackend::SetFillColor(Color nColor) { m_aFillColor = nColor; }
716 
SetXORMode(bool bSet,bool)717 void Qt5GraphicsBackend::SetXORMode(bool bSet, bool)
718 {
719     if (bSet)
720         m_eCompositionMode = QPainter::CompositionMode_Xor;
721     else
722         m_eCompositionMode = QPainter::CompositionMode_SourceOver;
723 }
724 
SetROPLineColor(SalROPColor)725 void Qt5GraphicsBackend::SetROPLineColor(SalROPColor /*nROPColor*/) {}
726 
SetROPFillColor(SalROPColor)727 void Qt5GraphicsBackend::SetROPFillColor(SalROPColor /*nROPColor*/) {}
728 
supportsOperation(OutDevSupportType eType) const729 bool Qt5GraphicsBackend::supportsOperation(OutDevSupportType eType) const
730 {
731     switch (eType)
732     {
733         case OutDevSupportType::B2DDraw:
734         case OutDevSupportType::TransparentRect:
735             return true;
736         default:
737             return false;
738     }
739 }
740 
GetResolution(sal_Int32 & rDPIX,sal_Int32 & rDPIY)741 void Qt5Graphics::GetResolution(sal_Int32& rDPIX, sal_Int32& rDPIY)
742 {
743     char* pForceDpi;
744     if ((pForceDpi = getenv("SAL_FORCEDPI")))
745     {
746         OString sForceDPI(pForceDpi);
747         rDPIX = rDPIY = sForceDPI.toInt32();
748         return;
749     }
750 
751     if (!m_pFrame || !m_pFrame->GetQWidget()->window()->windowHandle())
752         return;
753 
754     QScreen* pScreen = m_pFrame->GetQWidget()->window()->windowHandle()->screen();
755     rDPIX = pScreen->logicalDotsPerInchX() * pScreen->devicePixelRatio() + 0.5;
756     rDPIY = pScreen->logicalDotsPerInchY() * pScreen->devicePixelRatio() + 0.5;
757 }
758 
759 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
760