1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "qpixeltool.h"
30
31#include <qapplication.h>
32#include <qdesktopwidget.h>
33#include <qdir.h>
34#include <qapplication.h>
35#include <qscreen.h>
36#if QT_CONFIG(clipboard)
37#include <qclipboard.h>
38#endif
39#include <qpainter.h>
40#include <qevent.h>
41#include <qfiledialog.h>
42#include <qmessagebox.h>
43#include <qsettings.h>
44#include <qmenu.h>
45#include <qactiongroup.h>
46#include <qimagewriter.h>
47#include <qscreen.h>
48#include <qstandardpaths.h>
49#include <qtextstream.h>
50#include <qwindow.h>
51#include <private/qhighdpiscaling_p.h>
52
53#include <qdebug.h>
54
55QT_BEGIN_NAMESPACE
56
57static QPoint initialPos(const QSettings &settings, const QSize &initialSize)
58{
59 const QPoint defaultPos = QGuiApplication::primaryScreen()->availableGeometry().topLeft();
60 const QPoint savedPos =
61 settings.value(key: QLatin1String("position"), defaultValue: QVariant(defaultPos)).toPoint();
62 auto savedScreen = QGuiApplication::screenAt(point: savedPos);
63 return savedScreen != nullptr
64 && savedScreen->availableGeometry().intersects(r: QRect(savedPos, initialSize))
65 ? savedPos : defaultPos;
66}
67
68QPixelTool::QPixelTool(QWidget *parent)
69 : QWidget(parent)
70{
71 setWindowTitle(QCoreApplication::applicationName());
72 QSettings settings(QLatin1String("QtProject"), QLatin1String("QPixelTool"));
73 m_autoUpdate = settings.value(key: QLatin1String("autoUpdate"), defaultValue: 0).toBool();
74 m_gridSize = settings.value(key: QLatin1String("gridSize"), defaultValue: 1).toInt();
75 m_gridActive = settings.value(key: QLatin1String("gridActive"), defaultValue: 1).toInt();
76 m_zoom = settings.value(key: QLatin1String("zoom"), defaultValue: 4).toInt();
77 m_initialSize = settings.value(key: QLatin1String("initialSize"), defaultValue: QSize(250, 200)).toSize();
78 m_lcdMode = settings.value(key: QLatin1String("lcdMode"), defaultValue: 0).toInt();
79
80 move(initialPos(settings, initialSize: m_initialSize));
81
82 setMouseTracking(true);
83 setAttribute(Qt::WA_OpaquePaintEvent);
84 m_updateId = startTimer(interval: 30);
85}
86
87QPixelTool::~QPixelTool()
88{
89 QSettings settings(QLatin1String("QtProject"), QLatin1String("QPixelTool"));
90 settings.setValue(key: QLatin1String("autoUpdate"), value: int(m_autoUpdate));
91 settings.setValue(key: QLatin1String("gridSize"), value: m_gridSize);
92 settings.setValue(key: QLatin1String("gridActive"), value: m_gridActive);
93 settings.setValue(key: QLatin1String("zoom"), value: m_zoom);
94 settings.setValue(key: QLatin1String("initialSize"), value: size());
95 settings.setValue(key: QLatin1String("position"), value: pos());
96 settings.setValue(key: QLatin1String("lcdMode"), value: m_lcdMode);
97}
98
99void QPixelTool::setPreviewImage(const QImage &image)
100{
101 m_preview_mode = true;
102 m_preview_image = image;
103 m_freeze = true;
104}
105
106void QPixelTool::timerEvent(QTimerEvent *event)
107{
108 if (event->timerId() == m_updateId && !m_freeze) {
109 grabScreen();
110 } else if (event->timerId() == m_displayZoomId) {
111 killTimer(id: m_displayZoomId);
112 setZoomVisible(false);
113 } else if (event->timerId() == m_displayGridSizeId) {
114 killTimer(id: m_displayGridSizeId);
115 m_displayGridSize = false;
116 }
117}
118
119void render_string(QPainter *p, int w, int h, const QString &text, int flags)
120{
121 p->setBrush(QColor(255, 255, 255, 191));
122 p->setPen(Qt::black);
123 QRect bounds;
124 p->drawText(x: 0, y: 0, w, h, flags: Qt::TextDontPrint | flags, str: text, br: &bounds);
125
126 if (bounds.x() == 0) bounds.adjust(dx1: 0, dy1: 0, dx2: 10, dy2: 0);
127 else bounds.adjust(dx1: -10, dy1: 0, dx2: 0, dy2: 0);
128
129 if (bounds.y() == 0) bounds.adjust(dx1: 0, dy1: 0, dx2: 0, dy2: 10);
130 else bounds.adjust(dx1: 0, dy1: -10, dx2: 0, dy2: 0);
131
132 p->drawRect(r: bounds);
133 p->drawText(r: bounds, flags, text);
134}
135
136static QImage imageLCDFilter(const QImage &image, int lcdMode)
137{
138 Q_ASSERT(lcdMode > 0 && lcdMode < 5);
139 const bool vertical = (lcdMode > 2);
140 QImage scaled(image.width() * (vertical ? 1 : 3),
141 image.height() * (vertical ? 3 : 1),
142 image.format());
143
144 const int w = image.width();
145 const int h = image.height();
146 if (!vertical) {
147 for (int y = 0; y < h; ++y) {
148 const QRgb *in = reinterpret_cast<const QRgb *>(image.scanLine(y));
149 QRgb *out = reinterpret_cast<QRgb *>(scaled.scanLine(y));
150 if (lcdMode == 1) {
151 for (int x = 0; x < w; ++x) {
152 *out++ = in[x] & 0xffff0000;
153 *out++ = in[x] & 0xff00ff00;
154 *out++ = in[x] & 0xff0000ff;
155 }
156 } else {
157 for (int x = 0; x < w; ++x) {
158 *out++ = in[x] & 0xff0000ff;
159 *out++ = in[x] & 0xff00ff00;
160 *out++ = in[x] & 0xffff0000;
161 }
162 }
163 }
164 } else {
165 for (int y = 0; y < h; ++y) {
166 const QRgb *in = reinterpret_cast<const QRgb *>(image.scanLine(y));
167 QRgb *out1 = reinterpret_cast<QRgb *>(scaled.scanLine(y * 3 + 0));
168 QRgb *out2 = reinterpret_cast<QRgb *>(scaled.scanLine(y * 3 + 1));
169 QRgb *out3 = reinterpret_cast<QRgb *>(scaled.scanLine(y * 3 + 2));
170 if (lcdMode == 2) {
171 for (int x = 0; x < w; ++x) {
172 out1[x] = in[x] & 0xffff0000;
173 out2[x] = in[x] & 0xff00ff00;
174 out3[x] = in[x] & 0xff0000ff;
175 }
176 } else {
177 for (int x = 0; x < w; ++x) {
178 out1[x] = in[x] & 0xff0000ff;
179 out2[x] = in[x] & 0xff00ff00;
180 out3[x] = in[x] & 0xffff0000;
181 }
182 }
183 }
184 }
185 return scaled;
186}
187
188void QPixelTool::paintEvent(QPaintEvent *)
189{
190 QPainter p(this);
191
192 if (m_preview_mode) {
193 QPixmap pixmap(40, 40);
194 QPainter pt(&pixmap);
195 pt.fillRect(x: 0, y: 0, w: 20, h: 20, c: Qt::white);
196 pt.fillRect(x: 20, y: 20, w: 20, h: 20, c: Qt::white);
197 pt.fillRect(x: 20, y: 0, w: 20, h: 20, c: Qt::lightGray);
198 pt.fillRect(x: 0, y: 20, w: 20, h: 20, c: Qt::lightGray);
199 pt.end();
200 p.fillRect(x: 0, y: 0, w: width(), h: height(), b: pixmap);
201 }
202
203 int w = width();
204 int h = height();
205
206 p.save();
207 if (m_lcdMode == 0) {
208 p.scale(sx: m_zoom, sy: m_zoom);
209 p.drawPixmap(x: 0, y: 0, pm: m_buffer);
210 } else {
211 if (m_lcdMode <= 2)
212 p.scale(sx: m_zoom / 3.0, sy: m_zoom);
213 else
214 p.scale(sx: m_zoom, sy: m_zoom / 3.0);
215 p.drawImage(x: 0, y: 0, image: imageLCDFilter(image: m_buffer.toImage(), lcdMode: m_lcdMode));
216 }
217 p.restore();
218
219 // Draw the grid on top.
220 if (m_gridActive) {
221 p.setPen(m_gridActive == 1 ? Qt::black : Qt::white);
222 int incr = m_gridSize * m_zoom;
223 if (m_lcdMode == 0 || m_lcdMode > 2) {
224 for (int x=0; x<w; x+=incr)
225 p.drawLine(x1: x, y1: 0, x2: x, y2: h);
226 }
227 if (m_lcdMode <= 2) {
228 for (int y=0; y<h; y+=incr)
229 p.drawLine(x1: 0, y1: y, x2: w, y2: y);
230 }
231 }
232
233 QFont f(QLatin1String("courier"));
234 f.setBold(true);
235 p.setFont(f);
236
237 if (m_displayZoom) {
238 render_string(p: &p, w, h,
239 text: QLatin1String("Zoom: x") + QString::number(m_zoom),
240 flags: Qt::AlignTop | Qt::AlignRight);
241 }
242
243 if (m_displayGridSize) {
244 render_string(p: &p, w, h,
245 text: QLatin1String("Grid size: ") + QString::number(m_gridSize),
246 flags: Qt::AlignBottom | Qt::AlignLeft);
247 }
248
249 if (m_freeze) {
250 QString str = QString::asprintf(format: "%8X (%3d,%3d,%3d,%3d)",
251 m_currentColor,
252 (0xff000000 & m_currentColor) >> 24,
253 (0x00ff0000 & m_currentColor) >> 16,
254 (0x0000ff00 & m_currentColor) >> 8,
255 (0x000000ff & m_currentColor));
256 render_string(p: &p, w, h,
257 text: str,
258 flags: Qt::AlignBottom | Qt::AlignRight);
259 }
260
261 if (m_mouseDown && m_dragStart != m_dragCurrent) {
262 int x1 = (m_dragStart.x() / m_zoom) * m_zoom;
263 int y1 = (m_dragStart.y() / m_zoom) * m_zoom;
264 int x2 = (m_dragCurrent.x() / m_zoom) * m_zoom;
265 int y2 = (m_dragCurrent.y() / m_zoom) * m_zoom;
266 QRect r = QRect(x1, y1, x2 - x1, y2 - y1).normalized();
267 p.setBrush(Qt::NoBrush);
268 p.setPen(QPen(Qt::red, 3, Qt::SolidLine));
269 p.drawRect(r);
270 p.setPen(QPen(Qt::black, 1, Qt::SolidLine));
271 p.drawRect(r);
272
273 QString str = QString::asprintf(format: "Rect: x=%d, y=%d, w=%d, h=%d",
274 r.x() / m_zoom,
275 r.y() / m_zoom,
276 r.width() / m_zoom,
277 r.height() / m_zoom);
278 render_string(p: &p, w, h, text: str, flags: Qt::AlignBottom | Qt::AlignLeft);
279 }
280
281
282}
283
284void QPixelTool::keyPressEvent(QKeyEvent *e)
285{
286 switch (e->key()) {
287 case Qt::Key_Space:
288 toggleFreeze();
289 break;
290 case Qt::Key_Plus:
291 increaseZoom();
292 break;
293 case Qt::Key_Minus:
294 decreaseZoom();
295 break;
296 case Qt::Key_PageUp:
297 setGridSize(m_gridSize + 1);
298 break;
299 case Qt::Key_PageDown:
300 setGridSize(m_gridSize - 1);
301 break;
302 case Qt::Key_G:
303 toggleGrid();
304 break;
305 case Qt::Key_A:
306 m_autoUpdate = !m_autoUpdate;
307 break;
308#if QT_CONFIG(clipboard)
309 case Qt::Key_C:
310 if (e->modifiers().testFlag(flag: Qt::ControlModifier))
311 copyToClipboard();
312 else
313 copyColorToClipboard();
314 break;
315#endif // QT_CONFIG(clipboard)
316 case Qt::Key_S:
317 if (e->modifiers() & Qt::ControlModifier) {
318 releaseKeyboard();
319 saveToFile();
320 }
321 break;
322 case Qt::Key_Control:
323 grabKeyboard();
324 break;
325 case Qt::Key_F1:
326 aboutPixelTool();
327 break;
328 }
329}
330
331void QPixelTool::keyReleaseEvent(QKeyEvent *e)
332{
333 switch(e->key()) {
334 case Qt::Key_Control:
335 releaseKeyboard();
336 break;
337 default:
338 break;
339 }
340}
341
342void QPixelTool::resizeEvent(QResizeEvent *)
343{
344 grabScreen();
345}
346
347void QPixelTool::mouseMoveEvent(QMouseEvent *e)
348{
349 if (m_mouseDown)
350 m_dragCurrent = e->pos();
351
352 int x = e->x() / m_zoom;
353 int y = e->y() / m_zoom;
354
355 QImage im = m_buffer.toImage().convertToFormat(f: QImage::Format_ARGB32);
356 if (x < im.width() && y < im.height() && x >= 0 && y >= 0) {
357 m_currentColor = im.pixel(x, y);
358 update();
359 }
360}
361
362void QPixelTool::mousePressEvent(QMouseEvent *e)
363{
364 if (!m_freeze)
365 return;
366 m_mouseDown = true;
367 m_dragStart = e->pos();
368}
369
370void QPixelTool::mouseReleaseEvent(QMouseEvent *)
371{
372 m_mouseDown = false;
373}
374
375static QAction *addCheckableAction(QMenu &menu, const QString &title,
376 bool value, const QKeySequence &key)
377{
378 QAction *result = menu.addAction(text: title);
379 result->setCheckable(true);
380 result->setChecked(value);
381 result->setShortcut(key);
382 return result;
383}
384
385static QAction *addCheckableAction(QMenu &menu, const QString &title,
386 bool value, const QKeySequence &key,
387 QActionGroup *group)
388{
389 QAction *result = addCheckableAction(menu, title, value, key);
390 result->setActionGroup(group);
391 return result;
392}
393
394void QPixelTool::contextMenuEvent(QContextMenuEvent *e)
395{
396 const bool tmpFreeze = m_freeze;
397 m_freeze = true;
398
399 QMenu menu;
400 menu.addAction(text: QLatin1String("Qt Pixel Zooming Tool"))->setEnabled(false);
401 menu.addSeparator();
402
403 // Grid color options...
404 QActionGroup *gridGroup = new QActionGroup(&menu);
405 addCheckableAction(menu, title: QLatin1String("White grid"), value: m_gridActive == 2,
406 key: Qt::Key_W, group: gridGroup);
407 QAction *blackGrid = addCheckableAction(menu, title: QLatin1String("Black grid"),
408 value: m_gridActive == 1, key: Qt::Key_B, group: gridGroup);
409 QAction *noGrid = addCheckableAction(menu, title: QLatin1String("No grid"), value: m_gridActive == 0,
410 key: Qt::Key_N, group: gridGroup);
411 menu.addSeparator();
412
413 // Grid size options
414 menu.addAction(text: QLatin1String("Increase grid size"),
415 object: this, slot: &QPixelTool::increaseGridSize, shortcut: Qt::Key_PageUp);
416 menu.addAction(text: QLatin1String("Decrease grid size"),
417 object: this, slot: &QPixelTool::decreaseGridSize, shortcut: Qt::Key_PageDown);
418 menu.addSeparator();
419
420 QActionGroup *lcdGroup = new QActionGroup(&menu);
421 addCheckableAction(menu, title: QLatin1String("No subpixels"), value: m_lcdMode == 0,
422 key: QKeySequence(), group: lcdGroup);
423 QAction *rgbPixels = addCheckableAction(menu, title: QLatin1String("RGB subpixels"),
424 value: m_lcdMode == 1, key: QKeySequence(), group: lcdGroup);
425 QAction *bgrPixels = addCheckableAction(menu, title: QLatin1String("BGR subpixels"),
426 value: m_lcdMode == 2, key: QKeySequence(), group: lcdGroup);
427 QAction *vrgbPixels = addCheckableAction(menu, title: QLatin1String("VRGB subpixels"),
428 value: m_lcdMode == 3, key: QKeySequence(), group: lcdGroup);
429 QAction *vbgrPixels = addCheckableAction(menu, title: QLatin1String("VBGR subpixels"),
430 value: m_lcdMode == 4, key: QKeySequence(), group: lcdGroup);
431 menu.addSeparator();
432
433 // Zoom options
434 menu.addAction(text: QLatin1String("Zoom in"),
435 object: this, slot: &QPixelTool::increaseZoom, shortcut: Qt::Key_Plus);
436 menu.addAction(text: QLatin1String("Zoom out"),
437 object: this, slot: &QPixelTool::decreaseZoom, shortcut: Qt::Key_Minus);
438 menu.addSeparator();
439
440 // Freeze / Autoupdate
441 QAction *freeze = addCheckableAction(menu, title: QLatin1String("Frozen"),
442 value: tmpFreeze, key: Qt::Key_Space);
443 QAction *autoUpdate = addCheckableAction(menu, title: QLatin1String("Continuous update"),
444 value: m_autoUpdate, key: Qt::Key_A);
445 menu.addSeparator();
446
447 // Copy to clipboard / save
448 menu.addAction(text: QLatin1String("Save as image..."),
449 object: this, slot: &QPixelTool::saveToFile, shortcut: QKeySequence::SaveAs);
450#if QT_CONFIG(clipboard)
451 menu.addAction(text: QLatin1String("Copy to clipboard"),
452 object: this, slot: &QPixelTool::copyToClipboard, shortcut: QKeySequence::Copy);
453 menu.addAction(text: QLatin1String("Copy color value to clipboard"),
454 object: this, slot: &QPixelTool::copyColorToClipboard, shortcut: Qt::Key_C);
455#endif // QT_CONFIG(clipboard)
456
457 menu.addSeparator();
458 menu.addAction(text: QLatin1String("About Qt"), qApp, slot: &QApplication::aboutQt);
459 menu.addAction(text: QLatin1String("About Qt Pixeltool"), object: this, slot: &QPixelTool::aboutPixelTool);
460
461 menu.exec(pos: mapToGlobal(e->pos()));
462
463 // Read out grid settings
464 if (noGrid->isChecked())
465 m_gridActive = 0;
466 else if (blackGrid->isChecked())
467 m_gridActive = 1;
468 else
469 m_gridActive = 2;
470
471 // Read out lcd settings
472 if (rgbPixels->isChecked())
473 m_lcdMode = 1;
474 else if (bgrPixels->isChecked())
475 m_lcdMode = 2;
476 else if (vrgbPixels->isChecked())
477 m_lcdMode = 3;
478 else if (vbgrPixels->isChecked())
479 m_lcdMode = 4;
480 else
481 m_lcdMode = 0;
482
483 m_autoUpdate = autoUpdate->isChecked();
484 m_freeze = freeze->isChecked();
485
486 // LCD mode looks off unless zoom is dividable by 3
487 if (m_lcdMode && m_zoom % 3)
488 setZoom(qMax(a: 3, b: (m_zoom + 1) / 3));
489}
490
491QSize QPixelTool::sizeHint() const
492{
493 return m_initialSize;
494}
495
496static inline QString pixelToolTitle(QPoint pos, const QColor &currentColor)
497{
498 if (QHighDpiScaling::isActive()) {
499 if (auto screen = QGuiApplication::screenAt(point: pos))
500 pos = QHighDpi::toNativePixels(value: pos, context: screen);
501 }
502 return QCoreApplication::applicationName() + QLatin1String(" [")
503 + QString::number(pos.x())
504 + QLatin1String(", ") + QString::number(pos.y()) + QLatin1String("] ")
505 + currentColor.name();
506}
507
508void QPixelTool::grabScreen()
509{
510 if (m_preview_mode) {
511 int w = qMin(a: width() / m_zoom + 1, b: m_preview_image.width());
512 int h = qMin(a: height() / m_zoom + 1, b: m_preview_image.height());
513 m_buffer = QPixmap::fromImage(image: m_preview_image).copy(ax: 0, ay: 0, awidth: w, aheight: h);
514 update();
515 return;
516 }
517
518 QPoint mousePos = QCursor::pos();
519 if (mousePos == m_lastMousePos && !m_autoUpdate)
520 return;
521
522 if (m_lastMousePos != mousePos)
523 setWindowTitle(pixelToolTitle(pos: mousePos, currentColor: m_currentColor));
524
525 int w = int(width() / float(m_zoom));
526 int h = int(height() / float(m_zoom));
527
528 if (width() % m_zoom > 0)
529 ++w;
530 if (height() % m_zoom > 0)
531 ++h;
532
533 int x = mousePos.x() - w/2;
534 int y = mousePos.y() - h/2;
535
536 const QBrush darkBrush = palette().color(cr: QPalette::Dark);
537 const QDesktopWidget *desktopWidget = QApplication::desktop();
538 if (QScreen *screen = this->screen()) {
539 m_buffer = screen->grabWindow(window: desktopWidget->winId(), x, y, w, h);
540 } else {
541 m_buffer = QPixmap(w, h);
542 m_buffer.fill(fillColor: darkBrush.color());
543 }
544 QRegion geom(x, y, w, h);
545 QRect screenRect;
546 const auto screens = QGuiApplication::screens();
547 for (auto screen : screens)
548 screenRect |= screen->geometry();
549 geom -= screenRect;
550 const auto rectsInRegion = geom.rectCount();
551 if (rectsInRegion > 0) {
552 QPainter p(&m_buffer);
553 p.translate(dx: -x, dy: -y);
554 p.setPen(Qt::NoPen);
555 p.setBrush(darkBrush);
556 p.drawRects(rects: geom.begin(), rectCount: rectsInRegion);
557 }
558
559 update();
560
561 m_currentColor = m_buffer.toImage().pixel(pt: m_buffer.rect().center());
562 m_lastMousePos = mousePos;
563}
564
565void QPixelTool::startZoomVisibleTimer()
566{
567 if (m_displayZoomId > 0) {
568 killTimer(id: m_displayZoomId);
569 }
570 m_displayZoomId = startTimer(interval: 5000);
571 setZoomVisible(true);
572}
573
574void QPixelTool::startGridSizeVisibleTimer()
575{
576 if (m_gridActive) {
577 if (m_displayGridSizeId > 0)
578 killTimer(id: m_displayGridSizeId);
579 m_displayGridSizeId = startTimer(interval: 5000);
580 m_displayGridSize = true;
581 update();
582 }
583}
584
585void QPixelTool::setZoomVisible(bool visible)
586{
587 m_displayZoom = visible;
588 update();
589}
590
591void QPixelTool::toggleFreeze()
592{
593 m_freeze = !m_freeze;
594 if (!m_freeze)
595 m_dragStart = m_dragCurrent = QPoint();
596}
597
598void QPixelTool::increaseZoom()
599{
600 if (!m_lcdMode)
601 setZoom(m_zoom + 1);
602 else
603 setZoom(m_zoom + 3);
604}
605
606void QPixelTool::decreaseZoom()
607{
608 if (!m_lcdMode)
609 setZoom(m_zoom - 1);
610 else
611 setZoom(m_zoom - 3);
612}
613
614void QPixelTool::setZoom(int zoom)
615{
616 if (zoom > 0) {
617 QPoint pos = m_lastMousePos;
618 m_lastMousePos = QPoint();
619 m_zoom = zoom;
620 grabScreen();
621 m_lastMousePos = pos;
622 m_dragStart = m_dragCurrent = QPoint();
623 startZoomVisibleTimer();
624 }
625}
626
627void QPixelTool::toggleGrid()
628{
629 if (++m_gridActive > 2)
630 m_gridActive = 0;
631 update();
632}
633
634void QPixelTool::setGridSize(int gridSize)
635{
636 if (m_gridActive && gridSize > 0) {
637 m_gridSize = gridSize;
638 startGridSizeVisibleTimer();
639 update();
640 }
641}
642
643#if QT_CONFIG(clipboard)
644void QPixelTool::copyToClipboard()
645{
646 QGuiApplication::clipboard()->setPixmap(m_buffer);
647}
648
649void QPixelTool::copyColorToClipboard()
650{
651 QGuiApplication::clipboard()->setText(QColor(m_currentColor).name());
652}
653#endif // QT_CONFIG(clipboard)
654
655void QPixelTool::saveToFile()
656{
657 bool oldFreeze = m_freeze;
658 m_freeze = true;
659
660 QFileDialog fileDialog(this);
661 fileDialog.setWindowTitle(QLatin1String("Save as image"));
662 fileDialog.setAcceptMode(QFileDialog::AcceptSave);
663 fileDialog.setDirectory(QStandardPaths::writableLocation(type: QStandardPaths::PicturesLocation));
664 QStringList mimeTypes;
665 const QByteArrayList supportedMimeTypes = QImageWriter::supportedMimeTypes();
666 for (const QByteArray &mimeTypeB : supportedMimeTypes)
667 mimeTypes.append(t: QString::fromLatin1(str: mimeTypeB));
668 fileDialog.setMimeTypeFilters(mimeTypes);
669 const QString pngType = QLatin1String("image/png");
670 if (mimeTypes.contains(str: pngType)) {
671 fileDialog.selectMimeTypeFilter(filter: pngType);
672 fileDialog.setDefaultSuffix(QLatin1String("png"));
673 }
674
675 while (fileDialog.exec() == QDialog::Accepted
676 && !m_buffer.save(fileName: fileDialog.selectedFiles().constFirst())) {
677 QMessageBox::warning(parent: this, title: QLatin1String("Unable to write image"),
678 text: QLatin1String("Unable to write ")
679 + QDir::toNativeSeparators(pathName: fileDialog.selectedFiles().first()));
680 }
681 m_freeze = oldFreeze;
682}
683
684QTextStream &operator<<(QTextStream &str, const QScreen *screen)
685{
686 const QRect geometry = screen->geometry();
687 str << '"' << screen->name() << "\" " << geometry.width()
688 << 'x' << geometry.height() << Qt::forcesign << geometry.x() << geometry.y()
689 << Qt::noforcesign << ", " << qRound(d: screen->logicalDotsPerInch()) << "DPI"
690 << ", Depth: " << screen->depth() << ", " << screen->refreshRate() << "Hz";
691 const qreal dpr = screen->devicePixelRatio();
692 if (!qFuzzyCompare(p1: dpr, p2: qreal(1)))
693 str << ", DPR: " << dpr;
694 return str;
695}
696
697QString QPixelTool::aboutText() const
698{
699 const QList<QScreen *> screens = QGuiApplication::screens();
700 const QScreen *windowScreen = windowHandle()->screen();
701
702 QString result;
703 QTextStream str(&result);
704 str << "<html></head><body><h2>Qt Pixeltool</h2><p>Qt " << QT_VERSION_STR
705 << "</p><p>Copyright (C) 2017 The Qt Company Ltd.</p><h3>Screens</h3><ul>";
706 for (const QScreen *screen : screens)
707 str << "<li>" << (screen == windowScreen ? "* " : " ") << screen << "</li>";
708 str << "<ul></body></html>";
709 return result;
710}
711
712void QPixelTool::aboutPixelTool()
713{
714 QMessageBox aboutBox(QMessageBox::Information, tr(s: "About Qt Pixeltool"), aboutText(),
715 QMessageBox::Close, this);
716 aboutBox.setWindowFlags(aboutBox.windowFlags() & ~Qt::WindowContextHelpButtonHint);
717 aboutBox.setTextInteractionFlags(Qt::TextBrowserInteraction);
718 aboutBox.exec();
719}
720
721QT_END_NAMESPACE
722

source code of qttools/src/pixeltool/qpixeltool.cpp