1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors
3
+
4
+ #include " magnifierwidget.h"
5
+ #include < QApplication>
6
+ #include < QEvent>
7
+ #include < QMouseEvent>
8
+ #include < QPainter>
9
+ #include < QPainterPath>
10
+ #include < QPen>
11
+ #include < QPixmap>
12
+
13
+ MagnifierWidget::MagnifierWidget (const QPixmap& p,
14
+ const QColor& c,
15
+ bool isSquare,
16
+ QWidget* parent)
17
+ : QWidget(parent)
18
+ , m_color(c)
19
+ , m_borderColor(c)
20
+ , m_screenshot(p)
21
+ , m_square(isSquare)
22
+ {
23
+ setFixedSize (parent->width (), parent->height ());
24
+ setAttribute (Qt::WA_TransparentForMouseEvents);
25
+ m_color.setAlpha (130 );
26
+ // add padding for circular magnifier
27
+ QImage padded (p.width () + 2 * m_magPixels,
28
+ p.height () + 2 * m_magPixels,
29
+ QImage::Format_ARGB32);
30
+ padded.fill (Qt::black);
31
+ QPainter painter (&padded);
32
+ painter.drawPixmap (m_magPixels, m_magPixels, p);
33
+ m_paddedScreenshot.convertFromImage (padded);
34
+ }
35
+ void MagnifierWidget::paintEvent (QPaintEvent*)
36
+ {
37
+ QPainter p (this );
38
+ if (m_square)
39
+ drawMagnifier (p);
40
+ else
41
+ drawMagnifierCircle (p);
42
+ }
43
+
44
+ void MagnifierWidget::drawMagnifierCircle (QPainter& painter)
45
+ {
46
+ int x = QCursor::pos ().x () + m_magPixels;
47
+ int y = QCursor::pos ().y () + m_magPixels;
48
+ int magX = static_cast <int >(x * m_devicePixelRatio - m_magPixels);
49
+ int magY = static_cast <int >(y * m_devicePixelRatio - m_magPixels);
50
+ QRectF magniRect (magX, magY, m_pixels, m_pixels);
51
+
52
+ qreal drawPosX = x + m_magOffset + m_pixels * magZoom / 2 ;
53
+ if (drawPosX > width () - m_pixels * magZoom / 2 ) {
54
+ drawPosX = x - m_magOffset - m_pixels * magZoom / 2 ;
55
+ }
56
+ qreal drawPosY = y + m_magOffset + m_pixels * magZoom / 2 ;
57
+ if (drawPosY > height () - m_pixels * magZoom / 2 ) {
58
+ drawPosY = y - m_magOffset - m_pixels * magZoom / 2 ;
59
+ }
60
+ QPointF drawPos (drawPosX, drawPosY);
61
+ QRectF crossHairTop (drawPos.x () + magZoom * (-0.5 ),
62
+ drawPos.y () - magZoom * (m_magPixels + 0.5 ),
63
+ magZoom,
64
+ magZoom * (m_magPixels));
65
+ QRectF crossHairRight (drawPos.x () + magZoom * (0.5 ),
66
+ drawPos.y () + magZoom * (-0.5 ),
67
+ magZoom * (m_magPixels),
68
+ magZoom);
69
+ QRectF crossHairBottom (drawPos.x () + magZoom * (-0.5 ),
70
+ drawPos.y () + magZoom * (0.5 ),
71
+ magZoom,
72
+ magZoom * (m_magPixels));
73
+ QRectF crossHairLeft (drawPos.x () - magZoom * (m_magPixels + 0.5 ),
74
+ drawPos.y () + magZoom * (-0.5 ),
75
+ magZoom * (m_magPixels),
76
+ magZoom);
77
+ QRectF crossHairBorder (drawPos.x () - magZoom * (m_magPixels + 0.5 ) - 1 ,
78
+ drawPos.y () - magZoom * (m_magPixels + 0.5 ) - 1 ,
79
+ m_pixels * magZoom + 2 ,
80
+ m_pixels * magZoom + 2 );
81
+ const auto frag =
82
+ QPainter::PixmapFragment::create (drawPos, magniRect, magZoom, magZoom);
83
+
84
+ painter.setRenderHint (QPainter::Antialiasing, true );
85
+ QPainterPath path = QPainterPath ();
86
+ path.addEllipse (drawPos, m_pixels * magZoom / 2 , m_pixels * magZoom / 2 );
87
+ painter.setClipPath (path);
88
+
89
+ painter.drawPixmapFragments (
90
+ &frag, 1 , m_paddedScreenshot, QPainter::OpaqueHint);
91
+ painter.setCompositionMode (QPainter::CompositionMode_SourceOver);
92
+ for (auto & rect :
93
+ { crossHairTop, crossHairRight, crossHairBottom, crossHairLeft }) {
94
+ painter.fillRect (rect, m_color);
95
+ }
96
+ QPen pen (m_borderColor);
97
+ pen.setWidth (4 );
98
+ painter.setPen (pen);
99
+ painter.drawEllipse (
100
+ drawPos, m_pixels * magZoom / 2 , m_pixels * magZoom / 2 );
101
+ }
102
+ // https://invent.kde.org/graphics/spectacle/-/blob/master/src/QuickEditor/QuickEditor.cpp#L841
103
+ void MagnifierWidget::drawMagnifier (QPainter& painter)
104
+ {
105
+ int x = QCursor::pos ().x ();
106
+ int y = QCursor::pos ().y ();
107
+ int magX = static_cast <int >(x * m_devicePixelRatio - m_magPixels);
108
+ int offsetX = 0 ;
109
+ if (magX < 0 ) {
110
+ offsetX = magX;
111
+ magX = 0 ;
112
+ } else {
113
+ const int maxX = m_screenshot.width () - m_pixels;
114
+ if (magX > maxX) {
115
+ offsetX = magX - maxX;
116
+ magX = maxX;
117
+ }
118
+ }
119
+ int magY = static_cast <int >(y * m_devicePixelRatio - m_magPixels);
120
+ int offsetY = 0 ;
121
+ if (magY < 0 ) {
122
+ offsetY = magY;
123
+ magY = 0 ;
124
+ } else {
125
+ const int maxY = m_screenshot.height () - m_pixels;
126
+ if (magY > maxY) {
127
+ offsetY = magY - maxY;
128
+ magY = maxY;
129
+ }
130
+ }
131
+ QRectF magniRect (magX, magY, m_pixels, m_pixels);
132
+
133
+ qreal drawPosX = x + m_magOffset + m_pixels * magZoom / 2 ;
134
+ if (drawPosX > width () - m_pixels * magZoom / 2 ) {
135
+ drawPosX = x - m_magOffset - m_pixels * magZoom / 2 ;
136
+ }
137
+ qreal drawPosY = y + m_magOffset + m_pixels * magZoom / 2 ;
138
+ if (drawPosY > height () - m_pixels * magZoom / 2 ) {
139
+ drawPosY = y - m_magOffset - m_pixels * magZoom / 2 ;
140
+ }
141
+ QPointF drawPos (drawPosX, drawPosY);
142
+ QRectF crossHairTop (drawPos.x () + magZoom * (offsetX - 0.5 ),
143
+ drawPos.y () - magZoom * (m_magPixels + 0.5 ),
144
+ magZoom,
145
+ magZoom * (m_magPixels + offsetY));
146
+ QRectF crossHairRight (drawPos.x () + magZoom * (0.5 + offsetX),
147
+ drawPos.y () + magZoom * (offsetY - 0.5 ),
148
+ magZoom * (m_magPixels - offsetX),
149
+ magZoom);
150
+ QRectF crossHairBottom (drawPos.x () + magZoom * (offsetX - 0.5 ),
151
+ drawPos.y () + magZoom * (0.5 + offsetY),
152
+ magZoom,
153
+ magZoom * (m_magPixels - offsetY));
154
+ QRectF crossHairLeft (drawPos.x () - magZoom * (m_magPixels + 0.5 ),
155
+ drawPos.y () + magZoom * (offsetY - 0.5 ),
156
+ magZoom * (m_magPixels + offsetX),
157
+ magZoom);
158
+ QRectF crossHairBorder (drawPos.x () - magZoom * (m_magPixels + 0.5 ) - 1 ,
159
+ drawPos.y () - magZoom * (m_magPixels + 0.5 ) - 1 ,
160
+ m_pixels * magZoom + 2 ,
161
+ m_pixels * magZoom + 2 );
162
+ const auto frag =
163
+ QPainter::PixmapFragment::create (drawPos, magniRect, magZoom, magZoom);
164
+
165
+ painter.fillRect (crossHairBorder, m_borderColor);
166
+ painter.drawPixmapFragments (&frag, 1 , m_screenshot, QPainter::OpaqueHint);
167
+ painter.setCompositionMode (QPainter::CompositionMode_SourceOver);
168
+ for (auto & rect :
169
+ { crossHairTop, crossHairRight, crossHairBottom, crossHairLeft }) {
170
+ painter.fillRect (rect, m_color);
171
+ }
172
+ }
0 commit comments