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