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