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

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